All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/13] Add HD44780 Character LCD support
@ 2017-02-06 14:38 Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 01/13] misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange Geert Uytterhoeven
                   ` (16 more replies)
  0 siblings, 17 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

	Hi all,

Character LCDs are currently typically driven from userspace, either
directly through bit-banging, or via some kind of serial (UART/i2c)
interface.

This patch series adds kernel support for character LCDs using an
Hitachi HD44780 Character LCD Controller where its M6800 bus interface
is connected to GPIOs.  It does so after extracting the character LCD
core support from the existing Parallel port LCD/Keypad Panel driver
into its own subdriver.

  - Patch 1 fixes a bug in the existing panel driver,
  - Patches 2-8 perform clean ups and improvements to the panel driver,
  - Patch 9 extract the character LCD core support from the panel driver
    into an auxdisplay charlcd subdriver,
  - Patches 10-11 add new features to the character LCD core driver
    (4-bit interface support and support for displays with more than 2
    lines),
  - Patch 12 adds DT bindings for describing HD44780 Character LCDs,
  - Patch 13 adds a driver for HD44780 Character LCDs connected to
    GPIOs, using the new character LCD core.

Note that Linux has other support for HD44780 Character LCDs, but none of
it is generic:
  - drivers/misc/arm-charlcd.c involves a custom IP block for interfacing
    to the LCD controller,
  - drivers/parisc/led.c is tightly coupled to LED handling on various HP
    PA-RISC machines.

This has been tested using a 20x4 character LCD with backlight, using both
8-bit and 4-bit wiring to one or two 74HC595 shift registers connected to
an SPI master controller.

Regression testing on original "panel" hardware would be appreciated.

Thanks for your comments!

Geert Uytterhoeven (13):
  misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange
  misc: panel: Remove PANEL_VERSION
  misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID
  misc: panel: Remove reference to misc device support
  misc: panel: Move all suboptions into a big if section
  misc: panel: Remove always-true check from panel_detach()
  misc: panel: Add lcd_home() helper
  misc: panel: Abstract temporary backlight handling
  auxdisplay: charlcd: Extract character LCD core from misc/panel
  auxdisplay: charlcd: Add support for 4-bit interfaces
  auxdisplay: charlcd: Add support for displays with more than two lines
  dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
  auxdisplay: Add HD44780 Character LCD support

 .../devicetree/bindings/auxdisplay/hit,hd44780.txt |  44 ++
 drivers/auxdisplay/Kconfig                         |  14 +
 drivers/auxdisplay/Makefile                        |   2 +
 drivers/auxdisplay/charlcd.c                       | 818 ++++++++++++++++++++
 drivers/auxdisplay/hd44780.c                       | 326 ++++++++
 drivers/misc/Kconfig                               |  45 +-
 drivers/misc/panel.c                               | 852 +++------------------
 include/misc/charlcd.h                             |  42 +
 8 files changed, 1374 insertions(+), 769 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt
 create mode 100644 drivers/auxdisplay/charlcd.c
 create mode 100644 drivers/auxdisplay/hd44780.c
 create mode 100644 include/misc/charlcd.h

-- 
1.9.1

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* [PATCH 01/13] misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 02/13] misc: panel: Remove PANEL_VERSION Geert Uytterhoeven
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

LCD_FLAG_F is the font flag, LCD_FLAG_N is the two-lines flag.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index 6030ac5b8c639190..cac7ac62ce5b71d7 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -1278,9 +1278,10 @@ static inline int handle_lcd_special_code(void)
 			lcd_write_cmd(LCD_CMD_FUNCTION_SET
 				      | LCD_CMD_DATA_LEN_8BITS
 				      | ((lcd.flags & LCD_FLAG_F)
-						      ? LCD_CMD_TWO_LINES : 0)
-				      | ((lcd.flags & LCD_FLAG_N)
 						      ? LCD_CMD_FONT_5X10_DOTS
+								      : 0)
+				      | ((lcd.flags & LCD_FLAG_N)
+						      ? LCD_CMD_TWO_LINES
 								      : 0));
 		/* check whether L flag was changed */
 		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 02/13] misc: panel: Remove PANEL_VERSION
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 01/13] misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 03/13] misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID Geert Uytterhoeven
                   ` (14 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

Hardcoded driver versions are so pre-git.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index cac7ac62ce5b71d7..fa4c12768a161eaa 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -64,8 +64,6 @@
 #define LCD_MINOR		156
 #define KEYPAD_MINOR		185
 
-#define PANEL_VERSION		"0.9.5"
-
 #define LCD_MAXBYTES		256	/* max burst write */
 
 #define KEYPAD_BUFFER		64
@@ -1656,8 +1654,7 @@ static void lcd_init(void)
 	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
 #endif
 #else
-	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\nPanel-"
-			PANEL_VERSION);
+	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
 #endif
 	lcd.addr.x = 0;
 	lcd.addr.y = 0;
@@ -2278,8 +2275,7 @@ static void panel_detach(struct parport *port)
 		}
 
 		if (lcd.enabled) {
-			panel_lcd_print("\x0cLCD driver " PANEL_VERSION
-					"\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+			panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
 			misc_deregister(&lcd_dev);
 			lcd.initialized = false;
 		}
@@ -2401,7 +2397,7 @@ static int __init panel_init_module(void)
 
 	if (!lcd.enabled && !keypad.enabled) {
 		/* no device enabled, let's exit */
-		pr_err("driver version " PANEL_VERSION " disabled.\n");
+		pr_err("panel driver disabled.\n");
 		return -ENODEV;
 	}
 
@@ -2412,12 +2408,10 @@ static int __init panel_init_module(void)
 	}
 
 	if (pprt)
-		pr_info("driver version " PANEL_VERSION
-			" registered on parport%d (io=0x%lx).\n", parport,
-			pprt->port->base);
+		pr_info("panel driver registered on parport%d (io=0x%lx).\n",
+			parport, pprt->port->base);
 	else
-		pr_info("driver version " PANEL_VERSION
-			" not yet registered\n");
+		pr_info("panel driver not yet registered\n");
 	return 0;
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 03/13] misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 01/13] misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 02/13] misc: panel: Remove PANEL_VERSION Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 04/13] misc: panel: Remove reference to misc device support Geert Uytterhoeven
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

These definitions were never used in any publicly available version
since (at least) 2004.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index fa4c12768a161eaa..8af500ecaaaf6634 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -119,8 +119,6 @@
 #define PIN_SELECP		17
 #define PIN_NOT_SET		127
 
-#define LCD_FLAG_S		0x0001
-#define LCD_FLAG_ID		0x0002
 #define LCD_FLAG_B		0x0004	/* blink on */
 #define LCD_FLAG_C		0x0008	/* cursor on */
 #define LCD_FLAG_D		0x0010	/* display on */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 04/13] misc: panel: Remove reference to misc device support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (2 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 03/13] misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 05/13] misc: panel: Move all suboptions into a big if section Geert Uytterhoeven
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

As of commit 7c5763b8453a9487 ("drivers: misc: Remove MISC_DEVICES
config option"), misc device support no longer needs to be enabled
manually.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/Kconfig | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b4d6aac838244d85..2687ecf3e9e673fa 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -499,10 +499,9 @@ config PANEL
 	  Say Y here if you have an HD44780 or KS-0074 LCD connected to your
 	  parallel port. This driver also features 4 and 6-key keypads. The LCD
 	  is accessible through the /dev/lcd char device (10, 156), and the
-	  keypad through /dev/keypad (10, 185). Both require misc device to be
-	  enabled. This code can either be compiled as a module, or linked into
-	  the kernel and started at boot. If you don't understand what all this
-	  is about, say N.
+	  keypad through /dev/keypad (10, 185). This code can either be
+	  compiled as a module, or linked into the kernel and started at boot.
+	  If you don't understand what all this is about, say N.
 
 config PANEL_PARPORT
 	int "Default parallel port number (0=LPT1)"
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 05/13] misc: panel: Move all suboptions into a big if section
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (3 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 04/13] misc: panel: Remove reference to misc device support Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 06/13] misc: panel: Remove always-true check from panel_detach() Geert Uytterhoeven
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

All 18 suboptions related to the panel driver have individual
dependencies on PANEL.

Replace them by a single "if PANEL / endif # PANEL" section for easier
dependency management.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/Kconfig | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2687ecf3e9e673fa..c290990d73edf87e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -503,9 +503,10 @@ config PANEL
 	  compiled as a module, or linked into the kernel and started at boot.
 	  If you don't understand what all this is about, say N.
 
+if PANEL
+
 config PANEL_PARPORT
 	int "Default parallel port number (0=LPT1)"
-	depends on PANEL
 	range 0 255
 	default "0"
 	---help---
@@ -517,7 +518,6 @@ config PANEL_PARPORT
 
 config PANEL_PROFILE
 	int "Default panel profile (0-5, 0=custom)"
-	depends on PANEL
 	range 0 5
 	default "5"
 	---help---
@@ -538,7 +538,7 @@ config PANEL_PROFILE
 	  for experts.
 
 config PANEL_KEYPAD
-	depends on PANEL && PANEL_PROFILE="0"
+	depends on PANEL_PROFILE="0"
 	int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)"
 	range 0 3
 	default 0
@@ -555,7 +555,7 @@ config PANEL_KEYPAD
 	  supports simultaneous keys pressed when the keypad supports them.
 
 config PANEL_LCD
-	depends on PANEL && PANEL_PROFILE="0"
+	depends on PANEL_PROFILE="0"
 	int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)"
 	range 0 5
 	default 0
@@ -578,7 +578,7 @@ config PANEL_LCD
 	   that those values changed from the 2.4 driver for better consistency.
 
 config PANEL_LCD_HEIGHT
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "Number of lines on the LCD (1-2)"
 	range 1 2
 	default 2
@@ -587,7 +587,7 @@ config PANEL_LCD_HEIGHT
 	  It can either be 1 or 2.
 
 config PANEL_LCD_WIDTH
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "Number of characters per line on the LCD (1-40)"
 	range 1 40
 	default 40
@@ -596,7 +596,7 @@ config PANEL_LCD_WIDTH
 	  Common values are 16,20,24,40.
 
 config PANEL_LCD_BWIDTH
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "Internal LCD line width (1-40, 40 by default)"
 	range 1 40
 	default 40
@@ -612,7 +612,7 @@ config PANEL_LCD_BWIDTH
 	  If you don't know, put '40' here.
 
 config PANEL_LCD_HWIDTH
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "Hardware LCD line width (1-64, 64 by default)"
 	range 1 64
 	default 64
@@ -626,7 +626,7 @@ config PANEL_LCD_HWIDTH
 	  64 here for a 2x40.
 
 config PANEL_LCD_CHARSET
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "LCD character set (0=normal, 1=KS0074)"
 	range 0 1
 	default 0
@@ -642,7 +642,7 @@ config PANEL_LCD_CHARSET
 	  If you don't know, use the normal one (0).
 
 config PANEL_LCD_PROTO
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
 	int "LCD communication mode (0=parallel 8 bits, 1=serial)"
 	range 0 1
 	default 0
@@ -655,7 +655,7 @@ config PANEL_LCD_PROTO
 	  parallel LCD, and 1 for a serial LCD.
 
 config PANEL_LCD_PIN_E
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
         int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) "
 	range -17 17
 	default 14
@@ -670,7 +670,7 @@ config PANEL_LCD_PIN_E
 	  Default for the 'E' pin in custom profile is '14' (AUTOFEED).
 
 config PANEL_LCD_PIN_RS
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
         int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) "
 	range -17 17
 	default 17
@@ -685,7 +685,7 @@ config PANEL_LCD_PIN_RS
 	  Default for the 'RS' pin in custom profile is '17' (SELECT IN).
 
 config PANEL_LCD_PIN_RW
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
         int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) "
 	range -17 17
 	default 16
@@ -700,7 +700,7 @@ config PANEL_LCD_PIN_RW
 	  Default for the 'RW' pin in custom profile is '16' (INIT).
 
 config PANEL_LCD_PIN_SCL
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
         int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) "
 	range -17 17
 	default 1
@@ -715,7 +715,7 @@ config PANEL_LCD_PIN_SCL
 	  Default for the 'SCL' pin in custom profile is '1' (STROBE).
 
 config PANEL_LCD_PIN_SDA
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
         int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) "
 	range -17 17
 	default 2
@@ -730,7 +730,7 @@ config PANEL_LCD_PIN_SDA
 	  Default for the 'SDA' pin in custom profile is '2' (D0).
 
 config PANEL_LCD_PIN_BL
-	depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+	depends on PANEL_PROFILE="0" && PANEL_LCD="1"
         int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) "
 	range -17 17
 	default 0
@@ -745,7 +745,6 @@ config PANEL_LCD_PIN_BL
 	  Default for the 'BL' pin in custom profile is '0' (uncontrolled).
 
 config PANEL_CHANGE_MESSAGE
-	depends on PANEL
 	bool "Change LCD initialization message ?"
 	default "n"
 	---help---
@@ -758,7 +757,7 @@ config PANEL_CHANGE_MESSAGE
 	  say 'N' and keep the default message with the version.
 
 config PANEL_BOOT_MESSAGE
-	depends on PANEL && PANEL_CHANGE_MESSAGE="y"
+	depends on PANEL_CHANGE_MESSAGE="y"
 	string "New initialization message"
 	default ""
 	---help---
@@ -770,6 +769,8 @@ config PANEL_BOOT_MESSAGE
 	  An empty message will only clear the display at driver init time. Any other
 	  printf()-formatted message is valid with newline and escape codes.
 
+endif # PANEL
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 06/13] misc: panel: Remove always-true check from panel_detach()
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (4 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 05/13] misc: panel: Move all suboptions into a big if section Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 07/13] misc: panel: Add lcd_home() helper Geert Uytterhoeven
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

panel_detach() already verified that pptr is a valid pointer.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index 8af500ecaaaf6634..ed19b3e399b08d1f 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -2266,24 +2266,22 @@ static void panel_detach(struct parport *port)
 	if (scan_timer.function)
 		del_timer_sync(&scan_timer);
 
-	if (pprt) {
-		if (keypad.enabled) {
-			misc_deregister(&keypad_dev);
-			keypad_initialized = 0;
-		}
-
-		if (lcd.enabled) {
-			panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
-			misc_deregister(&lcd_dev);
-			lcd.initialized = false;
-		}
+	if (keypad.enabled) {
+		misc_deregister(&keypad_dev);
+		keypad_initialized = 0;
+	}
 
-		/* TODO: free all input signals */
-		parport_release(pprt);
-		parport_unregister_device(pprt);
-		pprt = NULL;
-		unregister_reboot_notifier(&panel_notifier);
+	if (lcd.enabled) {
+		panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+		misc_deregister(&lcd_dev);
+		lcd.initialized = false;
 	}
+
+	/* TODO: free all input signals */
+	parport_release(pprt);
+	parport_unregister_device(pprt);
+	pprt = NULL;
+	unregister_reboot_notifier(&panel_notifier);
 }
 
 static struct parport_driver panel_driver = {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 07/13] misc: panel: Add lcd_home() helper
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (5 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 06/13] misc: panel: Remove always-true check from panel_detach() Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 08/13] misc: panel: Abstract temporary backlight handling Geert Uytterhoeven
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

Add a helper function to move the cursor to the home position, so
callers no longer need access to internal state.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 39 +++++++++++++++------------------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index ed19b3e399b08d1f..9c7b00951c22b1c2 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -903,6 +903,13 @@ static void lcd_gotoxy(void)
 			 (lcd.hwidth - 1) : lcd.bwidth - 1));
 }
 
+static void lcd_home(void)
+{
+	lcd.addr.x = 0;
+	lcd.addr.y = 0;
+	lcd_gotoxy();
+}
+
 static void lcd_print(char c)
 {
 	if (lcd.addr.x < lcd.bwidth) {
@@ -921,9 +928,7 @@ static void lcd_clear_fast_s(void)
 {
 	int pos;
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 
 	spin_lock_irq(&pprt_lock);
 	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@@ -935,9 +940,7 @@ static void lcd_clear_fast_s(void)
 	}
 	spin_unlock_irq(&pprt_lock);
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 }
 
 /* fills the display with spaces and resets X/Y */
@@ -945,9 +948,7 @@ static void lcd_clear_fast_p8(void)
 {
 	int pos;
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 
 	spin_lock_irq(&pprt_lock);
 	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@@ -973,9 +974,7 @@ static void lcd_clear_fast_p8(void)
 	}
 	spin_unlock_irq(&pprt_lock);
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 }
 
 /* fills the display with spaces and resets X/Y */
@@ -983,9 +982,7 @@ static void lcd_clear_fast_tilcd(void)
 {
 	int pos;
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 
 	spin_lock_irq(&pprt_lock);
 	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@@ -996,9 +993,7 @@ static void lcd_clear_fast_tilcd(void)
 
 	spin_unlock_irq(&pprt_lock);
 
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
+	lcd_home();
 }
 
 /* clears the display and resets X/Y */
@@ -1373,9 +1368,7 @@ static void lcd_write_char(char c)
 			processed = 1;
 		} else if (!strcmp(lcd.esc_seq.buf, "[H")) {
 			/* cursor to home */
-			lcd.addr.x = 0;
-			lcd.addr.y = 0;
-			lcd_gotoxy();
+			lcd_home();
 			processed = 1;
 		}
 		/* codes starting with ^[[L */
@@ -1654,11 +1647,9 @@ static void lcd_init(void)
 #else
 	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
 #endif
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
 	/* clear the display on the next device opening */
 	lcd.must_clear = true;
-	lcd_gotoxy();
+	lcd_home();
 }
 
 /*
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 08/13] misc: panel: Abstract temporary backlight handling
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (6 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 07/13] misc: panel: Add lcd_home() helper Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel Geert Uytterhoeven
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

Currently the periodic scan timer is used for three purposes,
entangling keypad and display handling, which are both optional:
  1. Scanning the keypad,
  2. Flashing the backlight when a key is pressed,
  3. Disabling temporary backlighting after a fixed period of time.

Abstract the second purpose using a new lcd_poke() function.
Make the non-periodic temporary backlight handling independent from
keypad handling by converting it to a delayed workqueue.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/misc/panel.c | 101 ++++++++++++++++++++++++++++++---------------------
 1 file changed, 60 insertions(+), 41 deletions(-)

diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index 9c7b00951c22b1c2..ef2ece0f26afc6b5 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -56,6 +56,7 @@
 #include <linux/list.h>
 #include <linux/notifier.h>
 #include <linux/reboot.h>
+#include <linux/workqueue.h>
 #include <generated/utsrelease.h>
 
 #include <linux/io.h>
@@ -75,8 +76,8 @@
 /* a key repeats this times INPUT_POLL_TIME */
 #define KEYPAD_REP_DELAY	(2)
 
-/* keep the light on this times INPUT_POLL_TIME for each flash */
-#define FLASH_LIGHT_TEMPO	(200)
+/* keep the light on this many seconds for each flash */
+#define FLASH_LIGHT_TEMPO	(4)
 
 /* converts an r_str() input to an active high, bits string : 000BAOSE */
 #define PNL_PINPUT(a)		((((unsigned char)(a)) ^ 0x7F) >> 3)
@@ -252,7 +253,10 @@ struct logical_input {
 	int hwidth;
 	int charset;
 	int proto;
-	int light_tempo;
+
+	struct delayed_work bl_work;
+	struct mutex bl_tempo_lock;	/* Protects access to bl_tempo */
+	bool bl_tempo;
 
 	/* TODO: use union here? */
 	struct {
@@ -657,8 +661,6 @@ static void lcd_get_bits(unsigned int port, int *val)
 	}
 }
 
-static void init_scan_timer(void);
-
 /* sets data port bits according to current signals values */
 static int set_data_bits(void)
 {
@@ -790,11 +792,8 @@ static void lcd_send_serial(int byte)
 }
 
 /* turn the backlight on or off */
-static void lcd_backlight(int on)
+static void __lcd_backlight(int on)
 {
-	if (lcd.pins.bl == PIN_NONE)
-		return;
-
 	/* The backlight is activated by setting the AUTOFEED line to +5V  */
 	spin_lock_irq(&pprt_lock);
 	if (on)
@@ -805,6 +804,44 @@ static void lcd_backlight(int on)
 	spin_unlock_irq(&pprt_lock);
 }
 
+static void lcd_backlight(int on)
+{
+	if (lcd.pins.bl == PIN_NONE)
+		return;
+
+	mutex_lock(&lcd.bl_tempo_lock);
+	if (!lcd.bl_tempo)
+		__lcd_backlight(on);
+	mutex_unlock(&lcd.bl_tempo_lock);
+}
+
+static void lcd_bl_off(struct work_struct *work)
+{
+	mutex_lock(&lcd.bl_tempo_lock);
+	if (lcd.bl_tempo) {
+		lcd.bl_tempo = false;
+		if (!(lcd.flags & LCD_FLAG_L))
+			__lcd_backlight(0);
+	}
+	mutex_unlock(&lcd.bl_tempo_lock);
+}
+
+/* turn the backlight on for a little while */
+static void lcd_poke(void)
+{
+	if (lcd.pins.bl == PIN_NONE)
+		return;
+
+	cancel_delayed_work_sync(&lcd.bl_work);
+
+	mutex_lock(&lcd.bl_tempo_lock);
+	if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L))
+		__lcd_backlight(1);
+	lcd.bl_tempo = true;
+	schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ);
+	mutex_unlock(&lcd.bl_tempo_lock);
+}
+
 /* send a command to the LCD panel in serial mode */
 static void lcd_write_cmd_s(int cmd)
 {
@@ -1099,13 +1136,8 @@ static inline int handle_lcd_special_code(void)
 		processed = 1;
 		break;
 	case '*':
-		/* flash back light using the keypad timer */
-		if (scan_timer.function) {
-			if (lcd.light_tempo == 0 &&
-			    ((lcd.flags & LCD_FLAG_L) == 0))
-				lcd_backlight(1);
-			lcd.light_tempo = FLASH_LIGHT_TEMPO;
-		}
+		/* flash back light */
+		lcd_poke();
 		processed = 1;
 		break;
 	case 'f':	/* Small Font */
@@ -1275,16 +1307,8 @@ static inline int handle_lcd_special_code(void)
 						      ? LCD_CMD_TWO_LINES
 								      : 0));
 		/* check whether L flag was changed */
-		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) {
-			if (lcd.flags & (LCD_FLAG_L))
-				lcd_backlight(1);
-			else if (lcd.light_tempo == 0)
-				/*
-				 * switch off the light only when the tempo
-				 * lighting is gone
-				 */
-				lcd_backlight(0);
-		}
+		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L))
+			lcd_backlight(!!(lcd.flags & LCD_FLAG_L));
 	}
 
 	return processed;
@@ -1615,8 +1639,10 @@ static void lcd_init(void)
 	else
 		lcd_char_conv = NULL;
 
-	if (lcd.pins.bl != PIN_NONE)
-		init_scan_timer();
+	if (lcd.pins.bl != PIN_NONE) {
+		mutex_init(&lcd.bl_tempo_lock);
+		INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off);
+	}
 
 	pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
 		    lcd_bits[LCD_PORT_C][LCD_BIT_E]);
@@ -1984,19 +2010,8 @@ static void panel_scan_timer(void)
 			panel_process_inputs();
 	}
 
-	if (lcd.enabled && lcd.initialized) {
-		if (keypressed) {
-			if (lcd.light_tempo == 0 &&
-			    ((lcd.flags & LCD_FLAG_L) == 0))
-				lcd_backlight(1);
-			lcd.light_tempo = FLASH_LIGHT_TEMPO;
-		} else if (lcd.light_tempo > 0) {
-			lcd.light_tempo--;
-			if (lcd.light_tempo == 0 &&
-			    ((lcd.flags & LCD_FLAG_L) == 0))
-				lcd_backlight(0);
-		}
-	}
+	if (keypressed && lcd.enabled && lcd.initialized)
+		lcd_poke();
 
 	mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
 }
@@ -2265,6 +2280,10 @@ static void panel_detach(struct parport *port)
 	if (lcd.enabled) {
 		panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
 		misc_deregister(&lcd_dev);
+		if (lcd.pins.bl != PIN_NONE) {
+			cancel_delayed_work_sync(&lcd.bl_work);
+			__lcd_backlight(0);
+		}
 		lcd.initialized = false;
 	}
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (7 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 08/13] misc: panel: Abstract temporary backlight handling Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 15:09   ` Arnd Bergmann
  2017-02-08 12:22   ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 10/13] auxdisplay: charlcd: Add support for 4-bit interfaces Geert Uytterhoeven
                   ` (7 subsequent siblings)
  16 siblings, 2 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

Extract the character LCD core from the Parallel port LCD/Keypad Panel
driver in the misc subsystem, and convert it into a subdriver in the
auxdisplay subsystem.  This allows the character LCD core to be used by
other drivers later.

Compilation is controlled by its own Kconfig symbol CHARLCD, which is to
be selected by its users, but can be enabled manually for
compile-testing.

All functions changed their prefix from "lcd_" to "charlcd_", and gained
a "struct charlcd *" parameter to operate on a specific instance.
While the driver API thus is ready to support multiple instances, the
current limitation of a single display (/dev/lcd has a single misc minor
assigned) is retained.

No functional changes intended.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/auxdisplay/Kconfig   |   3 +
 drivers/auxdisplay/Makefile  |   1 +
 drivers/auxdisplay/charlcd.c | 790 +++++++++++++++++++++++++++++++++++++++++
 drivers/misc/Kconfig         |   1 +
 drivers/misc/panel.c         | 827 +++++--------------------------------------
 include/misc/charlcd.h       |  40 +++
 6 files changed, 927 insertions(+), 735 deletions(-)
 create mode 100644 drivers/auxdisplay/charlcd.c
 create mode 100644 include/misc/charlcd.h

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 8a8e403644d6e0d6..b4686380d4e3cca9 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -13,6 +13,9 @@ menuconfig AUXDISPLAY
 
 	  If you say N, all options in this submenu will be skipped and disabled.
 
+config CHARLCD
+	tristate "Character LCD core support" if COMPILE_TEST
+
 if AUXDISPLAY
 
 config KS0108
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index cb3dd847713bb6d1..f56f45dcc78e332e 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -2,6 +2,7 @@
 # Makefile for the kernel auxiliary displays device drivers.
 #
 
+obj-$(CONFIG_CHARLCD)		+= charlcd.o
 obj-$(CONFIG_KS0108)		+= ks0108.o
 obj-$(CONFIG_CFAG12864B)	+= cfag12864b.o cfag12864bfb.o
 obj-$(CONFIG_IMG_ASCII_LCD)	+= img-ascii-lcd.o
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
new file mode 100644
index 0000000000000000..8d45410056357880
--- /dev/null
+++ b/drivers/auxdisplay/charlcd.c
@@ -0,0 +1,790 @@
+/*
+ * Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+
+#include <generated/utsrelease.h>
+
+#include <misc/charlcd.h>
+
+#define LCD_MINOR		156
+
+#define DEFAULT_LCD_BWIDTH      40
+#define DEFAULT_LCD_HWIDTH      64
+
+/* Keep the backlight on this many seconds for each flash */
+#define LCD_BL_TEMPO_PERIOD	4
+
+#define LCD_FLAG_B		0x0004	/* Blink on */
+#define LCD_FLAG_C		0x0008	/* Cursor on */
+#define LCD_FLAG_D		0x0010	/* Display on */
+#define LCD_FLAG_F		0x0020	/* Large font mode */
+#define LCD_FLAG_N		0x0040	/* 2-rows mode */
+#define LCD_FLAG_L		0x0080	/* Backlight enabled */
+
+/* LCD commands */
+#define LCD_CMD_DISPLAY_CLEAR	0x01	/* Clear entire display */
+
+#define LCD_CMD_ENTRY_MODE	0x04	/* Set entry mode */
+#define LCD_CMD_CURSOR_INC	0x02	/* Increment cursor */
+
+#define LCD_CMD_DISPLAY_CTRL	0x08	/* Display control */
+#define LCD_CMD_DISPLAY_ON	0x04	/* Set display on */
+#define LCD_CMD_CURSOR_ON	0x02	/* Set cursor on */
+#define LCD_CMD_BLINK_ON	0x01	/* Set blink on */
+
+#define LCD_CMD_SHIFT		0x10	/* Shift cursor/display */
+#define LCD_CMD_DISPLAY_SHIFT	0x08	/* Shift display instead of cursor */
+#define LCD_CMD_SHIFT_RIGHT	0x04	/* Shift display/cursor to the right */
+
+#define LCD_CMD_FUNCTION_SET	0x20	/* Set function */
+#define LCD_CMD_DATA_LEN_8BITS	0x10	/* Set data length to 8 bits */
+#define LCD_CMD_TWO_LINES	0x08	/* Set to two display lines */
+#define LCD_CMD_FONT_5X10_DOTS	0x04	/* Set char font to 5x10 dots */
+
+#define LCD_CMD_SET_CGRAM_ADDR	0x40	/* Set char generator RAM address */
+
+#define LCD_CMD_SET_DDRAM_ADDR	0x80	/* Set display data RAM address */
+
+#define LCD_ESCAPE_LEN		24	/* Max chars for LCD escape command */
+#define LCD_ESCAPE_CHAR		27	/* Use char 27 for escape command */
+
+struct charlcd_priv {
+	struct charlcd lcd;
+
+	struct delayed_work bl_work;
+	struct mutex bl_tempo_lock;	/* Protects access to bl_tempo */
+	bool bl_tempo;
+
+	bool must_clear;
+
+	/* contains the LCD config state */
+	unsigned long int flags;
+
+	/* Contains the LCD X and Y offset */
+	struct {
+		unsigned long int x;
+		unsigned long int y;
+	} addr;
+
+	/* Current escape sequence and it's length or -1 if outside */
+	struct {
+		char buf[LCD_ESCAPE_LEN + 1];
+		int len;
+	} esc_seq;
+
+	unsigned long long drvdata[0];
+};
+
+#define to_priv(p)	container_of(p, struct charlcd_priv, lcd)
+
+/* Device single-open policy control */
+static atomic_t charlcd_available = ATOMIC_INIT(1);
+
+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+	if (in_interrupt())
+		mdelay(ms);
+	else
+		schedule_timeout_interruptible(msecs_to_jiffies(ms));
+}
+
+/* turn the backlight on or off */
+static void charlcd_backlight(struct charlcd *lcd, int on)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (!lcd->ops->backlight)
+		return;
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (!priv->bl_tempo)
+		lcd->ops->backlight(lcd, on);
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+
+static void charlcd_bl_off(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct charlcd_priv *priv =
+		container_of(dwork, struct charlcd_priv, bl_work);
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (priv->bl_tempo) {
+		priv->bl_tempo = false;
+		if (!(priv->flags & LCD_FLAG_L))
+			priv->lcd.ops->backlight(&priv->lcd, 0);
+	}
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+
+/* turn the backlight on for a little while */
+void charlcd_poke(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (!lcd->ops->backlight)
+		return;
+
+	cancel_delayed_work_sync(&priv->bl_work);
+
+	mutex_lock(&priv->bl_tempo_lock);
+	if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
+		lcd->ops->backlight(lcd, 1);
+	priv->bl_tempo = true;
+	schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
+	mutex_unlock(&priv->bl_tempo_lock);
+}
+EXPORT_SYMBOL_GPL(charlcd_poke);
+
+static void charlcd_gotoxy(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	lcd->ops->write_cmd(lcd,
+		LCD_CMD_SET_DDRAM_ADDR | (priv->addr.y ? lcd->hwidth : 0) |
+		/*
+		 * we force the cursor to stay at the end of the
+		 * line if it wants to go farther
+		 */
+		((priv->addr.x < lcd->bwidth) ? priv->addr.x & (lcd->hwidth - 1)
+					      : lcd->bwidth - 1));
+}
+
+static void charlcd_home(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	priv->addr.x = 0;
+	priv->addr.y = 0;
+	charlcd_gotoxy(lcd);
+}
+
+static void charlcd_print(struct charlcd *lcd, char c)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	if (priv->addr.x < lcd->bwidth) {
+		if (lcd->char_conv)
+			c = lcd->char_conv[(unsigned char)c];
+		lcd->ops->write_data(lcd, c);
+		priv->addr.x++;
+	}
+	/* prevents the cursor from wrapping onto the next line */
+	if (priv->addr.x == lcd->bwidth)
+		charlcd_gotoxy(lcd);
+}
+
+static void charlcd_clear_fast(struct charlcd *lcd)
+{
+	int pos;
+
+	charlcd_home(lcd);
+
+	if (lcd->ops->clear_fast)
+		lcd->ops->clear_fast(lcd);
+	else
+		for (pos = 0; pos < lcd->height * lcd->hwidth; pos++)
+			lcd->ops->write_data(lcd, ' ');
+
+	charlcd_home(lcd);
+}
+
+/* clears the display and resets X/Y */
+static void charlcd_clear_display(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
+	priv->addr.x = 0;
+	priv->addr.y = 0;
+	/* we must wait a few milliseconds (15) */
+	long_sleep(15);
+}
+
+static int charlcd_init_display(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+		      LCD_FLAG_C | LCD_FLAG_B;
+
+	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
+
+	/* 8bits, 1 line, small fonts; let's do it 3 times */
+	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	long_sleep(10);
+	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	long_sleep(10);
+	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	long_sleep(10);
+
+	/* set font height and lines number */
+	lcd->ops->write_cmd(lcd,
+		LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS |
+		((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+		((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+	long_sleep(10);
+
+	/* display off, cursor off, blink off */
+	lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
+	long_sleep(10);
+
+	lcd->ops->write_cmd(lcd,
+		LCD_CMD_DISPLAY_CTRL |	/* set display mode */
+		((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+		((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+		((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+
+	charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
+
+	long_sleep(10);
+
+	/* entry mode set : increment, cursor shifting */
+	lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+	charlcd_clear_display(lcd);
+	return 0;
+}
+
+/*
+ * These are the file operation function for user access to /dev/lcd
+ * This function can also be called from inside the kernel, by
+ * setting file and ppos to NULL.
+ *
+ */
+
+static inline int handle_lcd_special_code(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	/* LCD special codes */
+
+	int processed = 0;
+
+	char *esc = priv->esc_seq.buf + 2;
+	int oldflags = priv->flags;
+
+	/* check for display mode flags */
+	switch (*esc) {
+	case 'D':	/* Display ON */
+		priv->flags |= LCD_FLAG_D;
+		processed = 1;
+		break;
+	case 'd':	/* Display OFF */
+		priv->flags &= ~LCD_FLAG_D;
+		processed = 1;
+		break;
+	case 'C':	/* Cursor ON */
+		priv->flags |= LCD_FLAG_C;
+		processed = 1;
+		break;
+	case 'c':	/* Cursor OFF */
+		priv->flags &= ~LCD_FLAG_C;
+		processed = 1;
+		break;
+	case 'B':	/* Blink ON */
+		priv->flags |= LCD_FLAG_B;
+		processed = 1;
+		break;
+	case 'b':	/* Blink OFF */
+		priv->flags &= ~LCD_FLAG_B;
+		processed = 1;
+		break;
+	case '+':	/* Back light ON */
+		priv->flags |= LCD_FLAG_L;
+		processed = 1;
+		break;
+	case '-':	/* Back light OFF */
+		priv->flags &= ~LCD_FLAG_L;
+		processed = 1;
+		break;
+	case '*':	/* Flash back light */
+		charlcd_poke(lcd);
+		processed = 1;
+		break;
+	case 'f':	/* Small Font */
+		priv->flags &= ~LCD_FLAG_F;
+		processed = 1;
+		break;
+	case 'F':	/* Large Font */
+		priv->flags |= LCD_FLAG_F;
+		processed = 1;
+		break;
+	case 'n':	/* One Line */
+		priv->flags &= ~LCD_FLAG_N;
+		processed = 1;
+		break;
+	case 'N':	/* Two Lines */
+		priv->flags |= LCD_FLAG_N;
+		break;
+	case 'l':	/* Shift Cursor Left */
+		if (priv->addr.x > 0) {
+			/* back one char if not at end of line */
+			if (priv->addr.x < lcd->bwidth)
+				lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+			priv->addr.x--;
+		}
+		processed = 1;
+		break;
+	case 'r':	/* shift cursor right */
+		if (priv->addr.x < lcd->width) {
+			/* allow the cursor to pass the end of the line */
+			if (priv->addr.x < (lcd->bwidth - 1))
+				lcd->ops->write_cmd(lcd,
+					LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
+			priv->addr.x++;
+		}
+		processed = 1;
+		break;
+	case 'L':	/* shift display left */
+		lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+		processed = 1;
+		break;
+	case 'R':	/* shift display right */
+		lcd->ops->write_cmd(lcd,
+				    LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+				    LCD_CMD_SHIFT_RIGHT);
+		processed = 1;
+		break;
+	case 'k': {	/* kill end of line */
+		int x;
+
+		for (x = priv->addr.x; x < lcd->bwidth; x++)
+			lcd->ops->write_data(lcd, ' ');
+
+		/* restore cursor position */
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+	case 'I':	/* reinitialize display */
+		charlcd_init_display(lcd);
+		processed = 1;
+		break;
+	case 'G': {
+		/* Generator : LGcxxxxx...xx; must have <c> between '0'
+		 * and '7', representing the numerical ASCII code of the
+		 * redefined character, and <xx...xx> a sequence of 16
+		 * hex digits representing 8 bytes for each character.
+		 * Most LCDs will only use 5 lower bits of the 7 first
+		 * bytes.
+		 */
+
+		unsigned char cgbytes[8];
+		unsigned char cgaddr;
+		int cgoffset;
+		int shift;
+		char value;
+		int addr;
+
+		if (!strchr(esc, ';'))
+			break;
+
+		esc++;
+
+		cgaddr = *(esc++) - '0';
+		if (cgaddr > 7) {
+			processed = 1;
+			break;
+		}
+
+		cgoffset = 0;
+		shift = 0;
+		value = 0;
+		while (*esc && cgoffset < 8) {
+			shift ^= 4;
+			if (*esc >= '0' && *esc <= '9') {
+				value |= (*esc - '0') << shift;
+			} else if (*esc >= 'A' && *esc <= 'Z') {
+				value |= (*esc - 'A' + 10) << shift;
+			} else if (*esc >= 'a' && *esc <= 'z') {
+				value |= (*esc - 'a' + 10) << shift;
+			} else {
+				esc++;
+				continue;
+			}
+
+			if (shift == 0) {
+				cgbytes[cgoffset++] = value;
+				value = 0;
+			}
+
+			esc++;
+		}
+
+		lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+		for (addr = 0; addr < cgoffset; addr++)
+			lcd->ops->write_data(lcd, cgbytes[addr]);
+
+		/* ensures that we stop writing to CGRAM */
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+	case 'x':	/* gotoxy : LxXXX[yYYY]; */
+	case 'y':	/* gotoxy : LyYYY[xXXX]; */
+		if (!strchr(esc, ';'))
+			break;
+
+		while (*esc) {
+			if (*esc == 'x') {
+				esc++;
+				if (kstrtoul(esc, 10, &priv->addr.x) < 0)
+					break;
+			} else if (*esc == 'y') {
+				esc++;
+				if (kstrtoul(esc, 10, &priv->addr.y) < 0)
+					break;
+			} else {
+				break;
+			}
+		}
+
+		charlcd_gotoxy(lcd);
+		processed = 1;
+		break;
+	}
+
+	/* TODO: This indent party here got ugly, clean it! */
+	/* Check whether one flag was changed */
+	if (oldflags == priv->flags)
+		return processed;
+
+	/* check whether one of B,C,D flags were changed */
+	if ((oldflags ^ priv->flags) &
+	    (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
+		/* set display mode */
+		lcd->ops->write_cmd(lcd,
+			LCD_CMD_DISPLAY_CTRL |
+			((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
+			((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
+			((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
+	/* check whether one of F,N flags was changed */
+	else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
+		lcd->ops->write_cmd(lcd,
+			LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS |
+			((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
+			((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
+	/* check whether L flag was changed */
+	else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
+		charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
+
+	return processed;
+}
+
+static void charlcd_write_char(struct charlcd *lcd, char c)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	/* first, we'll test if we're in escape mode */
+	if ((c != '\n') && priv->esc_seq.len >= 0) {
+		/* yes, let's add this char to the buffer */
+		priv->esc_seq.buf[priv->esc_seq.len++] = c;
+		priv->esc_seq.buf[priv->esc_seq.len] = 0;
+	} else {
+		/* aborts any previous escape sequence */
+		priv->esc_seq.len = -1;
+
+		switch (c) {
+		case LCD_ESCAPE_CHAR:
+			/* start of an escape sequence */
+			priv->esc_seq.len = 0;
+			priv->esc_seq.buf[priv->esc_seq.len] = 0;
+			break;
+		case '\b':
+			/* go back one char and clear it */
+			if (priv->addr.x > 0) {
+				/*
+				 * check if we're not at the
+				 * end of the line
+				 */
+				if (priv->addr.x < lcd->bwidth)
+					/* back one char */
+					lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+				priv->addr.x--;
+			}
+			/* replace with a space */
+			lcd->ops->write_data(lcd, ' ');
+			/* back one char again */
+			lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+			break;
+		case '\014':
+			/* quickly clear the display */
+			charlcd_clear_fast(lcd);
+			break;
+		case '\n':
+			/*
+			 * flush the remainder of the current line and
+			 * go to the beginning of the next line
+			 */
+			for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
+				lcd->ops->write_data(lcd, ' ');
+			priv->addr.x = 0;
+			priv->addr.y = (priv->addr.y + 1) % lcd->height;
+			charlcd_gotoxy(lcd);
+			break;
+		case '\r':
+			/* go to the beginning of the same line */
+			priv->addr.x = 0;
+			charlcd_gotoxy(lcd);
+			break;
+		case '\t':
+			/* print a space instead of the tab */
+			charlcd_print(lcd, ' ');
+			break;
+		default:
+			/* simply print this char */
+			charlcd_print(lcd, c);
+			break;
+		}
+	}
+
+	/*
+	 * now we'll see if we're in an escape mode and if the current
+	 * escape sequence can be understood.
+	 */
+	if (priv->esc_seq.len >= 2) {
+		int processed = 0;
+
+		if (!strcmp(priv->esc_seq.buf, "[2J")) {
+			/* clear the display */
+			charlcd_clear_fast(lcd);
+			processed = 1;
+		} else if (!strcmp(priv->esc_seq.buf, "[H")) {
+			/* cursor to home */
+			charlcd_home(lcd);
+			processed = 1;
+		}
+		/* codes starting with ^[[L */
+		else if ((priv->esc_seq.len >= 3) &&
+			 (priv->esc_seq.buf[0] == '[') &&
+			 (priv->esc_seq.buf[1] == 'L')) {
+			processed = handle_lcd_special_code(lcd);
+		}
+
+		/* LCD special escape codes */
+		/*
+		 * flush the escape sequence if it's been processed
+		 * or if it is getting too long.
+		 */
+		if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
+			priv->esc_seq.len = -1;
+	} /* escape codes */
+}
+
+static struct charlcd *the_charlcd;
+
+static ssize_t charlcd_write(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	const char __user *tmp = buf;
+	char c;
+
+	for (; count-- > 0; (*ppos)++, tmp++) {
+		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+			/*
+			 * let's be a little nice with other processes
+			 * that need some CPU
+			 */
+			schedule();
+
+		if (get_user(c, tmp))
+			return -EFAULT;
+
+		charlcd_write_char(the_charlcd, c);
+	}
+
+	return tmp - buf;
+}
+
+static int charlcd_open(struct inode *inode, struct file *file)
+{
+	struct charlcd_priv *priv = to_priv(the_charlcd);
+
+	if (!atomic_dec_and_test(&charlcd_available))
+		return -EBUSY;	/* open only once at a time */
+
+	if (file->f_mode & FMODE_READ)	/* device is write-only */
+		return -EPERM;
+
+	if (priv->must_clear) {
+		charlcd_clear_display(&priv->lcd);
+		priv->must_clear = false;
+	}
+	return nonseekable_open(inode, file);
+}
+
+static int charlcd_release(struct inode *inode, struct file *file)
+{
+	atomic_inc(&charlcd_available);
+	return 0;
+}
+
+static const struct file_operations charlcd_fops = {
+	.write   = charlcd_write,
+	.open    = charlcd_open,
+	.release = charlcd_release,
+	.llseek  = no_llseek,
+};
+
+static struct miscdevice charlcd_dev = {
+	.minor	= LCD_MINOR,
+	.name	= "lcd",
+	.fops	= &charlcd_fops,
+};
+
+static void charlcd_puts(struct charlcd *lcd, const char *s)
+{
+	const char *tmp = s;
+	int count = strlen(s);
+
+	for (; count-- > 0; tmp++) {
+		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+			/*
+			 * let's be a little nice with other processes
+			 * that need some CPU
+			 */
+			schedule();
+
+		charlcd_write_char(lcd, *tmp);
+	}
+}
+
+/* initialize the LCD driver */
+static int charlcd_init(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+	int ret;
+
+	/*
+	 * before this line, we must NOT send anything to the display.
+	 * Since charlcd_init_display() needs to write data, we have to
+	 * enable mark the LCD initialized just before.
+	 */
+	ret = charlcd_init_display(lcd);
+	if (ret)
+		return ret;
+
+	if (lcd->ops->backlight) {
+		mutex_init(&priv->bl_tempo_lock);
+		INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
+	}
+
+	/* display a short message */
+#ifdef CONFIG_PANEL_CHANGE_MESSAGE
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+	charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
+#endif
+#else
+	charlcd_puts(lcd, "\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\n");
+#endif
+	/* clear the display on the next device opening */
+	priv->must_clear = true;
+	charlcd_home(lcd);
+	return 0;
+}
+
+struct charlcd *charlcd_alloc(unsigned int drvdata_size)
+{
+	struct charlcd_priv *priv;
+	struct charlcd *lcd;
+
+	priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->esc_seq.len = -1;
+
+	lcd = &priv->lcd;
+	lcd->bwidth = DEFAULT_LCD_BWIDTH;
+	lcd->hwidth = DEFAULT_LCD_HWIDTH;
+	lcd->drvdata = priv->drvdata;
+
+	return lcd;
+}
+EXPORT_SYMBOL_GPL(charlcd_alloc);
+
+static int panel_notify_sys(struct notifier_block *this, unsigned long code,
+			    void *unused)
+{
+	struct charlcd *lcd = the_charlcd;
+
+	switch (code) {
+	case SYS_DOWN:
+		charlcd_puts(lcd,
+			     "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	case SYS_HALT:
+		charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	case SYS_POWER_OFF:
+		charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panel_notifier = {
+	panel_notify_sys,
+	NULL,
+	0
+};
+
+int charlcd_register(struct charlcd *lcd)
+{
+	int ret;
+
+	ret = charlcd_init(lcd);
+	if (ret)
+		return ret;
+
+	ret = misc_register(&charlcd_dev);
+	if (ret)
+		return ret;
+
+	the_charlcd = lcd;
+	register_reboot_notifier(&panel_notifier);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_register);
+
+int charlcd_unregister(struct charlcd *lcd)
+{
+	struct charlcd_priv *priv = to_priv(lcd);
+
+	unregister_reboot_notifier(&panel_notifier);
+	charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+	misc_deregister(&charlcd_dev);
+	the_charlcd = NULL;
+	if (lcd->ops->backlight) {
+		cancel_delayed_work_sync(&priv->bl_work);
+		priv->lcd.ops->backlight(&priv->lcd, 0);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(charlcd_unregister);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c290990d73edf87e..6e7bf55a542ebf8c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -495,6 +495,7 @@ config VEXPRESS_SYSCFG
 config PANEL
 	tristate "Parallel port LCD/Keypad Panel support"
 	depends on PARPORT
+	select CHARLCD
 	---help---
 	  Say Y here if you have an HD44780 or KS-0074 LCD connected to your
 	  parallel port. This driver also features 4 and 6-key keypads. The LCD
diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c
index ef2ece0f26afc6b5..e0c014c2356ffb14 100644
--- a/drivers/misc/panel.c
+++ b/drivers/misc/panel.c
@@ -1,6 +1,7 @@
 /*
  * Front panel driver for Linux
  * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -54,15 +55,12 @@
 #include <linux/ctype.h>
 #include <linux/parport.h>
 #include <linux/list.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/workqueue.h>
-#include <generated/utsrelease.h>
 
 #include <linux/io.h>
 #include <linux/uaccess.h>
 
-#define LCD_MINOR		156
+#include <misc/charlcd.h>
+
 #define KEYPAD_MINOR		185
 
 #define LCD_MAXBYTES		256	/* max burst write */
@@ -76,9 +74,6 @@
 /* a key repeats this times INPUT_POLL_TIME */
 #define KEYPAD_REP_DELAY	(2)
 
-/* keep the light on this many seconds for each flash */
-#define FLASH_LIGHT_TEMPO	(4)
-
 /* converts an r_str() input to an active high, bits string : 000BAOSE */
 #define PNL_PINPUT(a)		((((unsigned char)(a)) ^ 0x7F) >> 3)
 
@@ -120,40 +115,6 @@
 #define PIN_SELECP		17
 #define PIN_NOT_SET		127
 
-#define LCD_FLAG_B		0x0004	/* blink on */
-#define LCD_FLAG_C		0x0008	/* cursor on */
-#define LCD_FLAG_D		0x0010	/* display on */
-#define LCD_FLAG_F		0x0020	/* large font mode */
-#define LCD_FLAG_N		0x0040	/* 2-rows mode */
-#define LCD_FLAG_L		0x0080	/* backlight enabled */
-
-/* LCD commands */
-#define LCD_CMD_DISPLAY_CLEAR	0x01	/* Clear entire display */
-
-#define LCD_CMD_ENTRY_MODE	0x04	/* Set entry mode */
-#define LCD_CMD_CURSOR_INC	0x02	/* Increment cursor */
-
-#define LCD_CMD_DISPLAY_CTRL	0x08	/* Display control */
-#define LCD_CMD_DISPLAY_ON	0x04	/* Set display on */
-#define LCD_CMD_CURSOR_ON	0x02	/* Set cursor on */
-#define LCD_CMD_BLINK_ON	0x01	/* Set blink on */
-
-#define LCD_CMD_SHIFT		0x10	/* Shift cursor/display */
-#define LCD_CMD_DISPLAY_SHIFT	0x08	/* Shift display instead of cursor */
-#define LCD_CMD_SHIFT_RIGHT	0x04	/* Shift display/cursor to the right */
-
-#define LCD_CMD_FUNCTION_SET	0x20	/* Set function */
-#define LCD_CMD_DATA_LEN_8BITS	0x10	/* Set data length to 8 bits */
-#define LCD_CMD_TWO_LINES	0x08	/* Set to two display lines */
-#define LCD_CMD_FONT_5X10_DOTS	0x04	/* Set char font to 5x10 dots */
-
-#define LCD_CMD_SET_CGRAM_ADDR	0x40	/* Set char generator RAM address */
-
-#define LCD_CMD_SET_DDRAM_ADDR	0x80	/* Set display data RAM address */
-
-#define LCD_ESCAPE_LEN		24	/* max chars for LCD escape command */
-#define LCD_ESCAPE_CHAR	27	/* use char 27 for escape command */
-
 #define NOT_SET			-1
 
 /* macros to simplify use of the parallel port */
@@ -245,19 +206,10 @@ struct logical_input {
 static struct {
 	bool enabled;
 	bool initialized;
-	bool must_clear;
 
-	int height;
-	int width;
-	int bwidth;
-	int hwidth;
 	int charset;
 	int proto;
 
-	struct delayed_work bl_work;
-	struct mutex bl_tempo_lock;	/* Protects access to bl_tempo */
-	bool bl_tempo;
-
 	/* TODO: use union here? */
 	struct {
 		int e;
@@ -268,20 +220,7 @@ struct logical_input {
 		int bl;
 	} pins;
 
-	/* contains the LCD config state */
-	unsigned long int flags;
-
-	/* Contains the LCD X and Y offset */
-	struct {
-		unsigned long int x;
-		unsigned long int y;
-	} addr;
-
-	/* Current escape sequence and it's length or -1 if outside */
-	struct {
-		char buf[LCD_ESCAPE_LEN + 1];
-		int len;
-	} esc_seq;
+	struct charlcd *charlcd;
 } lcd;
 
 /* Needed only for init */
@@ -464,17 +403,12 @@ struct logical_input {
 /* global variables */
 
 /* Device single-open policy control */
-static atomic_t lcd_available = ATOMIC_INIT(1);
 static atomic_t keypad_available = ATOMIC_INIT(1);
 
 static struct pardevice *pprt;
 
 static int keypad_initialized;
 
-static void (*lcd_write_cmd)(int);
-static void (*lcd_write_data)(int);
-static void (*lcd_clear_fast)(void);
-
 static DEFINE_SPINLOCK(pprt_lock);
 static struct timer_list scan_timer;
 
@@ -574,8 +508,6 @@ struct logical_input {
 module_param(keypad_enabled, int, 0000);
 MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
 
-static const unsigned char *lcd_char_conv;
-
 /* for some LCD drivers (ks0074) we need a charset conversion table. */
 static const unsigned char lcd_char_conv_ks0074[256] = {
 	/*          0|8   1|9   2|A   3|B   4|C   5|D   6|E   7|F */
@@ -752,15 +684,6 @@ static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
 	}
 }
 
-/* sleeps that many milliseconds with a reschedule */
-static void long_sleep(int ms)
-{
-	if (in_interrupt())
-		mdelay(ms);
-	else
-		schedule_timeout_interruptible(msecs_to_jiffies(ms));
-}
-
 /*
  * send a serial byte to the LCD panel. The caller is responsible for locking
  * if needed.
@@ -792,8 +715,11 @@ static void lcd_send_serial(int byte)
 }
 
 /* turn the backlight on or off */
-static void __lcd_backlight(int on)
+static void lcd_backlight(struct charlcd *charlcd, int on)
 {
+	if (lcd.pins.bl == PIN_NONE)
+		return;
+
 	/* The backlight is activated by setting the AUTOFEED line to +5V  */
 	spin_lock_irq(&pprt_lock);
 	if (on)
@@ -804,46 +730,8 @@ static void __lcd_backlight(int on)
 	spin_unlock_irq(&pprt_lock);
 }
 
-static void lcd_backlight(int on)
-{
-	if (lcd.pins.bl == PIN_NONE)
-		return;
-
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (!lcd.bl_tempo)
-		__lcd_backlight(on);
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-static void lcd_bl_off(struct work_struct *work)
-{
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (lcd.bl_tempo) {
-		lcd.bl_tempo = false;
-		if (!(lcd.flags & LCD_FLAG_L))
-			__lcd_backlight(0);
-	}
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
-/* turn the backlight on for a little while */
-static void lcd_poke(void)
-{
-	if (lcd.pins.bl == PIN_NONE)
-		return;
-
-	cancel_delayed_work_sync(&lcd.bl_work);
-
-	mutex_lock(&lcd.bl_tempo_lock);
-	if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L))
-		__lcd_backlight(1);
-	lcd.bl_tempo = true;
-	schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ);
-	mutex_unlock(&lcd.bl_tempo_lock);
-}
-
 /* send a command to the LCD panel in serial mode */
-static void lcd_write_cmd_s(int cmd)
+static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
 {
 	spin_lock_irq(&pprt_lock);
 	lcd_send_serial(0x1F);	/* R/W=W, RS=0 */
@@ -854,7 +742,7 @@ static void lcd_write_cmd_s(int cmd)
 }
 
 /* send data to the LCD panel in serial mode */
-static void lcd_write_data_s(int data)
+static void lcd_write_data_s(struct charlcd *charlcd, int data)
 {
 	spin_lock_irq(&pprt_lock);
 	lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
@@ -865,7 +753,7 @@ static void lcd_write_data_s(int data)
 }
 
 /* send a command to the LCD panel in 8 bits parallel mode */
-static void lcd_write_cmd_p8(int cmd)
+static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
 {
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
@@ -887,7 +775,7 @@ static void lcd_write_cmd_p8(int cmd)
 }
 
 /* send data to the LCD panel in 8 bits parallel mode */
-static void lcd_write_data_p8(int data)
+static void lcd_write_data_p8(struct charlcd *charlcd, int data)
 {
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
@@ -909,7 +797,7 @@ static void lcd_write_data_p8(int data)
 }
 
 /* send a command to the TI LCD panel */
-static void lcd_write_cmd_tilcd(int cmd)
+static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
 {
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the control port */
@@ -919,7 +807,7 @@ static void lcd_write_cmd_tilcd(int cmd)
 }
 
 /* send data to the TI LCD panel */
-static void lcd_write_data_tilcd(int data)
+static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
 {
 	spin_lock_irq(&pprt_lock);
 	/* present the data to the data port */
@@ -928,47 +816,13 @@ static void lcd_write_data_tilcd(int data)
 	spin_unlock_irq(&pprt_lock);
 }
 
-static void lcd_gotoxy(void)
-{
-	lcd_write_cmd(LCD_CMD_SET_DDRAM_ADDR
-		      | (lcd.addr.y ? lcd.hwidth : 0)
-		      /*
-		       * we force the cursor to stay at the end of the
-		       * line if it wants to go farther
-		       */
-		      | ((lcd.addr.x < lcd.bwidth) ? lcd.addr.x &
-			 (lcd.hwidth - 1) : lcd.bwidth - 1));
-}
-
-static void lcd_home(void)
-{
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	lcd_gotoxy();
-}
-
-static void lcd_print(char c)
-{
-	if (lcd.addr.x < lcd.bwidth) {
-		if (lcd_char_conv)
-			c = lcd_char_conv[(unsigned char)c];
-		lcd_write_data(c);
-		lcd.addr.x++;
-	}
-	/* prevents the cursor from wrapping onto the next line */
-	if (lcd.addr.x == lcd.bwidth)
-		lcd_gotoxy();
-}
-
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_s(void)
+static void lcd_clear_fast_s(struct charlcd *charlcd)
 {
 	int pos;
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		lcd_send_serial(0x5F);	/* R/W=W, RS=1 */
 		lcd_send_serial(' ' & 0x0F);
 		lcd_send_serial((' ' >> 4) & 0x0F);
@@ -976,19 +830,15 @@ static void lcd_clear_fast_s(void)
 		udelay(40);
 	}
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
 }
 
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_p8(void)
+static void lcd_clear_fast_p8(struct charlcd *charlcd)
 {
 	int pos;
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		/* present the data to the data port */
 		w_dtr(pprt, ' ');
 
@@ -1010,488 +860,62 @@ static void lcd_clear_fast_p8(void)
 		udelay(45);
 	}
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
 }
 
 /* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_tilcd(void)
+static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
 {
 	int pos;
 
-	lcd_home();
-
 	spin_lock_irq(&pprt_lock);
-	for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+	for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
 		/* present the data to the data port */
 		w_dtr(pprt, ' ');
 		udelay(60);
 	}
 
 	spin_unlock_irq(&pprt_lock);
-
-	lcd_home();
-}
-
-/* clears the display and resets X/Y */
-static void lcd_clear_display(void)
-{
-	lcd_write_cmd(LCD_CMD_DISPLAY_CLEAR);
-	lcd.addr.x = 0;
-	lcd.addr.y = 0;
-	/* we must wait a few milliseconds (15) */
-	long_sleep(15);
 }
 
-static void lcd_init_display(void)
-{
-	lcd.flags = ((lcd.height > 1) ? LCD_FLAG_N : 0)
-	    | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
-
-	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
-
-	/* 8bits, 1 line, small fonts; let's do it 3 times */
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
-	long_sleep(10);
-
-	/* set font height and lines number */
-	lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS
-		      | ((lcd.flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0)
-		      | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)
-	    );
-	long_sleep(10);
-
-	/* display off, cursor off, blink off */
-	lcd_write_cmd(LCD_CMD_DISPLAY_CTRL);
-	long_sleep(10);
-
-	lcd_write_cmd(LCD_CMD_DISPLAY_CTRL	/* set display mode */
-		      | ((lcd.flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0)
-		      | ((lcd.flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0)
-		      | ((lcd.flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)
-	    );
-
-	lcd_backlight((lcd.flags & LCD_FLAG_L) ? 1 : 0);
-
-	long_sleep(10);
-
-	/* entry mode set : increment, cursor shifting */
-	lcd_write_cmd(LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+static struct charlcd_ops charlcd_serial_ops = {
+	.write_cmd	= lcd_write_cmd_s,
+	.write_data	= lcd_write_data_s,
+	.clear_fast	= lcd_clear_fast_s,
+	.backlight	= lcd_backlight,
+};
 
-	lcd_clear_display();
-}
+static struct charlcd_ops charlcd_parallel_ops = {
+	.write_cmd	= lcd_write_cmd_p8,
+	.write_data	= lcd_write_data_p8,
+	.clear_fast	= lcd_clear_fast_p8,
+	.backlight	= lcd_backlight,
+};
 
-/*
- * These are the file operation function for user access to /dev/lcd
- * This function can also be called from inside the kernel, by
- * setting file and ppos to NULL.
- *
- */
+static struct charlcd_ops charlcd_tilcd_ops = {
+	.write_cmd	= lcd_write_cmd_tilcd,
+	.write_data	= lcd_write_data_tilcd,
+	.clear_fast	= lcd_clear_fast_tilcd,
+	.backlight	= lcd_backlight,
+};
 
-static inline int handle_lcd_special_code(void)
+/* initialize the LCD driver */
+static void lcd_init(void)
 {
-	/* LCD special codes */
-
-	int processed = 0;
+	struct charlcd *charlcd;
 
-	char *esc = lcd.esc_seq.buf + 2;
-	int oldflags = lcd.flags;
-
-	/* check for display mode flags */
-	switch (*esc) {
-	case 'D':	/* Display ON */
-		lcd.flags |= LCD_FLAG_D;
-		processed = 1;
-		break;
-	case 'd':	/* Display OFF */
-		lcd.flags &= ~LCD_FLAG_D;
-		processed = 1;
-		break;
-	case 'C':	/* Cursor ON */
-		lcd.flags |= LCD_FLAG_C;
-		processed = 1;
-		break;
-	case 'c':	/* Cursor OFF */
-		lcd.flags &= ~LCD_FLAG_C;
-		processed = 1;
-		break;
-	case 'B':	/* Blink ON */
-		lcd.flags |= LCD_FLAG_B;
-		processed = 1;
-		break;
-	case 'b':	/* Blink OFF */
-		lcd.flags &= ~LCD_FLAG_B;
-		processed = 1;
-		break;
-	case '+':	/* Back light ON */
-		lcd.flags |= LCD_FLAG_L;
-		processed = 1;
-		break;
-	case '-':	/* Back light OFF */
-		lcd.flags &= ~LCD_FLAG_L;
-		processed = 1;
-		break;
-	case '*':
-		/* flash back light */
-		lcd_poke();
-		processed = 1;
-		break;
-	case 'f':	/* Small Font */
-		lcd.flags &= ~LCD_FLAG_F;
-		processed = 1;
-		break;
-	case 'F':	/* Large Font */
-		lcd.flags |= LCD_FLAG_F;
-		processed = 1;
-		break;
-	case 'n':	/* One Line */
-		lcd.flags &= ~LCD_FLAG_N;
-		processed = 1;
-		break;
-	case 'N':	/* Two Lines */
-		lcd.flags |= LCD_FLAG_N;
-		break;
-	case 'l':	/* Shift Cursor Left */
-		if (lcd.addr.x > 0) {
-			/* back one char if not at end of line */
-			if (lcd.addr.x < lcd.bwidth)
-				lcd_write_cmd(LCD_CMD_SHIFT);
-			lcd.addr.x--;
-		}
-		processed = 1;
-		break;
-	case 'r':	/* shift cursor right */
-		if (lcd.addr.x < lcd.width) {
-			/* allow the cursor to pass the end of the line */
-			if (lcd.addr.x < (lcd.bwidth - 1))
-				lcd_write_cmd(LCD_CMD_SHIFT |
-						LCD_CMD_SHIFT_RIGHT);
-			lcd.addr.x++;
-		}
-		processed = 1;
-		break;
-	case 'L':	/* shift display left */
-		lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
-		processed = 1;
-		break;
-	case 'R':	/* shift display right */
-		lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
-				LCD_CMD_SHIFT_RIGHT);
-		processed = 1;
-		break;
-	case 'k': {	/* kill end of line */
-		int x;
-
-		for (x = lcd.addr.x; x < lcd.bwidth; x++)
-			lcd_write_data(' ');
-
-		/* restore cursor position */
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-	case 'I':	/* reinitialize display */
-		lcd_init_display();
-		processed = 1;
-		break;
-	case 'G': {
-		/* Generator : LGcxxxxx...xx; must have <c> between '0'
-		 * and '7', representing the numerical ASCII code of the
-		 * redefined character, and <xx...xx> a sequence of 16
-		 * hex digits representing 8 bytes for each character.
-		 * Most LCDs will only use 5 lower bits of the 7 first
-		 * bytes.
-		 */
-
-		unsigned char cgbytes[8];
-		unsigned char cgaddr;
-		int cgoffset;
-		int shift;
-		char value;
-		int addr;
-
-		if (!strchr(esc, ';'))
-			break;
-
-		esc++;
-
-		cgaddr = *(esc++) - '0';
-		if (cgaddr > 7) {
-			processed = 1;
-			break;
-		}
-
-		cgoffset = 0;
-		shift = 0;
-		value = 0;
-		while (*esc && cgoffset < 8) {
-			shift ^= 4;
-			if (*esc >= '0' && *esc <= '9') {
-				value |= (*esc - '0') << shift;
-			} else if (*esc >= 'A' && *esc <= 'Z') {
-				value |= (*esc - 'A' + 10) << shift;
-			} else if (*esc >= 'a' && *esc <= 'z') {
-				value |= (*esc - 'a' + 10) << shift;
-			} else {
-				esc++;
-				continue;
-			}
-
-			if (shift == 0) {
-				cgbytes[cgoffset++] = value;
-				value = 0;
-			}
-
-			esc++;
-		}
-
-		lcd_write_cmd(LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
-		for (addr = 0; addr < cgoffset; addr++)
-			lcd_write_data(cgbytes[addr]);
-
-		/* ensures that we stop writing to CGRAM */
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-	case 'x':	/* gotoxy : LxXXX[yYYY]; */
-	case 'y':	/* gotoxy : LyYYY[xXXX]; */
-		if (!strchr(esc, ';'))
-			break;
-
-		while (*esc) {
-			if (*esc == 'x') {
-				esc++;
-				if (kstrtoul(esc, 10, &lcd.addr.x) < 0)
-					break;
-			} else if (*esc == 'y') {
-				esc++;
-				if (kstrtoul(esc, 10, &lcd.addr.y) < 0)
-					break;
-			} else {
-				break;
-			}
-		}
-
-		lcd_gotoxy();
-		processed = 1;
-		break;
-	}
-
-	/* TODO: This indent party here got ugly, clean it! */
-	/* Check whether one flag was changed */
-	if (oldflags != lcd.flags) {
-		/* check whether one of B,C,D flags were changed */
-		if ((oldflags ^ lcd.flags) &
-		    (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
-			/* set display mode */
-			lcd_write_cmd(LCD_CMD_DISPLAY_CTRL
-				      | ((lcd.flags & LCD_FLAG_D)
-						      ? LCD_CMD_DISPLAY_ON : 0)
-				      | ((lcd.flags & LCD_FLAG_C)
-						      ? LCD_CMD_CURSOR_ON : 0)
-				      | ((lcd.flags & LCD_FLAG_B)
-						      ? LCD_CMD_BLINK_ON : 0));
-		/* check whether one of F,N flags was changed */
-		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_F | LCD_FLAG_N))
-			lcd_write_cmd(LCD_CMD_FUNCTION_SET
-				      | LCD_CMD_DATA_LEN_8BITS
-				      | ((lcd.flags & LCD_FLAG_F)
-						      ? LCD_CMD_FONT_5X10_DOTS
-								      : 0)
-				      | ((lcd.flags & LCD_FLAG_N)
-						      ? LCD_CMD_TWO_LINES
-								      : 0));
-		/* check whether L flag was changed */
-		else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L))
-			lcd_backlight(!!(lcd.flags & LCD_FLAG_L));
-	}
-
-	return processed;
-}
-
-static void lcd_write_char(char c)
-{
-	/* first, we'll test if we're in escape mode */
-	if ((c != '\n') && lcd.esc_seq.len >= 0) {
-		/* yes, let's add this char to the buffer */
-		lcd.esc_seq.buf[lcd.esc_seq.len++] = c;
-		lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
-	} else {
-		/* aborts any previous escape sequence */
-		lcd.esc_seq.len = -1;
-
-		switch (c) {
-		case LCD_ESCAPE_CHAR:
-			/* start of an escape sequence */
-			lcd.esc_seq.len = 0;
-			lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
-			break;
-		case '\b':
-			/* go back one char and clear it */
-			if (lcd.addr.x > 0) {
-				/*
-				 * check if we're not at the
-				 * end of the line
-				 */
-				if (lcd.addr.x < lcd.bwidth)
-					/* back one char */
-					lcd_write_cmd(LCD_CMD_SHIFT);
-				lcd.addr.x--;
-			}
-			/* replace with a space */
-			lcd_write_data(' ');
-			/* back one char again */
-			lcd_write_cmd(LCD_CMD_SHIFT);
-			break;
-		case '\014':
-			/* quickly clear the display */
-			lcd_clear_fast();
-			break;
-		case '\n':
-			/*
-			 * flush the remainder of the current line and
-			 * go to the beginning of the next line
-			 */
-			for (; lcd.addr.x < lcd.bwidth; lcd.addr.x++)
-				lcd_write_data(' ');
-			lcd.addr.x = 0;
-			lcd.addr.y = (lcd.addr.y + 1) % lcd.height;
-			lcd_gotoxy();
-			break;
-		case '\r':
-			/* go to the beginning of the same line */
-			lcd.addr.x = 0;
-			lcd_gotoxy();
-			break;
-		case '\t':
-			/* print a space instead of the tab */
-			lcd_print(' ');
-			break;
-		default:
-			/* simply print this char */
-			lcd_print(c);
-			break;
-		}
-	}
+	charlcd = charlcd_alloc(0);
+	if (!charlcd)
+		return;
 
 	/*
-	 * now we'll see if we're in an escape mode and if the current
-	 * escape sequence can be understood.
+	 * Init lcd struct with load-time values to preserve exact
+	 * current functionality (at least for now).
 	 */
-	if (lcd.esc_seq.len >= 2) {
-		int processed = 0;
-
-		if (!strcmp(lcd.esc_seq.buf, "[2J")) {
-			/* clear the display */
-			lcd_clear_fast();
-			processed = 1;
-		} else if (!strcmp(lcd.esc_seq.buf, "[H")) {
-			/* cursor to home */
-			lcd_home();
-			processed = 1;
-		}
-		/* codes starting with ^[[L */
-		else if ((lcd.esc_seq.len >= 3) &&
-			 (lcd.esc_seq.buf[0] == '[') &&
-			 (lcd.esc_seq.buf[1] == 'L')) {
-			processed = handle_lcd_special_code();
-		}
-
-		/* LCD special escape codes */
-		/*
-		 * flush the escape sequence if it's been processed
-		 * or if it is getting too long.
-		 */
-		if (processed || (lcd.esc_seq.len >= LCD_ESCAPE_LEN))
-			lcd.esc_seq.len = -1;
-	} /* escape codes */
-}
+	charlcd->height = lcd_height;
+	charlcd->width = lcd_width;
+	charlcd->bwidth = lcd_bwidth;
+	charlcd->hwidth = lcd_hwidth;
 
-static ssize_t lcd_write(struct file *file,
-			 const char __user *buf, size_t count, loff_t *ppos)
-{
-	const char __user *tmp = buf;
-	char c;
-
-	for (; count-- > 0; (*ppos)++, tmp++) {
-		if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
-			/*
-			 * let's be a little nice with other processes
-			 * that need some CPU
-			 */
-			schedule();
-
-		if (get_user(c, tmp))
-			return -EFAULT;
-
-		lcd_write_char(c);
-	}
-
-	return tmp - buf;
-}
-
-static int lcd_open(struct inode *inode, struct file *file)
-{
-	if (!atomic_dec_and_test(&lcd_available))
-		return -EBUSY;	/* open only once at a time */
-
-	if (file->f_mode & FMODE_READ)	/* device is write-only */
-		return -EPERM;
-
-	if (lcd.must_clear) {
-		lcd_clear_display();
-		lcd.must_clear = false;
-	}
-	return nonseekable_open(inode, file);
-}
-
-static int lcd_release(struct inode *inode, struct file *file)
-{
-	atomic_inc(&lcd_available);
-	return 0;
-}
-
-static const struct file_operations lcd_fops = {
-	.write   = lcd_write,
-	.open    = lcd_open,
-	.release = lcd_release,
-	.llseek  = no_llseek,
-};
-
-static struct miscdevice lcd_dev = {
-	.minor	= LCD_MINOR,
-	.name	= "lcd",
-	.fops	= &lcd_fops,
-};
-
-/* public function usable from the kernel for any purpose */
-static void panel_lcd_print(const char *s)
-{
-	const char *tmp = s;
-	int count = strlen(s);
-
-	if (lcd.enabled && lcd.initialized) {
-		for (; count-- > 0; tmp++) {
-			if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
-				/*
-				 * let's be a little nice with other processes
-				 * that need some CPU
-				 */
-				schedule();
-
-			lcd_write_char(*tmp);
-		}
-	}
-}
-
-/* initialize the LCD driver */
-static void lcd_init(void)
-{
 	switch (selected_lcd_type) {
 	case LCD_TYPE_OLD:
 		/* parallel mode, 8 bits */
@@ -1500,10 +924,10 @@ static void lcd_init(void)
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.rs = PIN_AUTOLF;
 
-		lcd.width = 40;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 40;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 	case LCD_TYPE_KS0074:
 		/* serial mode, ks0074 */
@@ -1513,10 +937,10 @@ static void lcd_init(void)
 		lcd.pins.cl = PIN_STROBE;
 		lcd.pins.da = PIN_D0;
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 16;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 16;
+		charlcd->height = 2;
 		break;
 	case LCD_TYPE_NEXCOM:
 		/* parallel mode, 8 bits, generic */
@@ -1526,10 +950,10 @@ static void lcd_init(void)
 		lcd.pins.rs = PIN_SELECP;
 		lcd.pins.rw = PIN_INITP;
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 	case LCD_TYPE_CUSTOM:
 		/* customer-defined */
@@ -1545,22 +969,22 @@ static void lcd_init(void)
 		lcd.pins.e = PIN_STROBE;
 		lcd.pins.rs = PIN_SELECP;
 
-		lcd.width = 16;
-		lcd.bwidth = 40;
-		lcd.hwidth = 64;
-		lcd.height = 2;
+		charlcd->width = 16;
+		charlcd->bwidth = 40;
+		charlcd->hwidth = 64;
+		charlcd->height = 2;
 		break;
 	}
 
 	/* Overwrite with module params set on loading */
 	if (lcd_height != NOT_SET)
-		lcd.height = lcd_height;
+		charlcd->height = lcd_height;
 	if (lcd_width != NOT_SET)
-		lcd.width = lcd_width;
+		charlcd->width = lcd_width;
 	if (lcd_bwidth != NOT_SET)
-		lcd.bwidth = lcd_bwidth;
+		charlcd->bwidth = lcd_bwidth;
 	if (lcd_hwidth != NOT_SET)
-		lcd.hwidth = lcd_hwidth;
+		charlcd->hwidth = lcd_hwidth;
 	if (lcd_charset != NOT_SET)
 		lcd.charset = lcd_charset;
 	if (lcd_proto != NOT_SET)
@@ -1579,19 +1003,17 @@ static void lcd_init(void)
 		lcd.pins.bl = lcd_bl_pin;
 
 	/* this is used to catch wrong and default values */
-	if (lcd.width <= 0)
-		lcd.width = DEFAULT_LCD_WIDTH;
-	if (lcd.bwidth <= 0)
-		lcd.bwidth = DEFAULT_LCD_BWIDTH;
-	if (lcd.hwidth <= 0)
-		lcd.hwidth = DEFAULT_LCD_HWIDTH;
-	if (lcd.height <= 0)
-		lcd.height = DEFAULT_LCD_HEIGHT;
+	if (charlcd->width <= 0)
+		charlcd->width = DEFAULT_LCD_WIDTH;
+	if (charlcd->bwidth <= 0)
+		charlcd->bwidth = DEFAULT_LCD_BWIDTH;
+	if (charlcd->hwidth <= 0)
+		charlcd->hwidth = DEFAULT_LCD_HWIDTH;
+	if (charlcd->height <= 0)
+		charlcd->height = DEFAULT_LCD_HEIGHT;
 
 	if (lcd.proto == LCD_PROTO_SERIAL) {	/* SERIAL */
-		lcd_write_cmd = lcd_write_cmd_s;
-		lcd_write_data = lcd_write_data_s;
-		lcd_clear_fast = lcd_clear_fast_s;
+		charlcd->ops = &charlcd_serial_ops;
 
 		if (lcd.pins.cl == PIN_NOT_SET)
 			lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1599,9 +1021,7 @@ static void lcd_init(void)
 			lcd.pins.da = DEFAULT_LCD_PIN_SDA;
 
 	} else if (lcd.proto == LCD_PROTO_PARALLEL) {	/* PARALLEL */
-		lcd_write_cmd = lcd_write_cmd_p8;
-		lcd_write_data = lcd_write_data_p8;
-		lcd_clear_fast = lcd_clear_fast_p8;
+		charlcd->ops = &charlcd_parallel_ops;
 
 		if (lcd.pins.e == PIN_NOT_SET)
 			lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1610,9 +1030,7 @@ static void lcd_init(void)
 		if (lcd.pins.rw == PIN_NOT_SET)
 			lcd.pins.rw = DEFAULT_LCD_PIN_RW;
 	} else {
-		lcd_write_cmd = lcd_write_cmd_tilcd;
-		lcd_write_data = lcd_write_data_tilcd;
-		lcd_clear_fast = lcd_clear_fast_tilcd;
+		charlcd->ops = &charlcd_tilcd_ops;
 	}
 
 	if (lcd.pins.bl == PIN_NOT_SET)
@@ -1635,14 +1053,9 @@ static void lcd_init(void)
 		lcd.charset = DEFAULT_LCD_CHARSET;
 
 	if (lcd.charset == LCD_CHARSET_KS0074)
-		lcd_char_conv = lcd_char_conv_ks0074;
+		charlcd->char_conv = lcd_char_conv_ks0074;
 	else
-		lcd_char_conv = NULL;
-
-	if (lcd.pins.bl != PIN_NONE) {
-		mutex_init(&lcd.bl_tempo_lock);
-		INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off);
-	}
+		charlcd->char_conv = NULL;
 
 	pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
 		    lcd_bits[LCD_PORT_C][LCD_BIT_E]);
@@ -1657,25 +1070,8 @@ static void lcd_init(void)
 	pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
 		    lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
 
-	/*
-	 * before this line, we must NOT send anything to the display.
-	 * Since lcd_init_display() needs to write data, we have to
-	 * enable mark the LCD initialized just before.
-	 */
+	lcd.charlcd = charlcd;
 	lcd.initialized = true;
-	lcd_init_display();
-
-	/* display a short message */
-#ifdef CONFIG_PANEL_CHANGE_MESSAGE
-#ifdef CONFIG_PANEL_BOOT_MESSAGE
-	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
-#endif
-#else
-	panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
-#endif
-	/* clear the display on the next device opening */
-	lcd.must_clear = true;
-	lcd_home();
 }
 
 /*
@@ -2011,7 +1407,7 @@ static void panel_scan_timer(void)
 	}
 
 	if (keypressed && lcd.enabled && lcd.initialized)
-		lcd_poke();
+		charlcd_poke(lcd.charlcd);
 
 	mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
 }
@@ -2175,35 +1571,6 @@ static void keypad_init(void)
 /* device initialization                          */
 /**************************************************/
 
-static int panel_notify_sys(struct notifier_block *this, unsigned long code,
-			    void *unused)
-{
-	if (lcd.enabled && lcd.initialized) {
-		switch (code) {
-		case SYS_DOWN:
-			panel_lcd_print
-			    ("\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		case SYS_HALT:
-			panel_lcd_print
-			    ("\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		case SYS_POWER_OFF:
-			panel_lcd_print("\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
-			break;
-		default:
-			break;
-		}
-	}
-	return NOTIFY_DONE;
-}
-
-static struct notifier_block panel_notifier = {
-	panel_notify_sys,
-	NULL,
-	0
-};
-
 static void panel_attach(struct parport *port)
 {
 	struct pardev_cb panel_cb;
@@ -2239,7 +1606,7 @@ static void panel_attach(struct parport *port)
 	 */
 	if (lcd.enabled) {
 		lcd_init();
-		if (misc_register(&lcd_dev))
+		if (!lcd.charlcd || charlcd_register(lcd.charlcd))
 			goto err_unreg_device;
 	}
 
@@ -2248,13 +1615,14 @@ static void panel_attach(struct parport *port)
 		if (misc_register(&keypad_dev))
 			goto err_lcd_unreg;
 	}
-	register_reboot_notifier(&panel_notifier);
 	return;
 
 err_lcd_unreg:
 	if (lcd.enabled)
-		misc_deregister(&lcd_dev);
+		charlcd_unregister(lcd.charlcd);
 err_unreg_device:
+	kfree(lcd.charlcd);
+	lcd.charlcd = NULL;
 	parport_unregister_device(pprt);
 	pprt = NULL;
 }
@@ -2278,20 +1646,16 @@ static void panel_detach(struct parport *port)
 	}
 
 	if (lcd.enabled) {
-		panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
-		misc_deregister(&lcd_dev);
-		if (lcd.pins.bl != PIN_NONE) {
-			cancel_delayed_work_sync(&lcd.bl_work);
-			__lcd_backlight(0);
-		}
+		charlcd_unregister(lcd.charlcd);
 		lcd.initialized = false;
+		kfree(lcd.charlcd);
+		lcd.charlcd = NULL;
 	}
 
 	/* TODO: free all input signals */
 	parport_release(pprt);
 	parport_unregister_device(pprt);
 	pprt = NULL;
-	unregister_reboot_notifier(&panel_notifier);
 }
 
 static struct parport_driver panel_driver = {
@@ -2369,10 +1733,6 @@ static int __init panel_init_module(void)
 		 * Init lcd struct with load-time values to preserve exact
 		 * current functionality (at least for now).
 		 */
-		lcd.height = lcd_height;
-		lcd.width = lcd_width;
-		lcd.bwidth = lcd_bwidth;
-		lcd.hwidth = lcd_hwidth;
 		lcd.charset = lcd_charset;
 		lcd.proto = lcd_proto;
 		lcd.pins.e = lcd_e_pin;
@@ -2381,9 +1741,6 @@ static int __init panel_init_module(void)
 		lcd.pins.cl = lcd_cl_pin;
 		lcd.pins.da = lcd_da_pin;
 		lcd.pins.bl = lcd_bl_pin;
-
-		/* Leave it for now, just in case */
-		lcd.esc_seq.len = -1;
 	}
 
 	switch (selected_keypad_type) {
diff --git a/include/misc/charlcd.h b/include/misc/charlcd.h
new file mode 100644
index 0000000000000000..c40047b673c9ea09
--- /dev/null
+++ b/include/misc/charlcd.h
@@ -0,0 +1,40 @@
+/*
+ * Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+struct charlcd {
+	const struct charlcd_ops *ops;
+	const unsigned char *char_conv;	/* Optional */
+
+	int height;
+	int width;
+	int bwidth;			/* Default set by charlcd_alloc() */
+	int hwidth;			/* Default set by charlcd_alloc() */
+
+	void *drvdata;			/* Set by charlcd_alloc() */
+};
+
+struct charlcd_ops {
+	/* Required */
+	void (*write_cmd)(struct charlcd *lcd, int cmd);
+	void (*write_data)(struct charlcd *lcd, int data);
+
+	/* Optional */
+	void (*clear_fast)(struct charlcd *lcd);
+	void (*backlight)(struct charlcd *lcd, int on);
+};
+
+struct charlcd *charlcd_alloc(unsigned int drvdata_size);
+
+int charlcd_register(struct charlcd *lcd);
+int charlcd_unregister(struct charlcd *lcd);
+
+void charlcd_poke(struct charlcd *lcd);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 10/13] auxdisplay: charlcd: Add support for 4-bit interfaces
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (8 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 11/13] auxdisplay: charlcd: Add support for displays with more than two lines Geert Uytterhoeven
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

In 4-bit mode, 8-bit commands and data are written using two raw writes
to the data interface: high nibble first, low nibble last.  This must be
handled by the low-level driver.

However, as we don't know in which mode (4-bit or 8-bit) nor 4-bit phase
the LCD was left, initialization must always be handled using raw
writes, and needs to configure the LCD for 8-bit mode first.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/auxdisplay/charlcd.c | 36 ++++++++++++++++++++++++++++++------
 include/misc/charlcd.h       |  2 ++
 2 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 8d45410056357880..6d27d9363c9baaf2 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -223,24 +223,46 @@ static void charlcd_clear_display(struct charlcd *lcd)
 
 static int charlcd_init_display(struct charlcd *lcd)
 {
+	void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
 	struct charlcd_priv *priv = to_priv(lcd);
+	u8 init;
+
+	if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
+		return -EINVAL;
 
 	priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
 		      LCD_FLAG_C | LCD_FLAG_B;
 
 	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
 
-	/* 8bits, 1 line, small fonts; let's do it 3 times */
-	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	/*
+	 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
+	 * the LCD is in 8-bit mode afterwards
+	 */
+	init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
+	if (lcd->ifwidth == 4) {
+		init >>= 4;
+		write_cmd_raw = lcd->ops->write_cmd_raw4;
+	} else {
+		write_cmd_raw = lcd->ops->write_cmd;
+	}
+	write_cmd_raw(lcd, init);
 	long_sleep(10);
-	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	write_cmd_raw(lcd, init);
 	long_sleep(10);
-	lcd->ops->write_cmd(lcd, LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+	write_cmd_raw(lcd, init);
 	long_sleep(10);
 
+	if (lcd->ifwidth == 4) {
+		/* Switch to 4-bit mode, 1 line, small fonts */
+		lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
+		long_sleep(10);
+	}
+
 	/* set font height and lines number */
 	lcd->ops->write_cmd(lcd,
-		LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS |
+		LCD_CMD_FUNCTION_SET |
+		((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
 		((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
 		((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
 	long_sleep(10);
@@ -482,7 +504,8 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
 	/* check whether one of F,N flags was changed */
 	else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
 		lcd->ops->write_cmd(lcd,
-			LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS |
+			LCD_CMD_FUNCTION_SET |
+			((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
 			((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
 			((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
 	/* check whether L flag was changed */
@@ -716,6 +739,7 @@ struct charlcd *charlcd_alloc(unsigned int drvdata_size)
 	priv->esc_seq.len = -1;
 
 	lcd = &priv->lcd;
+	lcd->ifwidth = 8;
 	lcd->bwidth = DEFAULT_LCD_BWIDTH;
 	lcd->hwidth = DEFAULT_LCD_HWIDTH;
 	lcd->drvdata = priv->drvdata;
diff --git a/include/misc/charlcd.h b/include/misc/charlcd.h
index c40047b673c9ea09..23f61850f3639ae1 100644
--- a/include/misc/charlcd.h
+++ b/include/misc/charlcd.h
@@ -14,6 +14,7 @@ struct charlcd {
 	const struct charlcd_ops *ops;
 	const unsigned char *char_conv;	/* Optional */
 
+	int ifwidth;			/* 4-bit or 8-bit (default) */
 	int height;
 	int width;
 	int bwidth;			/* Default set by charlcd_alloc() */
@@ -28,6 +29,7 @@ struct charlcd_ops {
 	void (*write_data)(struct charlcd *lcd, int data);
 
 	/* Optional */
+	void (*write_cmd_raw4)(struct charlcd *lcd, int cmd);	/* 4-bit only */
 	void (*clear_fast)(struct charlcd *lcd);
 	void (*backlight)(struct charlcd *lcd, int on);
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 11/13] auxdisplay: charlcd: Add support for displays with more than two lines
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (9 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 10/13] auxdisplay: charlcd: Add support for 4-bit interfaces Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-06 14:38 ` [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780 Geert Uytterhoeven
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

On displays with more than two lines, the additional lines are stored in
the buffers used for the first two lines, but beyond the visible parts.
Adjust the DDRAM address calculation to cater for this.

When clearing the display, avoid writing more spaces than the actual
size of the physical buffer.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
 drivers/auxdisplay/charlcd.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 6d27d9363c9baaf2..6aea90086e3695c5 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -159,15 +159,19 @@ void charlcd_poke(struct charlcd *lcd)
 static void charlcd_gotoxy(struct charlcd *lcd)
 {
 	struct charlcd_priv *priv = to_priv(lcd);
+	unsigned int addr;
 
-	lcd->ops->write_cmd(lcd,
-		LCD_CMD_SET_DDRAM_ADDR | (priv->addr.y ? lcd->hwidth : 0) |
-		/*
-		 * we force the cursor to stay at the end of the
-		 * line if it wants to go farther
-		 */
-		((priv->addr.x < lcd->bwidth) ? priv->addr.x & (lcd->hwidth - 1)
-					      : lcd->bwidth - 1));
+	/*
+	 * we force the cursor to stay at the end of the
+	 * line if it wants to go farther
+	 */
+	addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1)
+					  : lcd->bwidth - 1;
+	if (priv->addr.y & 1)
+		addr += lcd->hwidth;
+	if (priv->addr.y & 2)
+		addr += lcd->bwidth;
+	lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
 }
 
 static void charlcd_home(struct charlcd *lcd)
@@ -203,7 +207,7 @@ static void charlcd_clear_fast(struct charlcd *lcd)
 	if (lcd->ops->clear_fast)
 		lcd->ops->clear_fast(lcd);
 	else
-		for (pos = 0; pos < lcd->height * lcd->hwidth; pos++)
+		for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
 			lcd->ops->write_data(lcd, ' ');
 
 	charlcd_home(lcd);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (10 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 11/13] auxdisplay: charlcd: Add support for displays with more than two lines Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-09  1:11     ` Rob Herring
  2017-02-06 14:38 ` [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven, Rob Herring, Mark Rutland, devicetree

Add DT bindings for an Hitachi HD44780 Character LCD Controller where
its M6800 bus interface is connected to GPIOs.

Memory-mapped configurations are not yet supported.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: devicetree@vger.kernel.org
---
 .../devicetree/bindings/auxdisplay/hit,hd44780.txt | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt

diff --git a/Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt b/Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt
new file mode 100644
index 0000000000000000..ee4054da458d412f
--- /dev/null
+++ b/Documentation/devicetree/bindings/auxdisplay/hit,hd44780.txt
@@ -0,0 +1,44 @@
+DT bindings for the Hitachi HD44780 Character LCD Controller
+
+The Hitachi HD44780 Character LCD Controller is commonly used on character LCDs
+that can display one or more lines of text. It exposes an M6800 bus interface,
+which can be used in either 4-bit or 8-bit mode.
+
+Required properties:
+  - compatible: Must contain "hit,hd44780",
+  - data-gpios: Must contain an array of either 4 or 8 GPIO specifiers,
+    referring to the GPIO pins connected to the data signal lines DB0-DB7
+    (8-bit mode) or DB4-DB7 (4-bit mode) of the LCD Controller's bus interface,
+  - enable-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "E" (Enable) signal line of the LCD Controller's bus
+    interface,
+  - rs-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "RS" (Register Select) signal line of the LCD Controller's
+    bus interface,
+  - display-height: Height of the display, in character cells,
+  - display-width: Width of the display, in character cells.
+
+Optional properties:
+  - rw-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    connected to the "RW" (Read/Write) signal line of the LCD Controller's bus
+    interface,
+  - backlight-gpios: Must contain a GPIO specifier, referring to the GPIO pin
+    used for enabling the LCD's backlight,
+  - internal-buffer-width: Internal buffer width (default is 40 for displays
+    with 1 or 2 lines, and display-width for displays with more than 2 lines).
+
+Example:
+
+	auxdisplay {
+		compatible = "hit,hd44780";
+
+		data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>,
+			     <&hc595 1 GPIO_ACTIVE_HIGH>,
+			     <&hc595 2 GPIO_ACTIVE_HIGH>,
+			     <&hc595 3 GPIO_ACTIVE_HIGH>;
+		enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>;
+		rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>;
+
+		display-height = <2>;
+		display-width = <16>;
+	};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (11 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780 Geert Uytterhoeven
@ 2017-02-06 14:38 ` Geert Uytterhoeven
  2017-02-07 11:23   ` Andy Shevchenko
  2017-02-06 15:09 ` [PATCH 00/13] " Willy TARREAU
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 14:38 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven, Rob Herring, Mark Rutland, devicetree

The Hitachi HD44780 Character LCD Controller is commonly used on
character LCDs that can display one or more lines of text.

This driver supports character LCDs connected to GPIOs, using either a
4-bit or 8-bit data bus, and provides access through the charlcd core
and /dev/lcd.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: devicetree@vger.kernel.org
---
 drivers/auxdisplay/Kconfig   |  11 ++
 drivers/auxdisplay/Makefile  |   1 +
 drivers/auxdisplay/hd44780.c | 326 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 338 insertions(+)
 create mode 100644 drivers/auxdisplay/hd44780.c

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index b4686380d4e3cca9..2964128b6046882d 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -18,6 +18,17 @@ config CHARLCD
 
 if AUXDISPLAY
 
+config HD44780
+	tristate "HD44780 Character LCD support"
+	depends on OF
+	select CHARLCD
+	---help---
+	  Enable support for Character LCDs using a HD44780 controller.
+	  The LCD is accessible through the /dev/lcd char device (10, 156).
+	  This code can either be compiled as a module, or linked into the
+	  kernel and started at boot.
+	  If you don't understand what all this is about, say N.
+
 config KS0108
 	tristate "KS0108 LCD Controller"
 	depends on PARPORT_PC
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index f56f45dcc78e332e..fcefa686be0fea9b 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_CHARLCD)		+= charlcd.o
 obj-$(CONFIG_KS0108)		+= ks0108.o
 obj-$(CONFIG_CFAG12864B)	+= cfag12864b.o cfag12864bfb.o
 obj-$(CONFIG_IMG_ASCII_LCD)	+= img-ascii-lcd.o
+obj-$(CONFIG_HD44780)		+= hd44780.o
 obj-$(CONFIG_HT16K33)		+= ht16k33.o
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
new file mode 100644
index 0000000000000000..36aa5942d9374f88
--- /dev/null
+++ b/drivers/auxdisplay/hd44780.c
@@ -0,0 +1,326 @@
+/*
+ * HD44780 Character LCD driver for Linux
+ *
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ * Copyright (C) 2016-2017 Glider bvba
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <misc/charlcd.h>
+
+
+enum hd44780_pin {
+	/* Order does matter due to writing to GPIO array subsets! */
+	PIN_DATA0,	/* Optional */
+	PIN_DATA1,	/* Optional */
+	PIN_DATA2,	/* Optional */
+	PIN_DATA3,	/* Optional */
+	PIN_DATA4,
+	PIN_DATA5,
+	PIN_DATA6,
+	PIN_DATA7,
+	PIN_CTRL_RS,
+	PIN_CTRL_RW,	/* Optional */
+	PIN_CTRL_E,
+	PIN_CTRL_BL,   /* Optional */
+	PIN_NUM
+};
+
+struct hd44780 {
+	struct gpio_desc *pins[PIN_NUM];
+};
+
+static void hd44780_backlight(struct charlcd *lcd, int on)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	if (hd->pins[PIN_CTRL_BL])
+		gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
+}
+
+static void hd44780_strobe_gpio(struct hd44780 *hd)
+{
+	/* Maintain the data during 20 us before the strobe */
+	udelay(20);
+
+	gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 1);
+
+	/* Maintain the strobe during 40 us */
+	udelay(40);
+
+	gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 0);
+}
+
+/* write to an LCD panel register in 8 bit GPIO mode */
+static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+	int values[10];	/* for DATA[0-7], RS, RW */
+	unsigned int i, n;
+
+	for (i = 0; i < 8; i++)
+		values[PIN_DATA0 + i] = !!(val & BIT(i));
+	values[PIN_CTRL_RS] = rs;
+	n = 9;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* write to an LCD panel register in 4 bit GPIO mode */
+static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
+{
+	int values[10];	/* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+	unsigned int i, n;
+
+	/* High nibble + RS, RW */
+	for (i = 4; i < 8; i++)
+		values[PIN_DATA0 + i] = !!(val & BIT(i));
+	values[PIN_CTRL_RS] = rs;
+	n = 5;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+
+	/* Low nibble */
+	for (i = 0; i < 4; i++)
+		values[PIN_DATA4 + i] = !!(val & BIT(i));
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* Send a command to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio8(hd, cmd, 0);
+
+	/* The shortest command takes at least 120 us */
+	udelay(120);
+}
+
+/* Send data to the LCD panel in 8 bit GPIO mode */
+static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio8(hd, data, 1);
+
+	/* The shortest data takes at least 45 us */
+	udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio8 = {
+	.write_cmd	= hd44780_write_cmd_gpio8,
+	.write_data	= hd44780_write_data_gpio8,
+	.backlight	= hd44780_backlight,
+};
+
+/* Send a command to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio4(hd, cmd, 0);
+
+	/* The shortest command takes at least 120 us */
+	udelay(120);
+}
+
+/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
+static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
+{
+	int values[10];	/* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+	struct hd44780 *hd = lcd->drvdata;
+	unsigned int i, n;
+
+	/* Command nibble + RS, RW */
+	for (i = 0; i < 4; i++)
+		values[PIN_DATA4 + i] = !!(cmd & BIT(i));
+	values[PIN_CTRL_RS] = 0;
+	n = 5;
+	if (hd->pins[PIN_CTRL_RW]) {
+		values[PIN_CTRL_RW] = 0;
+		n++;
+	}
+
+	/* Present the data to the port */
+	gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
+				       &values[PIN_DATA4]);
+
+	hd44780_strobe_gpio(hd);
+}
+
+/* Send data to the LCD panel in 4 bit GPIO mode */
+static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
+{
+	struct hd44780 *hd = lcd->drvdata;
+
+	hd44780_write_gpio4(hd, data, 1);
+
+	/* The shortest data takes at least 45 us */
+	udelay(45);
+}
+
+static const struct charlcd_ops hd44780_ops_gpio4 = {
+	.write_cmd	= hd44780_write_cmd_gpio4,
+	.write_cmd_raw4	= hd44780_write_cmd_raw_gpio4,
+	.write_data	= hd44780_write_data_gpio4,
+	.backlight	= hd44780_backlight,
+};
+
+static int hd44780_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	unsigned int i, base;
+	struct charlcd *lcd;
+	struct hd44780 *hd;
+	int ifwidth, ret;
+
+	/* Required pins */
+	ifwidth = of_gpio_named_count(np, "data-gpios");
+	if (ifwidth < 0)
+		return ifwidth;
+
+	switch (ifwidth) {
+	case 4:
+		base = PIN_DATA4;
+		break;
+	case 8:
+		base = PIN_DATA0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	lcd = charlcd_alloc(sizeof(struct hd44780));
+	if (!lcd)
+		return -ENOMEM;
+
+	hd = lcd->drvdata;
+
+	for (i = 0; i < ifwidth; i++) {
+		hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
+							  GPIOD_OUT_LOW);
+		if (IS_ERR(hd->pins[base + i])) {
+			ret = PTR_ERR(hd->pins[base + i]);
+			goto fail;
+		}
+	}
+
+	hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_E])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_E]);
+		goto fail;
+	}
+
+	hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);
+	if (IS_ERR(hd->pins[PIN_CTRL_RS])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);
+		goto fail;
+	}
+
+	/* Optional pins */
+	hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, "rw",
+							GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
+		goto fail;
+	}
+
+	hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",
+							GPIOD_OUT_LOW);
+	if (IS_ERR(hd->pins[PIN_CTRL_BL])) {
+		ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);
+		goto fail;
+	}
+
+	/* Required properties */
+	ret = of_property_read_u32(np, "display-height", &lcd->height);
+	if (ret)
+		goto fail;
+	ret = of_property_read_u32(np, "display-width", &lcd->width);
+	if (ret)
+		goto fail;
+
+	/*
+	 * On displays with more than two rows, the internal buffer width is
+	 * usually equal to the display width
+	 */
+	if (lcd->height > 2)
+		lcd->bwidth = lcd->width;
+
+	/* Optional properties */
+	of_property_read_u32(np, "internal-buffer-width", &lcd->bwidth);
+
+	lcd->ifwidth = ifwidth;
+	lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
+
+	ret = charlcd_register(lcd);
+	if (ret)
+		goto fail;
+
+	platform_set_drvdata(pdev, lcd);
+	return 0;
+
+fail:
+	kfree(lcd);
+	return ret;
+}
+
+static int hd44780_remove(struct platform_device *pdev)
+{
+	struct charlcd *lcd = platform_get_drvdata(pdev);
+
+	charlcd_unregister(lcd);
+	return 0;
+}
+
+static const struct of_device_id hd44780_of_match[] = {
+	{ .compatible = "hit,hd44780" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hd44780_of_match);
+
+static struct platform_driver hd44780_driver = {
+	.probe = hd44780_probe,
+	.remove = hd44780_remove,
+	.driver		= {
+		.name	= "hd44780",
+		.of_match_table = hd44780_of_match,
+	},
+};
+
+module_platform_driver(hd44780_driver);
+MODULE_DESCRIPTION("HD44780 Character LCD driver");
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (12 preceding siblings ...)
  2017-02-06 14:38 ` [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support Geert Uytterhoeven
@ 2017-02-06 15:09 ` Willy TARREAU
  2017-02-10 14:37 ` Greg Kroah-Hartman
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 40+ messages in thread
From: Willy TARREAU @ 2017-02-06 15:09 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

Hi Geert,

On Mon, Feb 06, 2017 at 03:38:02PM +0100, Geert Uytterhoeven wrote:
> 	Hi all,
> 
> Character LCDs are currently typically driven from userspace, either
> directly through bit-banging, or via some kind of serial (UART/i2c)
> interface.
> 
> This patch series adds kernel support for character LCDs using an
> Hitachi HD44780 Character LCD Controller where its M6800 bus interface
> is connected to GPIOs.  It does so after extracting the character LCD
> core support from the existing Parallel port LCD/Keypad Panel driver
> into its own subdriver.

That's excellent, I was dreaming about it a few days ago ;-)
Not reviewed yet but I like the idea!

Willy

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel
  2017-02-06 14:38 ` [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel Geert Uytterhoeven
@ 2017-02-06 15:09   ` Arnd Bergmann
  2017-02-06 15:18     ` Geert Uytterhoeven
  2017-02-08 12:22   ` Geert Uytterhoeven
  1 sibling, 1 reply; 40+ messages in thread
From: Arnd Bergmann @ 2017-02-06 15:09 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Linux Kernel Mailing List

On Mon, Feb 6, 2017 at 3:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> Extract the character LCD core from the Parallel port LCD/Keypad Panel
> driver in the misc subsystem, and convert it into a subdriver in the
> auxdisplay subsystem.  This allows the character LCD core to be used by
> other drivers later.
>
> Compilation is controlled by its own Kconfig symbol CHARLCD, which is to
> be selected by its users, but can be enabled manually for
> compile-testing.
>
> All functions changed their prefix from "lcd_" to "charlcd_", and gained
> a "struct charlcd *" parameter to operate on a specific instance.
> While the driver API thus is ready to support multiple instances, the
> current limitation of a single display (/dev/lcd has a single misc minor
> assigned) is retained.
>
> No functional changes intended.
>
> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
> ---
>  drivers/auxdisplay/Kconfig   |   3 +
>  drivers/auxdisplay/Makefile  |   1 +
>  drivers/auxdisplay/charlcd.c | 790 +++++++++++++++++++++++++++++++++++++++++
>  drivers/misc/Kconfig         |   1 +
>  drivers/misc/panel.c         | 827 +++++--------------------------------------
>  include/misc/charlcd.h       |  40 +++
>  6 files changed, 927 insertions(+), 735 deletions(-)
>  create mode 100644 drivers/auxdisplay/charlcd.c
>  create mode 100644 include/misc/charlcd.h

Looks good overall, and I like the entire series, but I wonder if we should move
the panel.c file into drivers/auxdisplay/ as well. If I understand it
right, what's
left now is a parport driver that hooks into the charlcd framework. I see that
we already have another parport driver in drivers/auxdisplay/ks0108.c, so why
not move both halves of the misc/panel.c driver into modules in
drivers/auxdisplay?

    Arnd

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel
  2017-02-06 15:09   ` Arnd Bergmann
@ 2017-02-06 15:18     ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-06 15:18 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Linux Kernel Mailing List

Hi Arnd,

On Mon, Feb 6, 2017 at 4:09 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Mon, Feb 6, 2017 at 3:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>> Extract the character LCD core from the Parallel port LCD/Keypad Panel
>> driver in the misc subsystem, and convert it into a subdriver in the
>> auxdisplay subsystem.  This allows the character LCD core to be used by
>> other drivers later.
>>
>> Compilation is controlled by its own Kconfig symbol CHARLCD, which is to
>> be selected by its users, but can be enabled manually for
>> compile-testing.
>>
>> All functions changed their prefix from "lcd_" to "charlcd_", and gained
>> a "struct charlcd *" parameter to operate on a specific instance.
>> While the driver API thus is ready to support multiple instances, the
>> current limitation of a single display (/dev/lcd has a single misc minor
>> assigned) is retained.
>>
>> No functional changes intended.
>>
>> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
>> ---
>>  drivers/auxdisplay/Kconfig   |   3 +
>>  drivers/auxdisplay/Makefile  |   1 +
>>  drivers/auxdisplay/charlcd.c | 790 +++++++++++++++++++++++++++++++++++++++++
>>  drivers/misc/Kconfig         |   1 +
>>  drivers/misc/panel.c         | 827 +++++--------------------------------------
>>  include/misc/charlcd.h       |  40 +++
>>  6 files changed, 927 insertions(+), 735 deletions(-)
>>  create mode 100644 drivers/auxdisplay/charlcd.c
>>  create mode 100644 include/misc/charlcd.h
>
> Looks good overall, and I like the entire series, but I wonder if we should move
> the panel.c file into drivers/auxdisplay/ as well. If I understand it
> right, what's
> left now is a parport driver that hooks into the charlcd framework. I see that
> we already have another parport driver in drivers/auxdisplay/ks0108.c, so why
> not move both halves of the misc/panel.c driver into modules in
> drivers/auxdisplay?

What's left in panel.c is:
  1. a parport driver that hooks into the charlcd framework,
  2. a parport driver that does keypad handling.

I didn't move all of it because of 2.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
  2017-02-06 14:38 ` [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support Geert Uytterhoeven
@ 2017-02-07 11:23   ` Andy Shevchenko
  2017-02-08 13:18       ` Geert Uytterhoeven
  0 siblings, 1 reply; 40+ messages in thread
From: Andy Shevchenko @ 2017-02-07 11:23 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

On Mon, Feb 6, 2017 at 4:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> The Hitachi HD44780 Character LCD Controller is commonly used on
> character LCDs that can display one or more lines of text.
>
> This driver supports character LCDs connected to GPIOs, using either a
> 4-bit or 8-bit data bus, and provides access through the charlcd core
> and /dev/lcd.

Can we not limit this to OF? I can easily imagine use case with
x86/ACPI enabled platform.

On the first glance of_property-* -> device_property_* is enough.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel
  2017-02-06 14:38 ` [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel Geert Uytterhoeven
  2017-02-06 15:09   ` Arnd Bergmann
@ 2017-02-08 12:22   ` Geert Uytterhoeven
  1 sibling, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 12:22 UTC (permalink / raw)
  To: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann
  Cc: linux-kernel, Geert Uytterhoeven

On Mon, Feb 6, 2017 at 3:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> Extract the character LCD core from the Parallel port LCD/Keypad Panel
> driver in the misc subsystem, and convert it into a subdriver in the
> auxdisplay subsystem.  This allows the character LCD core to be used by
> other drivers later.
>
> Compilation is controlled by its own Kconfig symbol CHARLCD, which is to
> be selected by its users, but can be enabled manually for
> compile-testing.
>
> All functions changed their prefix from "lcd_" to "charlcd_", and gained
> a "struct charlcd *" parameter to operate on a specific instance.
> While the driver API thus is ready to support multiple instances, the
> current limitation of a single display (/dev/lcd has a single misc minor
> assigned) is retained.
>
> No functional changes intended.
>
> Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>

> --- /dev/null
> +++ b/drivers/auxdisplay/charlcd.c

> +/* initialize the LCD driver */
> +static int charlcd_init(struct charlcd *lcd)
> +{
> +       struct charlcd_priv *priv = to_priv(lcd);
> +       int ret;
> +
> +       /*
> +        * before this line, we must NOT send anything to the display.
> +        * Since charlcd_init_display() needs to write data, we have to
> +        * enable mark the LCD initialized just before.
> +        */
> +       ret = charlcd_init_display(lcd);

0day reported:

| INFO: trying to register non-static key.
| the code is fine but needs lockdep annotation.
| turning off the locking correctness validator.

However, the code is not fine.
charlcd_init_display() calls charlcd_backlight(), which acquires the
mutex that is only initialized below.

> +       if (ret)
> +               return ret;
> +
> +       if (lcd->ops->backlight) {
> +               mutex_init(&priv->bl_tempo_lock);
> +               INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
> +       }

Moving the block above before the call to charlcd_init_display() fixes this.
Will do so in v2.

Note that "misc: panel: Abstract temporary backlight handling" doesn't
suffer from this bug.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 13:18       ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 13:18 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

Hi Andy,

On Tue, Feb 7, 2017 at 12:23 PM, Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> On Mon, Feb 6, 2017 at 4:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>> The Hitachi HD44780 Character LCD Controller is commonly used on
>> character LCDs that can display one or more lines of text.
>>
>> This driver supports character LCDs connected to GPIOs, using either a
>> 4-bit or 8-bit data bus, and provides access through the charlcd core
>> and /dev/lcd.
>
> Can we not limit this to OF? I can easily imagine use case with
> x86/ACPI enabled platform.

Sure!

> On the first glance of_property-* -> device_property_* is enough.

And of_gpio_named_count() -> gpiod_count().

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 13:18       ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 13:18 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
	devicetree

Hi Andy,

On Tue, Feb 7, 2017 at 12:23 PM, Andy Shevchenko
<andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Mon, Feb 6, 2017 at 4:38 PM, Geert Uytterhoeven <geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org> wrote:
>> The Hitachi HD44780 Character LCD Controller is commonly used on
>> character LCDs that can display one or more lines of text.
>>
>> This driver supports character LCDs connected to GPIOs, using either a
>> 4-bit or 8-bit data bus, and provides access through the charlcd core
>> and /dev/lcd.
>
> Can we not limit this to OF? I can easily imagine use case with
> x86/ACPI enabled platform.

Sure!

> On the first glance of_property-* -> device_property_* is enough.

And of_gpio_named_count() -> gpiod_count().

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
  2017-02-08 13:18       ` Geert Uytterhoeven
@ 2017-02-08 13:38         ` Andy Shevchenko
  -1 siblings, 0 replies; 40+ messages in thread
From: Andy Shevchenko @ 2017-02-08 13:38 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

On Wed, Feb 8, 2017 at 3:18 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> Hi Andy,
>
> On Tue, Feb 7, 2017 at 12:23 PM, Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
>> On Mon, Feb 6, 2017 at 4:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>>> The Hitachi HD44780 Character LCD Controller is commonly used on
>>> character LCDs that can display one or more lines of text.
>>>
>>> This driver supports character LCDs connected to GPIOs, using either a
>>> 4-bit or 8-bit data bus, and provides access through the charlcd core
>>> and /dev/lcd.
>>
>> Can we not limit this to OF? I can easily imagine use case with
>> x86/ACPI enabled platform.
>
> Sure!
>
>> On the first glance of_property-* -> device_property_* is enough.
>
> And of_gpio_named_count() -> gpiod_count().

Indeed.

Btw, have you any plans to make an order among spreaded LCD drivers in
kernel? Perhaps we may gather them under auxdisplay hood?

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 13:38         ` Andy Shevchenko
  0 siblings, 0 replies; 40+ messages in thread
From: Andy Shevchenko @ 2017-02-08 13:38 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
	devicetree

On Wed, Feb 8, 2017 at 3:18 PM, Geert Uytterhoeven <geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org> wrote:
> Hi Andy,
>
> On Tue, Feb 7, 2017 at 12:23 PM, Andy Shevchenko
> <andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On Mon, Feb 6, 2017 at 4:38 PM, Geert Uytterhoeven <geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org> wrote:
>>> The Hitachi HD44780 Character LCD Controller is commonly used on
>>> character LCDs that can display one or more lines of text.
>>>
>>> This driver supports character LCDs connected to GPIOs, using either a
>>> 4-bit or 8-bit data bus, and provides access through the charlcd core
>>> and /dev/lcd.
>>
>> Can we not limit this to OF? I can easily imagine use case with
>> x86/ACPI enabled platform.
>
> Sure!
>
>> On the first glance of_property-* -> device_property_* is enough.
>
> And of_gpio_named_count() -> gpiod_count().

Indeed.

Btw, have you any plans to make an order among spreaded LCD drivers in
kernel? Perhaps we may gather them under auxdisplay hood?

-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
  2017-02-08 13:38         ` Andy Shevchenko
  (?)
@ 2017-02-08 13:41         ` Geert Uytterhoeven
  2017-02-08 16:27             ` Andy Shevchenko
  -1 siblings, 1 reply; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 13:41 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

Hi Andy,

On Wed, Feb 8, 2017 at 2:38 PM, Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> Btw, have you any plans to make an order among spreaded LCD drivers in
> kernel? Perhaps we may gather them under auxdisplay hood?

I have no such plan. I just had a long-standing personal interest in hd44780.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 16:27             ` Andy Shevchenko
  0 siblings, 0 replies; 40+ messages in thread
From: Andy Shevchenko @ 2017-02-08 16:27 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

On Wed, Feb 8, 2017 at 3:41 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
> On Wed, Feb 8, 2017 at 2:38 PM, Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
>> Btw, have you any plans to make an order among spreaded LCD drivers in
>> kernel? Perhaps we may gather them under auxdisplay hood?
>
> I have no such plan. I just had a long-standing personal interest in hd44780.

I have one too regarding to I2C/SPI interfaced hardware.
If you remember it was an attempt for lcd-linux to be upstreamed,
which provides (besides i2c/spi bus drivers) those LCDs as tty
devices.
Also interesting feature to have.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 16:27             ` Andy Shevchenko
  0 siblings, 0 replies; 40+ messages in thread
From: Andy Shevchenko @ 2017-02-08 16:27 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
	devicetree

On Wed, Feb 8, 2017 at 3:41 PM, Geert Uytterhoeven <geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org> wrote:
> On Wed, Feb 8, 2017 at 2:38 PM, Andy Shevchenko
> <andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> Btw, have you any plans to make an order among spreaded LCD drivers in
>> kernel? Perhaps we may gather them under auxdisplay hood?
>
> I have no such plan. I just had a long-standing personal interest in hd44780.

I have one too regarding to I2C/SPI interfaced hardware.
If you remember it was an attempt for lcd-linux to be upstreamed,
which provides (besides i2c/spi bus drivers) those LCDs as tty
devices.
Also interesting feature to have.

-- 
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 17:19               ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 17:19 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Rob Herring,
	Mark Rutland, devicetree

Hi Andy,

On Wed, Feb 8, 2017 at 5:27 PM, Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
> On Wed, Feb 8, 2017 at 3:41 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>> On Wed, Feb 8, 2017 at 2:38 PM, Andy Shevchenko
>> <andy.shevchenko@gmail.com> wrote:
>>> Btw, have you any plans to make an order among spreaded LCD drivers in
>>> kernel? Perhaps we may gather them under auxdisplay hood?
>>
>> I have no such plan. I just had a long-standing personal interest in hd44780.
>
> I have one too regarding to I2C/SPI interfaced hardware.
> If you remember it was an attempt for lcd-linux to be upstreamed,
> which provides (besides i2c/spi bus drivers) those LCDs as tty
> devices.
> Also interesting feature to have.

I'm actually driving mine from SPI, using a 74HCT595 shift register and
the 74x164 GPIO driver.  Should work with i2c and a PCF8574 GPIO
expander too, except that my SoC has 3.3V I/Os, while my display
needs 5V, which is taken care of by the HCT shift register.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support
@ 2017-02-08 17:19               ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-08 17:19 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland,
	devicetree

Hi Andy,

On Wed, Feb 8, 2017 at 5:27 PM, Andy Shevchenko
<andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Wed, Feb 8, 2017 at 3:41 PM, Geert Uytterhoeven <geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org> wrote:
>> On Wed, Feb 8, 2017 at 2:38 PM, Andy Shevchenko
>> <andy.shevchenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>> Btw, have you any plans to make an order among spreaded LCD drivers in
>>> kernel? Perhaps we may gather them under auxdisplay hood?
>>
>> I have no such plan. I just had a long-standing personal interest in hd44780.
>
> I have one too regarding to I2C/SPI interfaced hardware.
> If you remember it was an attempt for lcd-linux to be upstreamed,
> which provides (besides i2c/spi bus drivers) those LCDs as tty
> devices.
> Also interesting feature to have.

I'm actually driving mine from SPI, using a 74HCT595 shift register and
the 74x164 GPIO driver.  Should work with i2c and a PCF8574 GPIO
expander too, except that my SoC has 3.3V I/Os, while my display
needs 5V, which is taken care of by the HCT shift register.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
@ 2017-02-09  1:11     ` Rob Herring
  0 siblings, 0 replies; 40+ messages in thread
From: Rob Herring @ 2017-02-09  1:11 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Mark Rutland,
	devicetree

On Mon, Feb 06, 2017 at 03:38:14PM +0100, Geert Uytterhoeven wrote:
> Add DT bindings for an Hitachi HD44780 Character LCD Controller where
> its M6800 bus interface is connected to GPIOs.

Really, you should have a bit-bang M6800 bus driver and binding and then 
the binding for the LCD ctrl looks the same whether you are bit-banging 
it or not.

That's a bit more work though. Maybe you could split the binding like 
this, but keep a monolithic driver for now. The driver can just fetch 
the GPIOs from the parent (bus controller) node. And you'd have to work 
out probing the driver.

> Memory-mapped configurations are not yet supported.

I guess this would require faulting on some address and then bit-banging 
the access. That sounds fun...

Rob

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
@ 2017-02-09  1:11     ` Rob Herring
  0 siblings, 0 replies; 40+ messages in thread
From: Rob Herring @ 2017-02-09  1:11 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, Feb 06, 2017 at 03:38:14PM +0100, Geert Uytterhoeven wrote:
> Add DT bindings for an Hitachi HD44780 Character LCD Controller where
> its M6800 bus interface is connected to GPIOs.

Really, you should have a bit-bang M6800 bus driver and binding and then 
the binding for the LCD ctrl looks the same whether you are bit-banging 
it or not.

That's a bit more work though. Maybe you could split the binding like 
this, but keep a monolithic driver for now. The driver can just fetch 
the GPIOs from the parent (bus controller) node. And you'd have to work 
out probing the driver.

> Memory-mapped configurations are not yet supported.

I guess this would require faulting on some address and then bit-banging 
the access. That sounds fun...

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
  2017-02-09  1:11     ` Rob Herring
@ 2017-02-09  8:51       ` Geert Uytterhoeven
  -1 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-09  8:51 UTC (permalink / raw)
  To: Rob Herring
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel, Mark Rutland,
	devicetree

Hi Rob,

On Thu, Feb 9, 2017 at 2:11 AM, Rob Herring <robh@kernel.org> wrote:
> On Mon, Feb 06, 2017 at 03:38:14PM +0100, Geert Uytterhoeven wrote:
>> Add DT bindings for an Hitachi HD44780 Character LCD Controller where
>> its M6800 bus interface is connected to GPIOs.
>
> Really, you should have a bit-bang M6800 bus driver and binding and then
> the binding for the LCD ctrl looks the same whether you are bit-banging
> it or not.

I thought about that, but turned it down due to the required complexity
for the little gain. And I think it would complicate memory mapped
configurations (see below), which would have to use regmap instead of readb()
and friends.

> That's a bit more work though. Maybe you could split the binding like
> this, but keep a monolithic driver for now. The driver can just fetch
> the GPIOs from the parent (bus controller) node. And you'd have to work
> out probing the driver.

Ugh...

>> Memory-mapped configurations are not yet supported.
>
> I guess this would require faulting on some address and then bit-banging
> the access. That sounds fun...

I meant connecting it to the processor bus instead of GPIOs, so the HD44780
registers become memory mapped.
That can be handled fairly easily by allowing a "reg" property as an
alternative to the data/enable/rw-gpios.

The only complication is that the M6800 bus has a single strobe (enable)
signal, and requires the rw signal to be valid before, while e.g. Intel CPUs
have separate read and write strobes. Usually people handle that by wiring\
rw to an address line, and thus using separate addresses for read and write.

I didn't add support for memory-mapped configurations as I can't test it
(without more hardware hacking ;-)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780
@ 2017-02-09  8:51       ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-02-09  8:51 UTC (permalink / raw)
  To: Rob Herring
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Rob,

On Thu, Feb 9, 2017 at 2:11 AM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Mon, Feb 06, 2017 at 03:38:14PM +0100, Geert Uytterhoeven wrote:
>> Add DT bindings for an Hitachi HD44780 Character LCD Controller where
>> its M6800 bus interface is connected to GPIOs.
>
> Really, you should have a bit-bang M6800 bus driver and binding and then
> the binding for the LCD ctrl looks the same whether you are bit-banging
> it or not.

I thought about that, but turned it down due to the required complexity
for the little gain. And I think it would complicate memory mapped
configurations (see below), which would have to use regmap instead of readb()
and friends.

> That's a bit more work though. Maybe you could split the binding like
> this, but keep a monolithic driver for now. The driver can just fetch
> the GPIOs from the parent (bus controller) node. And you'd have to work
> out probing the driver.

Ugh...

>> Memory-mapped configurations are not yet supported.
>
> I guess this would require faulting on some address and then bit-banging
> the access. That sounds fun...

I meant connecting it to the processor bus instead of GPIOs, so the HD44780
registers become memory mapped.
That can be handled fairly easily by allowing a "reg" property as an
alternative to the data/enable/rw-gpios.

The only complication is that the M6800 bus has a single strobe (enable)
signal, and requires the rw signal to be valid before, while e.g. Intel CPUs
have separate read and write strobes. Usually people handle that by wiring\
rw to an address line, and thus using separate addresses for read and write.

I didn't add support for memory-mapped configurations as I can't test it
(without more hardware hacking ;-)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (13 preceding siblings ...)
  2017-02-06 15:09 ` [PATCH 00/13] " Willy TARREAU
@ 2017-02-10 14:37 ` Greg Kroah-Hartman
  2017-03-16 20:50 ` Linus Walleij
  2017-03-23 17:15 ` Willy TARREAU
  16 siblings, 0 replies; 40+ messages in thread
From: Greg Kroah-Hartman @ 2017-02-10 14:37 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Willy Tarreau, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

On Mon, Feb 06, 2017 at 03:38:02PM +0100, Geert Uytterhoeven wrote:
> 	Hi all,
> 
> Character LCDs are currently typically driven from userspace, either
> directly through bit-banging, or via some kind of serial (UART/i2c)
> interface.
> 
> This patch series adds kernel support for character LCDs using an
> Hitachi HD44780 Character LCD Controller where its M6800 bus interface
> is connected to GPIOs.  It does so after extracting the character LCD
> core support from the existing Parallel port LCD/Keypad Panel driver
> into its own subdriver.
> 
>   - Patch 1 fixes a bug in the existing panel driver,
>   - Patches 2-8 perform clean ups and improvements to the panel driver,

I've applied the series up to here, I'm guessing the rest will take some
reworks based on review comments.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (14 preceding siblings ...)
  2017-02-10 14:37 ` Greg Kroah-Hartman
@ 2017-03-16 20:50 ` Linus Walleij
  2017-03-17  7:40   ` Geert Uytterhoeven
  2017-03-23 17:15 ` Willy TARREAU
  16 siblings, 1 reply; 40+ messages in thread
From: Linus Walleij @ 2017-03-16 20:50 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel

On Mon, Feb 6, 2017 at 3:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:

> Note that Linux has other support for HD44780 Character LCDs, but none of
> it is generic:
>   - drivers/misc/arm-charlcd.c involves a custom IP block for interfacing
>     to the LCD controller,

I can move this over to the generic framework if I get some example
of what needs to be done. At the very least the file should be moved
over to auxdisplay.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-03-16 20:50 ` Linus Walleij
@ 2017-03-17  7:40   ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-03-17  7:40 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Willy Tarreau,
	Ksenija Stanojevic, Arnd Bergmann, linux-kernel

Hi Linus,

On Thu, Mar 16, 2017 at 9:50 PM, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Mon, Feb 6, 2017 at 3:38 PM, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>
>> Note that Linux has other support for HD44780 Character LCDs, but none of
>> it is generic:
>>   - drivers/misc/arm-charlcd.c involves a custom IP block for interfacing
>>     to the LCD controller,
>
> I can move this over to the generic framework if I get some example
> of what needs to be done. At the very least the file should be moved
> over to auxdisplay.

Should be as simple as setting up the ops:

        static const struct charlcd_ops arm_charlcd_ops = {
                .write_cmd      = charlcd_4bit_command,
               .write_cmd_raw4 = wrapper around writel(x,
lcd->virtbase + CHAR_COM),
               .write_data     = charlcd_4bit_char,
        };

and replacing the custom initialization with:

        lcd = charlcd_alloc(sizeof(size(struct arm_charlcd));
        ...
        lcd->ifwidth = 4;
        lcd->width = FIXME;
        lcd->height = 2;
        cd->ops = &arm_charlcd_ops;
        charlcd_register(lcd);

There's no suspend/resume support in the charlcd core yet, though.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
                   ` (15 preceding siblings ...)
  2017-03-16 20:50 ` Linus Walleij
@ 2017-03-23 17:15 ` Willy TARREAU
  2017-03-23 17:37   ` Willy TARREAU
  16 siblings, 1 reply; 40+ messages in thread
From: Willy TARREAU @ 2017-03-23 17:15 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

Hi Geert,

On Mon, Feb 06, 2017 at 03:38:02PM +0100, Geert Uytterhoeven wrote:
> 	Hi all,
> 
> Character LCDs are currently typically driven from userspace, either
> directly through bit-banging, or via some kind of serial (UART/i2c)
> interface.
> 
> This patch series adds kernel support for character LCDs using an
> Hitachi HD44780 Character LCD Controller where its M6800 bus interface
> is connected to GPIOs.  It does so after extracting the character LCD
> core support from the existing Parallel port LCD/Keypad Panel driver
> into its own subdriver.
> 
>   - Patch 1 fixes a bug in the existing panel driver,
>   - Patches 2-8 perform clean ups and improvements to the panel driver,
>   - Patch 9 extract the character LCD core support from the panel driver
>     into an auxdisplay charlcd subdriver,
>   - Patches 10-11 add new features to the character LCD core driver
>     (4-bit interface support and support for displays with more than 2
>     lines),
>   - Patch 12 adds DT bindings for describing HD44780 Character LCDs,
>   - Patch 13 adds a driver for HD44780 Character LCDs connected to
>     GPIOs, using the new character LCD core.
> 
> Note that Linux has other support for HD44780 Character LCDs, but none of
> it is generic:
>   - drivers/misc/arm-charlcd.c involves a custom IP block for interfacing
>     to the LCD controller,
>   - drivers/parisc/led.c is tightly coupled to LED handling on various HP
>     PA-RISC machines.
> 
> This has been tested using a 20x4 character LCD with backlight, using both
> 8-bit and 4-bit wiring to one or two 74HC595 shift registers connected to
> an SPI master controller.
> 
> Regression testing on original "panel" hardware would be appreciated.

So I finally had the time to give it a try by applying your patchset
on top of our 4.9 prod kernel. For now I'm getting a panic at boot :

BUG: unable to handle kernel NULL pointer dereference at           (null)
IP: [<ffffffff813e203d>] __mutex_lock_slowpath+0x6d/0x100
PGD 0 
Oops: 0002 [#1] SMP
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.9.0-alb90.0-alb3100-lcdtest #3
Hardware name: To Be Filled By Intel To Be Filled By Intel/To be filled by Intel, BIOS 080015  04/01/2011
task: ffff88007c488000 task.stack: ffff88007c490000
RIP: 0010:[<ffffffff813e203d>]  [<ffffffff813e203d>] __mutex_lock_slowpath+0x6d/0x100
RSP: 0000:ffff88007c493cc8  EFLAGS: 00010282
RAX: 0000000000000000 RBX: ffff88007c5a0f90 RCX: ffff88007c48d700
RDX: ffff88007c493cd0 RSI: 0000000000000000 RDI: ffff88007c5a0f94
RBP: ffff88007c488000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000002ea48c3 R12: ffff88007c5a0f94
R13: 00000000ffffffff R14: ffff88007c5a0f98 R15: ffff88007c470000
FS:  0000000000000000(0000) GS:ffff88007ee00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 0000000001607000 CR4: 00000000000006f0
Stack:
 ffff88007ee0ea80 ffff88007c5a0f98 0000000000000000 0000000000000282
 ffff88007c5a0f90 ffff88007c5a0f90 0000000000000000 ffff88007c493f28
 ffff88007c493f28 ffffffff813e20de ffff88007c5a0f00 ffffffff812a9cc3
Call Trace:
 [<ffffffff813e20de>] ? mutex_lock+0xe/0x20
 [<ffffffff812a9cc3>] ? charlcd_backlight+0x33/0x80
 [<ffffffff812696c0>] ? lcd_write_data_p8+0x80/0x80
 [<ffffffff812a9fa5>] ? charlcd_init_display+0x135/0x1a0
 [<ffffffff812aaa6f>] ? charlcd_register+0xf/0xf0
 [<ffffffff81269e48>] ? panel_attach+0x4c8/0x9c0
 [<ffffffff8124e6b0>] ? attach_driver_chain+0x60/0x60
 [<ffffffff8124e6cb>] ? port_check+0x1b/0x30
 [<ffffffff812565af>] ? bus_for_each_dev+0x4f/0x80
 [<ffffffff8124e842>] ? __parport_register_driver+0x112/0x150
 [<ffffffff8169744a>] ? loop_init+0x11d/0x13c
 [<ffffffff81697469>] ? loop_init+0x13c/0x13c
 [<ffffffff81697622>] ? panel_init_module+0x1b9/0x20a
 [<ffffffff81000412>] ? do_one_initcall+0x42/0x160
 [<ffffffff81675124>] ? kernel_init_freeable+0x10e/0x1d8
 [<ffffffff8167483d>] ? loglevel+0x36/0x36
 [<ffffffff813ded10>] ? rest_init+0x70/0x70
 [<ffffffff813ded1a>] ? kernel_init+0xa/0x100
 [<ffffffff813ded10>] ? rest_init+0x70/0x70
 [<ffffffff813e4212>] ? ret_from_fork+0x22/0x30
Code: 03 83 f8 01 0f 84 9d 00 00 00 48 8b 43 10 48 8d 54 24 08 4c 8d 73 08 48 89 53 10 4c 89 74 24 08 48 89 44 24 10 41 bd ff ff ff ff <48> 89 10 48 89 6c 24 18 eb 1a 48 c7 45 08 02 00 00 00 c6 43 04 
RIP  [<ffffffff813e203d>] __mutex_lock_slowpath+0x6d/0x100
 RSP <ffff88007c493cc8>
CR2: 0000000000000000
---[ end trace e58465f0f36b5a27 ]---
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000009

I suspect that's related to the backlight timer though I'm not sure.
In case that helps I'm using the following config :

$ grep 'PANEL\|LCD' .config 
CONFIG_PANEL=y
CONFIG_PANEL_PARPORT=0
CONFIG_PANEL_PROFILE=4
CONFIG_PANEL_CHANGE_MESSAGE=y
CONFIG_PANEL_BOOT_MESSAGE="ALOHA ALB3100"
# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
# CONFIG_HID_PICOLCD is not set
# CONFIG_USB_LCD is not set
CONFIG_CHARLCD=y

I'm going to comment out the backlight code to see if it improves
the situation.

Cheers,
Willy

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-03-23 17:15 ` Willy TARREAU
@ 2017-03-23 17:37   ` Willy TARREAU
  2017-03-23 18:49     ` Geert Uytterhoeven
  0 siblings, 1 reply; 40+ messages in thread
From: Willy TARREAU @ 2017-03-23 17:37 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

Hi again,

On Thu, Mar 23, 2017 at 06:15:01PM +0100, Willy TARREAU wrote:
> So I finally had the time to give it a try by applying your patchset
> on top of our 4.9 prod kernel. For now I'm getting a panic at boot :
> 
> BUG: unable to handle kernel NULL pointer dereference at           (null)
> IP: [<ffffffff813e203d>] __mutex_lock_slowpath+0x6d/0x100
(...)

It was indeed related to the backlight mutex. It's initialized
after charlcd_init_display() succeeds, but the backlight is
touched within this function. I just moved it upwards and that
solved the issue. Feel free to re-integrate this (or an equivalent)
into your patch.

With only this fix, everything works fine here without even
having to touch the configuration, good job!

Thanks!
Willy
--

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 6aea900..e7f1bfe 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -703,6 +703,8 @@ static int charlcd_init(struct charlcd *lcd)
 	struct charlcd_priv *priv = to_priv(lcd);
 	int ret;
 
+	mutex_init(&priv->bl_tempo_lock);
+
 	/*
 	 * before this line, we must NOT send anything to the display.
 	 * Since charlcd_init_display() needs to write data, we have to
@@ -713,7 +715,6 @@ static int charlcd_init(struct charlcd *lcd)
 		return ret;
 
 	if (lcd->ops->backlight) {
-		mutex_init(&priv->bl_tempo_lock);
 		INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
 	}
 

^ permalink raw reply related	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-03-23 17:37   ` Willy TARREAU
@ 2017-03-23 18:49     ` Geert Uytterhoeven
  2017-03-23 19:03       ` Willy TARREAU
  0 siblings, 1 reply; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-03-23 18:49 UTC (permalink / raw)
  To: Willy TARREAU
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

Hi Willy,

On Thu, Mar 23, 2017 at 6:37 PM, Willy TARREAU <wtarreau@haproxy.com> wrote:
> On Thu, Mar 23, 2017 at 06:15:01PM +0100, Willy TARREAU wrote:
>> So I finally had the time to give it a try by applying your patchset
>> on top of our 4.9 prod kernel. For now I'm getting a panic at boot :
>>
>> BUG: unable to handle kernel NULL pointer dereference at           (null)
>> IP: [<ffffffff813e203d>] __mutex_lock_slowpath+0x6d/0x100
> (...)
>
> It was indeed related to the backlight mutex. It's initialized
> after charlcd_init_display() succeeds, but the backlight is
> touched within this function. I just moved it upwards and that
> solved the issue. Feel free to re-integrate this (or an equivalent)
> into your patch.
>
> With only this fix, everything works fine here without even
> having to touch the configuration, good job!

Thanks for testing!

>
> Thanks!
> Willy
> --
>
> diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
> index 6aea900..e7f1bfe 100644
> --- a/drivers/auxdisplay/charlcd.c
> +++ b/drivers/auxdisplay/charlcd.c
> @@ -703,6 +703,8 @@ static int charlcd_init(struct charlcd *lcd)
>         struct charlcd_priv *priv = to_priv(lcd);
>         int ret;
>
> +       mutex_init(&priv->bl_tempo_lock);
> +
>         /*
>          * before this line, we must NOT send anything to the display.
>          * Since charlcd_init_display() needs to write data, we have to
> @@ -713,7 +715,6 @@ static int charlcd_init(struct charlcd *lcd)
>                 return ret;
>
>         if (lcd->ops->backlight) {
> -               mutex_init(&priv->bl_tempo_lock);
>                 INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
>         }

I take it you're using v1 of the patchset?

v2 had this change:

  - Move backlight mutex initialization before call to
    charlcd_init_display(), to prevent locking an uninitialized mutex
    (reported by 0day).

https://lkml.org/lkml/2017/3/10/1151

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-03-23 18:49     ` Geert Uytterhoeven
@ 2017-03-23 19:03       ` Willy TARREAU
  2017-03-23 20:29         ` Geert Uytterhoeven
  0 siblings, 1 reply; 40+ messages in thread
From: Willy TARREAU @ 2017-03-23 19:03 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

On Thu, Mar 23, 2017 at 07:49:53PM +0100, Geert Uytterhoeven wrote:
> I take it you're using v1 of the patchset?
> 
> v2 had this change:
> 
>   - Move backlight mutex initialization before call to
>     charlcd_init_display(), to prevent locking an uninitialized mutex
>     (reported by 0day).
> 
> https://lkml.org/lkml/2017/3/10/1151

Ah yes good point sorry about that. I was surprized that the patches I
applied were that old as I remembered reading about them not that long
ago :-) I have too much stuff in this mailbox...

Given the few differences, I don't think I need to re-test it. But
just let me know if you need another test.

Cheers,
Willy

^ permalink raw reply	[flat|nested] 40+ messages in thread

* Re: [PATCH 00/13] Add HD44780 Character LCD support
  2017-03-23 19:03       ` Willy TARREAU
@ 2017-03-23 20:29         ` Geert Uytterhoeven
  0 siblings, 0 replies; 40+ messages in thread
From: Geert Uytterhoeven @ 2017-03-23 20:29 UTC (permalink / raw)
  To: Willy TARREAU
  Cc: Miguel Ojeda Sandonis, Greg Kroah-Hartman, Ksenija Stanojevic,
	Arnd Bergmann, linux-kernel

Hi Willy,

On Thu, Mar 23, 2017 at 8:03 PM, Willy TARREAU <wtarreau@haproxy.com> wrote:
> On Thu, Mar 23, 2017 at 07:49:53PM +0100, Geert Uytterhoeven wrote:
>> I take it you're using v1 of the patchset?
>>
>> v2 had this change:
>>
>>   - Move backlight mutex initialization before call to
>>     charlcd_init_display(), to prevent locking an uninitialized mutex
>>     (reported by 0day).
>>
>> https://lkml.org/lkml/2017/3/10/1151
>
> Ah yes good point sorry about that. I was surprized that the patches I
> applied were that old as I remembered reading about them not that long
> ago :-) I have too much stuff in this mailbox...
>
> Given the few differences, I don't think I need to re-test it. But
> just let me know if you need another test.

I don't think there's a need to re-test.
I did manage to reproduce the original problem when 0day reported it,
and verified that it was fixed.

Thanks!

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 40+ messages in thread

end of thread, other threads:[~2017-03-23 20:29 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-06 14:38 [PATCH 00/13] Add HD44780 Character LCD support Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 01/13] misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 02/13] misc: panel: Remove PANEL_VERSION Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 03/13] misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 04/13] misc: panel: Remove reference to misc device support Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 05/13] misc: panel: Move all suboptions into a big if section Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 06/13] misc: panel: Remove always-true check from panel_detach() Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 07/13] misc: panel: Add lcd_home() helper Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 08/13] misc: panel: Abstract temporary backlight handling Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 09/13] auxdisplay: charlcd: Extract character LCD core from misc/panel Geert Uytterhoeven
2017-02-06 15:09   ` Arnd Bergmann
2017-02-06 15:18     ` Geert Uytterhoeven
2017-02-08 12:22   ` Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 10/13] auxdisplay: charlcd: Add support for 4-bit interfaces Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 11/13] auxdisplay: charlcd: Add support for displays with more than two lines Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 12/13] dt-bindings: auxdisplay: Add bindings for Hitachi HD44780 Geert Uytterhoeven
2017-02-09  1:11   ` Rob Herring
2017-02-09  1:11     ` Rob Herring
2017-02-09  8:51     ` Geert Uytterhoeven
2017-02-09  8:51       ` Geert Uytterhoeven
2017-02-06 14:38 ` [PATCH 13/13] auxdisplay: Add HD44780 Character LCD support Geert Uytterhoeven
2017-02-07 11:23   ` Andy Shevchenko
2017-02-08 13:18     ` Geert Uytterhoeven
2017-02-08 13:18       ` Geert Uytterhoeven
2017-02-08 13:38       ` Andy Shevchenko
2017-02-08 13:38         ` Andy Shevchenko
2017-02-08 13:41         ` Geert Uytterhoeven
2017-02-08 16:27           ` Andy Shevchenko
2017-02-08 16:27             ` Andy Shevchenko
2017-02-08 17:19             ` Geert Uytterhoeven
2017-02-08 17:19               ` Geert Uytterhoeven
2017-02-06 15:09 ` [PATCH 00/13] " Willy TARREAU
2017-02-10 14:37 ` Greg Kroah-Hartman
2017-03-16 20:50 ` Linus Walleij
2017-03-17  7:40   ` Geert Uytterhoeven
2017-03-23 17:15 ` Willy TARREAU
2017-03-23 17:37   ` Willy TARREAU
2017-03-23 18:49     ` Geert Uytterhoeven
2017-03-23 19:03       ` Willy TARREAU
2017-03-23 20:29         ` Geert Uytterhoeven

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.