build/patch/kernel/archive/odroidxu4-5.13/0048-ODROID-XU4-drivers-fbtft-Add-fb_hktft35-module-for-H.patch

351 lines
9.5 KiB
Diff

From 4a73513af2b3e43538315fd784bcef2be79f4701 Mon Sep 17 00:00:00 2001
From: Yang Deokgyu <secugyu@gmail.com>
Date: Mon, 18 Nov 2019 12:10:34 +0900
Subject: [PATCH 048/109] ODROID-XU4: drivers/fbtft: Add fb_hktft35 module for
Hardkernel 3.5 inch TFT LCD
No longer use flexfb, fbtft_device that is deprecated since kernel 5.4.
Signed-off-by: Yang Deokgyu <secugyu@gmail.com>
Change-Id: Iae252c64b91b2eabe97eb3aace12d7c4b98801c5
---
drivers/staging/fbtft/Kconfig | 7 +
drivers/staging/fbtft/Makefile | 1 +
drivers/staging/fbtft/fb_hktft35.c | 300 +++++++++++++++++++++++++++++
3 files changed, 308 insertions(+)
create mode 100644 drivers/staging/fbtft/fb_hktft35.c
diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig
index dad1ddcd7b0c..b6cd416ebb26 100644
--- a/drivers/staging/fbtft/Kconfig
+++ b/drivers/staging/fbtft/Kconfig
@@ -206,3 +206,10 @@ config FB_TFT_WATTEROTT
depends on FB_TFT
help
Generic Framebuffer support for WATTEROTT
+
+config FB_TFT_HKTFT35
+ tristate "FB driver for the Hardkernel 3.5 inch TFT LCD"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for the Hardkernel 3.5 inch TFT LCD
+ that uses the ILI9488 LCD Controller
diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile
index e87193f7df14..3d41175663ed 100644
--- a/drivers/staging/fbtft/Makefile
+++ b/drivers/staging/fbtft/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_FB_TFT_UC1611) += fb_uc1611.o
obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o
obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o
obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o
+obj-$(CONFIG_FB_TFT_HKTFT35) += fb_hktft35.o
diff --git a/drivers/staging/fbtft/fb_hktft35.c b/drivers/staging/fbtft/fb_hktft35.c
new file mode 100644
index 000000000000..2389343492d4
--- /dev/null
+++ b/drivers/staging/fbtft/fb_hktft35.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * FB driver for the Hardkernel 3.5 inch TFT LCD
+ * that uses the ILI9488 LCD Controller
+ *
+ * Copyright (C) 2019 Yang Deokgyu
+ *
+ * Based on fb_ili9340.c by Noralf Tronnes
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_hktft35"
+#define WIDTH 320
+#define HEIGHT 480
+
+#define ODROIDXU3_GPX1_REG 0x13400C24
+#define ODROIDXU3_GPX2_REG 0x13400C44
+#define ODROIDXU3_GPA2_REG 0x14010044
+
+#define ODROID_TFT35_MACTL_MV 0x20
+#define ODROID_TFT35_MACTL_MX 0x40
+#define ODROID_TFT35_MACTL_MY 0x80
+
+union reg_bitfield {
+ unsigned int wvalue;
+ struct {
+ unsigned int bit0 : 1;
+ unsigned int bit1 : 1;
+ unsigned int bit2 : 1;
+ unsigned int bit3 : 1;
+ unsigned int bit4 : 1;
+ unsigned int bit5 : 1;
+ unsigned int bit6 : 1;
+ unsigned int bit7 : 1;
+ unsigned int bit8_bit31 : 24;
+ } bits;
+};
+
+volatile void __iomem *reg_gpx1;
+volatile void __iomem *reg_gpx2;
+volatile void __iomem *reg_gpa2;
+
+/* this init sequence matches Hardkernel 3.5 inch TFT LCD */
+static const s16 default_init_sequence[] = {
+ -1, 0xB0,0x00,
+ -1, 0x11,
+ -2, 120,
+ -1, 0x3A,0x55,
+ -1, 0xC2,0x33,
+ -1, 0xC5,0x00,0x1E,0x80,
+ -1, 0x36,0x28,
+ -1, 0xB1,0xB0,
+ -1, 0xE0,0x00,0x04,0x0E,0x08,0x17,0x0A,0x40,0x79,0x4D,0x07,0x0E,0x0A,0x1A,0x1D,0x0F,
+ -1, 0xE1,0x00,0x1B,0x1F,0x02,0x10,0x05,0x32,0x34,0x43,0x02,0x0A,0x09,0x33,0x37,0x0F,
+ -1, 0x11,
+ -1, 0x29,
+ -3
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row adress */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ u8 val;
+
+ switch (par->info->var.rotate) {
+ case 270:
+ val = ODROID_TFT35_MACTL_MV;
+ break;
+ case 180:
+ val = ODROID_TFT35_MACTL_MY;
+ break;
+ case 90:
+ val = ODROID_TFT35_MACTL_MV | ODROID_TFT35_MACTL_MX | ODROID_TFT35_MACTL_MY;
+ break;
+ default:
+ val = ODROID_TFT35_MACTL_MX;
+ break;
+ }
+ /* Memory Access Control */
+ write_reg(par, 0x36, val | (par->bgr << 3));
+ return 0;
+}
+
+static int fbtft_backlight_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int fbtft_backlight_update_status(struct backlight_device *bd)
+{
+ struct fbtft_par *par = bl_get_data(bd);
+ bool polarity = par->polarity;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s: polarity=%d, power=%d, fb_blank=%d\n",
+ __func__, polarity, bd->props.power, bd->props.fb_blank);
+
+ if ((bd->props.power == FB_BLANK_UNBLANK) &&
+ (bd->props.fb_blank == FB_BLANK_UNBLANK))
+ gpiod_set_value(par->gpio.led[0], polarity);
+ else
+ gpiod_set_value(par->gpio.led[0], !polarity);
+
+ return 0;
+}
+
+static const struct backlight_ops fbtft_bl_ops = {
+ .get_brightness = fbtft_backlight_get_brightness,
+ .update_status = fbtft_backlight_update_status,
+};
+
+static void register_backlight(struct fbtft_par *par)
+{
+ struct backlight_device *bd;
+ struct backlight_properties bl_props = { 0, };
+
+ if (!par->gpio.led[0]) {
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s(): led pin not set, exiting.\n", __func__);
+ return;
+ }
+
+ bl_props.type = BACKLIGHT_RAW;
+ /* Assume backlight is off, get polarity from current state of pin */
+ bl_props.power = FB_BLANK_POWERDOWN;
+
+ /* Force polarity to true */
+ par->polarity = true;
+
+ bd = backlight_device_register(dev_driver_string(par->info->device),
+ par->info->device, par,
+ &fbtft_bl_ops, &bl_props);
+ if (IS_ERR(bd)) {
+ dev_err(par->info->device,
+ "cannot register backlight device (%ld)\n",
+ PTR_ERR(bd));
+ return;
+ }
+ par->info->bl_dev = bd;
+
+ if (!par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
+}
+
+static void unregister_backlight(struct fbtft_par *par)
+{
+ if (par->info->bl_dev) {
+ par->info->bl_dev->props.power = FB_BLANK_POWERDOWN;
+ backlight_update_status(par->info->bl_dev);
+ backlight_device_unregister(par->info->bl_dev);
+ par->info->bl_dev = NULL;
+ }
+
+ /* Just to hook the remove routine */
+ if (reg_gpx1) iounmap(reg_gpx1);
+ if (reg_gpx2) iounmap(reg_gpx2);
+ if (reg_gpa2) iounmap(reg_gpa2);
+}
+
+static int verify_gpios(struct fbtft_par *par)
+{
+ struct fbtft_platform_data *pdata = par->pdata;
+ int i;
+
+ fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
+
+ if (pdata->display.buswidth != 9 && par->startbyte == 0 &&
+ !par->gpio.dc) {
+ dev_err(par->info->device,
+ "Missing info about 'dc' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+
+ if (!par->pdev)
+ return 0;
+
+ if (!par->gpio.wr) {
+ dev_err(par->info->device, "Missing 'wr' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < pdata->display.buswidth; i++) {
+ if (!par->gpio.db[i]) {
+ dev_err(par->info->device,
+ "Missing 'db%02d' gpio. Aborting.\n", i);
+ return -EINVAL;
+ }
+ }
+
+ /* Just to hook the probe routine */
+ reg_gpx1 = ioremap(ODROIDXU3_GPX1_REG, 4);
+ reg_gpx2 = ioremap(ODROIDXU3_GPX2_REG, 4);
+ reg_gpa2 = ioremap(ODROIDXU3_GPA2_REG, 4);
+ if ((reg_gpx1 == NULL) || (reg_gpx2 == NULL) || (reg_gpa2 == NULL)) {
+ pr_err("%s : ioremap gpio registers error!\n", __func__);
+ } else {
+ pr_info("%s : ioremap gpio registers success!\n", __func__);
+ }
+
+ return 0;
+}
+
+static void 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, 0);
+ usleep_range(20, 40);
+ gpiod_set_value_cansleep(par->gpio.reset, 1);
+ msleep(120);
+}
+
+static int write(struct fbtft_par *par, void *buf, size_t len)
+{
+ u8 data;
+ union reg_bitfield gpx1, gpx2, gpa2;
+
+ if ((reg_gpx1 == NULL) || (reg_gpx2 == NULL) || (reg_gpa2 == NULL)) {
+ pr_err("%s : ioremap gpio register fail!\n", __func__);
+ return 0;
+ }
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%zu): ", __func__, len);
+
+ gpx1.wvalue = ioread32(reg_gpx1);
+ gpx2.wvalue = ioread32(reg_gpx2);
+ gpa2.wvalue = ioread32(reg_gpa2);
+
+ while (len--) {
+ data = *(u8 *) buf;
+ gpx1.bits.bit7 = (data & 0x01) ? 1 : 0;
+ gpx2.bits.bit0 = (data & 0x02) ? 1 : 0;
+ gpx1.bits.bit3 = (data & 0x04) ? 1 : 0;
+ gpa2.bits.bit4 = (data & 0x08) ? 1 : 0;
+ gpa2.bits.bit6 = (data & 0x10) ? 1 : 0;
+ gpa2.bits.bit7 = (data & 0x20) ? 1 : 0;
+ gpx1.bits.bit6 = (data & 0x40) ? 1 : 0;
+ gpx1.bits.bit5 = (data & 0x80) ? 1 : 0;
+ /* Start writing by pulling down /WR */
+ gpa2.bits.bit5 = 0;
+ iowrite32(gpx1.wvalue, reg_gpx1);
+ iowrite32(gpx2.wvalue, reg_gpx2);
+ iowrite32(gpa2.wvalue, reg_gpa2);
+ gpa2.bits.bit5 = 1;
+ iowrite32(gpa2.wvalue, reg_gpa2);
+
+ buf++;
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .buswidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .init_sequence = default_init_sequence,
+ .fbtftops = {
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .verify_gpios = verify_gpios,
+ .register_backlight = register_backlight,
+ .unregister_backlight = unregister_backlight,
+ .reset = reset,
+ .write = write,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "odroid,hktft35", &display);
+
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("platform:hktft35");
+
+MODULE_DESCRIPTION("FB driver for the Hardkernel 3.5 inch TFT LCD uses the ILI9488 LCD Controller");
+MODULE_AUTHOR("Yang Deokgyu");
+MODULE_LICENSE("GPL");
--
2.25.1