2019-10-16 12:51:32

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

charlcd.c contains lots of hd44780 hardware specific stuff. It is nearly
impossible to reuse the interface for other character based displays.
The current users of charlcd are the hd44780 and the panel drivers.
This does factor out the hd44780 specific stuff out of charlcd into a
new module called hd44780_common.
charlcd gets rid of the hd44780 specfics and more generally useable.
The hd44780 and panel drivers are modified to use the new
hd44780_common.
This is tested on a hd44780 connected through the gpios of a pcf8574.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/Kconfig | 16 +
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/charlcd.c | 591 ++++++++--------------------
drivers/auxdisplay/charlcd.h | 109 ++++-
drivers/auxdisplay/hd44780.c | 121 ++++--
drivers/auxdisplay/hd44780_common.c | 370 +++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 32 ++
drivers/auxdisplay/panel.c | 178 ++++-----
8 files changed, 851 insertions(+), 567 deletions(-)
create mode 100644 drivers/auxdisplay/hd44780_common.c
create mode 100644 drivers/auxdisplay/hd44780_common.h

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index b8313a04422d..5fb6784c6579 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -14,12 +14,27 @@ 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
+ help
+ This is the base system for character based lcd displays.
+ It makes no sense to have this alone, you select your display driver
+ and if it needs the charlcd core, it has to select it automatically.
+
+config HD44780_COMMON
+ tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST
+ help
+ This is a module with the common symbols for HD44780 (and compatibles)
+ displays. This is code that is shared between other modules. It is not
+ useful alone.
+
if AUXDISPLAY

config HD44780
tristate "HD44780 Character LCD support"
depends on GPIOLIB || COMPILE_TEST
select CHARLCD
+ select HD44780_COMMON
---help---
Enable support for Character LCDs using a HD44780 controller.
The LCD is accessible through the /dev/lcd char device (10, 156).
@@ -168,6 +183,7 @@ menuconfig PARPORT_PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
select CHARLCD
+ select HD44780_COMMON
---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/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index cf54b5efb07e..7e8a8c3eb3c3 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -4,6 +4,7 @@
#

obj-$(CONFIG_CHARLCD) += charlcd.o
+obj-$(CONFIG_HD44780_COMMON) += hd44780_common.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index bef6b85778b6..86c38b978c8b 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -24,267 +24,85 @@

#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 charlcd_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)
+static void charlcd_bl_off(struct work_struct *work)
{
- schedule_timeout_interruptible(msecs_to_jiffies(ms));
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct charlcd *lcd =
+ container_of(dwork, struct charlcd, bl_work);
+
+ mutex_lock(&lcd->bl_tempo_lock);
+ if (lcd->bl_tempo) {
+ lcd->bl_tempo = false;
+ if (!(lcd->flags & LCD_FLAG_L))
+ lcd->ops->backlight(lcd, CHARLCD_OFF);
+ }
+ mutex_unlock(&lcd->bl_tempo_lock);
}

/* turn the backlight on or off */
-static void charlcd_backlight(struct charlcd *lcd, int on)
+int charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ int ret = 0;

if (!lcd->ops->backlight)
- return;
+ return ret;

- mutex_lock(&priv->bl_tempo_lock);
- if (!priv->bl_tempo)
- lcd->ops->backlight(lcd, on);
- mutex_unlock(&priv->bl_tempo_lock);
-}
+ mutex_lock(&lcd->bl_tempo_lock);
+ if (!lcd->bl_tempo)
+ ret = lcd->ops->backlight(lcd, on);

-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);
+ mutex_unlock(&lcd->bl_tempo_lock);
+ return ret;
}
+EXPORT_SYMBOL_GPL(charlcd_backlight);

/* turn the backlight on for a little while */
-void charlcd_poke(struct charlcd *lcd)
+int charlcd_poke(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ int ret = 0;

if (!lcd->ops->backlight)
- return;
-
- cancel_delayed_work_sync(&priv->bl_work);
+ return ret;

- 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);
+ cancel_delayed_work_sync(&lcd->bl_work);

-static void charlcd_gotoxy(struct charlcd *lcd)
-{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
- unsigned int addr;
+ mutex_lock(&lcd->bl_tempo_lock);
+ if (!lcd->bl_tempo && !(lcd->flags & LCD_FLAG_L))
+ ret = lcd->ops->backlight(lcd, CHARLCD_ON);

- /*
- * 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)
-{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
-
- priv->addr.x = 0;
- priv->addr.y = 0;
- charlcd_gotoxy(lcd);
+ lcd->bl_tempo = true;
+ schedule_delayed_work(&lcd->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
+ mutex_unlock(&lcd->bl_tempo_lock);
+ return ret;
}
+EXPORT_SYMBOL_GPL(charlcd_poke);

static void charlcd_print(struct charlcd *lcd, char c)
{
- struct charlcd_priv *priv = charlcd_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 < min(2, 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 = charlcd_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)
-{
- void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
- u8 init;
-
- if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
- return -EINVAL;
+ if (lcd->addr.x >= lcd->width)
+ return;

- priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
- LCD_FLAG_C | LCD_FLAG_B;
+ if (lcd->char_conv)
+ c = lcd->char_conv[(unsigned char)c];

- long_sleep(20); /* wait 20 ms after power-up for the paranoid */
+ if (!lcd->ops->print(lcd, c))
+ lcd->addr.x++;

- /*
- * 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;
+ /* prevents the cursor from wrapping onto the next line */
+ if (lcd->addr.x >= lcd->width) {
+ lcd->addr.x = lcd->width;
+ lcd->addr.x--;
+ lcd->ops->gotoxy(lcd);
+ lcd->addr.x++;
}
- write_cmd_raw(lcd, init);
- long_sleep(10);
- write_cmd_raw(lcd, init);
- long_sleep(10);
- 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->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);
-
- /* 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;
}

/*
@@ -369,47 +187,69 @@ static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)

static inline int handle_lcd_special_code(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
-
/* LCD special codes */

int processed = 0;

- char *esc = priv->esc_seq.buf + 2;
- int oldflags = priv->flags;
+ char *esc = lcd->esc_seq.buf + 2;
+ int oldflags = lcd->flags;

/* check for display mode flags */
switch (*esc) {
case 'D': /* Display ON */
- priv->flags |= LCD_FLAG_D;
+ lcd->flags |= LCD_FLAG_D;
+ if (lcd->flags != oldflags)
+ lcd->ops->display(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'd': /* Display OFF */
- priv->flags &= ~LCD_FLAG_D;
+ lcd->flags &= ~LCD_FLAG_D;
+ if (lcd->flags != oldflags)
+ lcd->ops->display(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case 'C': /* Cursor ON */
- priv->flags |= LCD_FLAG_C;
+ lcd->flags |= LCD_FLAG_C;
+ if (lcd->flags != oldflags)
+ lcd->ops->cursor(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'c': /* Cursor OFF */
- priv->flags &= ~LCD_FLAG_C;
+ lcd->flags &= ~LCD_FLAG_C;
+ if (lcd->flags != oldflags)
+ lcd->ops->cursor(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case 'B': /* Blink ON */
- priv->flags |= LCD_FLAG_B;
+ lcd->flags |= LCD_FLAG_B;
+ if (lcd->flags != oldflags)
+ lcd->ops->blink(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'b': /* Blink OFF */
- priv->flags &= ~LCD_FLAG_B;
+ lcd->flags &= ~LCD_FLAG_B;
+ if (lcd->flags != oldflags)
+ lcd->ops->blink(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case '+': /* Back light ON */
- priv->flags |= LCD_FLAG_L;
+ lcd->flags |= LCD_FLAG_L;
+ if (lcd->flags != oldflags)
+ lcd->ops->backlight(lcd, CHARLCD_ON);
+
processed = 1;
break;
case '-': /* Back light OFF */
- priv->flags &= ~LCD_FLAG_L;
+ lcd->flags &= ~LCD_FLAG_L;
+ if (lcd->flags != oldflags)
+ lcd->ops->backlight(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case '*': /* Flash back light */
@@ -417,222 +257,144 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
processed = 1;
break;
case 'f': /* Small Font */
- priv->flags &= ~LCD_FLAG_F;
+ lcd->flags &= ~LCD_FLAG_F;
processed = 1;
break;
case 'F': /* Large Font */
- priv->flags |= LCD_FLAG_F;
+ lcd->flags |= LCD_FLAG_F;
processed = 1;
break;
case 'n': /* One Line */
- priv->flags &= ~LCD_FLAG_N;
+ lcd->flags &= ~LCD_FLAG_N;
processed = 1;
break;
case 'N': /* Two Lines */
- priv->flags |= LCD_FLAG_N;
+ lcd->flags |= LCD_FLAG_N;
processed = 1;
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--;
+ if (lcd->addr.x > 0) {
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
+ lcd->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++;
+ if (lcd->addr.x < lcd->width) {
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT))
+ lcd->addr.x++;
}
+
processed = 1;
break;
case 'L': /* shift display left */
- lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT);
processed = 1;
break;
case 'R': /* shift display right */
- lcd->ops->write_cmd(lcd,
- LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
- LCD_CMD_SHIFT_RIGHT);
+ lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT);
processed = 1;
break;
case 'k': { /* kill end of line */
- int x;
+ int x, xs, ys;

- for (x = priv->addr.x; x < lcd->bwidth; x++)
- lcd->ops->write_data(lcd, ' ');
+ xs = lcd->addr.x;
+ ys = lcd->addr.y;
+ for (x = lcd->addr.x; x < lcd->width; x++)
+ lcd->ops->print(lcd, ' ');

/* restore cursor position */
- charlcd_gotoxy(lcd);
+ lcd->addr.x = xs;
+ lcd->addr.y = ys;
+ lcd->ops->gotoxy(lcd);
processed = 1;
break;
}
case 'I': /* reinitialize display */
- charlcd_init_display(lcd);
+ lcd->ops->init_display(lcd);
+ lcd->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+ LCD_FLAG_C | LCD_FLAG_B;
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) {
+ if (lcd->ops->redefine_char)
+ processed = lcd->ops->redefine_char(lcd, esc);
+ else
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 <= 'F') {
- value |= (*esc - 'A' + 10) << shift;
- } else if (*esc >= 'a' && *esc <= 'f') {
- 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 (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
+ if (lcd->esc_seq.buf[lcd->esc_seq.len - 1] != ';')
break;

/* If the command is valid, move to the new address */
- if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
- charlcd_gotoxy(lcd);
+ if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
+ lcd->ops->gotoxy(lcd);

/* Regardless of its validity, mark as processed */
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->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 */
- 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 = charlcd_to_priv(lcd);
-
/* first, we'll test if we're in escape mode */
- if ((c != '\n') && priv->esc_seq.len >= 0) {
+ if ((c != '\n') && lcd->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';
+ lcd->esc_seq.buf[lcd->esc_seq.len++] = c;
+ lcd->esc_seq.buf[lcd->esc_seq.len] = '\0';
} else {
/* aborts any previous escape sequence */
- priv->esc_seq.len = -1;
+ lcd->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';
+ 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 (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--;
+ if (lcd->addr.x > 0) {
+ /* back one char */
+ if (!lcd->ops->shift_cursor(lcd,
+ CHARLCD_SHIFT_LEFT))
+ lcd->addr.x--;
}
/* replace with a space */
- lcd->ops->write_data(lcd, ' ');
+ charlcd_print(lcd, ' ');
/* back one char again */
- lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
+ lcd->addr.x--;
+
break;
case '\f':
/* quickly clear the display */
- charlcd_clear_fast(lcd);
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ lcd->ops->clear_display(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);
+ for (; lcd->addr.x < lcd->width; )
+ charlcd_print(lcd, ' ');
+
+ lcd->addr.x = 0;
+ lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
+ lcd->ops->gotoxy(lcd);
break;
case '\r':
/* go to the beginning of the same line */
- priv->addr.x = 0;
- charlcd_gotoxy(lcd);
+ lcd->addr.x = 0;
+ lcd->ops->gotoxy(lcd);
break;
case '\t':
/* print a space instead of the tab */
@@ -649,22 +411,26 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* 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) {
+ if (lcd->esc_seq.len >= 2) {
int processed = 0;

- if (!strcmp(priv->esc_seq.buf, "[2J")) {
+ if (!strcmp(lcd->esc_seq.buf, "[2J")) {
/* clear the display */
- charlcd_clear_fast(lcd);
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ lcd->ops->clear_display(lcd);
processed = 1;
- } else if (!strcmp(priv->esc_seq.buf, "[H")) {
+ } else if (!strcmp(lcd->esc_seq.buf, "[H")) {
/* cursor to home */
- charlcd_home(lcd);
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ lcd->ops->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')) {
+ else if ((lcd->esc_seq.len >= 3) &&
+ (lcd->esc_seq.buf[0] == '[') &&
+ (lcd->esc_seq.buf[1] == 'L')) {
processed = handle_lcd_special_code(lcd);
}

@@ -673,8 +439,8 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* 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;
+ if (processed || (lcd->esc_seq.len >= LCD_ESCAPE_LEN))
+ lcd->esc_seq.len = -1;
} /* escape codes */
}

@@ -705,7 +471,7 @@ static ssize_t charlcd_write(struct file *file, const char __user *buf,

static int charlcd_open(struct inode *inode, struct file *file)
{
- struct charlcd_priv *priv = charlcd_to_priv(the_charlcd);
+ struct charlcd *lcd = the_charlcd;
int ret;

ret = -EBUSY;
@@ -716,9 +482,11 @@ static int charlcd_open(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_READ) /* device is write-only */
goto fail;

- if (priv->must_clear) {
- charlcd_clear_display(&priv->lcd);
- priv->must_clear = false;
+ if (lcd->must_clear) {
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ lcd->ops->clear_display(lcd);
+ lcd->must_clear = false;
}
return nonseekable_open(inode, file);

@@ -752,15 +520,17 @@ static void charlcd_puts(struct charlcd *lcd, const char *s)
int count = strlen(s);

for (; count-- > 0; tmp++) {
- if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+ 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);
}
+
}

#ifdef CONFIG_PANEL_BOOT_MESSAGE
@@ -780,12 +550,13 @@ static void charlcd_puts(struct charlcd *lcd, const char *s)
/* initialize the LCD driver */
static int charlcd_init(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
int ret;

+ lcd->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+ LCD_FLAG_C | LCD_FLAG_B;
if (lcd->ops->backlight) {
- mutex_init(&priv->bl_tempo_lock);
- INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
+ mutex_init(&lcd->bl_tempo_lock);
+ INIT_DELAYED_WORK(&lcd->bl_work, charlcd_bl_off);
}

/*
@@ -793,7 +564,7 @@ static int charlcd_init(struct charlcd *lcd)
* Since charlcd_init_display() needs to write data, we have to
* enable mark the LCD initialized just before.
*/
- ret = charlcd_init_display(lcd);
+ ret = lcd->ops->init_display(lcd);
if (ret)
return ret;

@@ -801,38 +572,26 @@ static int charlcd_init(struct charlcd *lcd)
charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT);

/* clear the display on the next device opening */
- priv->must_clear = true;
- charlcd_home(lcd);
+ lcd->must_clear = true;
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ lcd->ops->home(lcd);
return 0;
}

-struct charlcd *charlcd_alloc(unsigned int drvdata_size)
+struct charlcd *charlcd_alloc(void)
{
- struct charlcd_priv *priv;
struct charlcd *lcd;

- priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
- if (!priv)
+ lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL);
+ if (!lcd)
return NULL;

- 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;
-
+ lcd->esc_seq.len = -1;
return lcd;
}
EXPORT_SYMBOL_GPL(charlcd_alloc);

-void charlcd_free(struct charlcd *lcd)
-{
- kfree(charlcd_to_priv(lcd));
-}
-EXPORT_SYMBOL_GPL(charlcd_free);
-
static int panel_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
@@ -881,15 +640,13 @@ EXPORT_SYMBOL_GPL(charlcd_register);

int charlcd_unregister(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_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);
+ cancel_delayed_work_sync(&lcd->bl_work);
+ lcd->ops->backlight(lcd, CHARLCD_OFF);
}

return 0;
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 00911ad0f3de..6a9215871ff4 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -9,36 +9,113 @@
#ifndef _CHARLCD_H
#define _CHARLCD_H

+#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 */
+
+#define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
+
+enum charlcd_onoff {
+ CHARLCD_OFF = 0,
+ CHARLCD_ON,
+};
+
+enum charlcd_shift_dir {
+ CHARLCD_SHIFT_LEFT,
+ CHARLCD_SHIFT_RIGHT,
+};
+
+enum charlcd_fontsize {
+ CHARLCD_FONTSIZE_SMALL,
+ CHARLCD_FONTSIZE_LARGE,
+};
+
+enum charlcd_lines {
+ CHARLCD_LINES_1,
+ CHARLCD_LINES_2,
+};
+
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() */
- int hwidth; /* Default set by charlcd_alloc() */

- void *drvdata; /* Set by charlcd_alloc() */
+ 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 flags;
+
+ /* Contains the LCD X and Y offset */
+ struct {
+ unsigned long x;
+ unsigned long y;
+ } addr;
+
+ /* Current escape sequence and it's length or -1 if outside */
+ struct {
+ char buf[LCD_ESCAPE_LEN + 1];
+ int len;
+ } esc_seq;
+
+ void *drvdata;
};

+/**
+ * struct charlcd_ops - Functions used by charlcd. Drivers have to implement
+ * these.
+ * @print: just Print one character to the display at current cursor position.
+ * The cursor is advanced by charlcd.
+ * @clear_fast: Clear the whole display and set cursor to position 0, 0.
+ * @backlight: Turn backlight on or off. Optional.
+ * @gotoxy: Set cursor to x, y. The x and y values to set the cursor to are
+ * previously set in addr.x and addr.y by charlcd.
+ * @home: Set cursor to 0, 0. The values in addr.x and addr.y are set to 0, 0 by
+ * charlcd prior to calling this function.
+ * @clear_display: Again clear the whole display, set the cursor to 0, 0. The
+ * values in addr.x and addr.y are set to 0, 0 by charlcd prior to calling this
+ * function.
+ * @init_display: Initialize the display.
+ * @shift_cursor: Shift cursor left or right one position.
+ * @shift_display: Shift whole display content left or right.
+ * @display: Turn display on or off.
+ * @cursor: Turn cursor on or off.
+ * @blink: Turn cursor blink on or off.
+ * @fontsize: Fontsize large or small.
+ * @lines: One or two lines.
+ * @redefine_char: Redefine the actual pixel matrix of character.
+ */
struct charlcd_ops {
- /* Required */
- void (*write_cmd)(struct charlcd *lcd, int cmd);
- 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);
+ int (*print)(struct charlcd *lcd, int c);
+ int (*clear_fast)(struct charlcd *lcd);
+ int (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*gotoxy)(struct charlcd *lcd);
+ int (*home)(struct charlcd *lcd);
+ int (*clear_display)(struct charlcd *lcd);
+ int (*init_display)(struct charlcd *lcd);
+ int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
+ int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
+ int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size);
+ int (*lines)(struct charlcd *lcd, enum charlcd_lines lines);
+ int (*redefine_char)(struct charlcd *lcd, char *esc);
};

-struct charlcd *charlcd_alloc(unsigned int drvdata_size);
+int charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
+struct charlcd *charlcd_alloc(void);
void charlcd_free(struct charlcd *lcd);
-
int charlcd_register(struct charlcd *lcd);
int charlcd_unregister(struct charlcd *lcd);
-
-void charlcd_poke(struct charlcd *lcd);
+int charlcd_poke(struct charlcd *lcd);

#endif /* CHARLCD_H */
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index bcbe13092327..60bec69f9c29 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>

#include "charlcd.h"
+#include "hd44780_common.h"

enum hd44780_pin {
/* Order does matter due to writing to GPIO array subsets! */
@@ -37,12 +38,15 @@ struct hd44780 {
struct gpio_desc *pins[PIN_NUM];
};

-static void hd44780_backlight(struct charlcd *lcd, int on)
+static int hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

if (hd->pins[PIN_CTRL_BL])
gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
+
+ return 0;
}

static void hd44780_strobe_gpio(struct hd44780 *hd)
@@ -101,9 +105,9 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
}

/* Send a command to the LCD panel in 8 bit GPIO mode */
-static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
+static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, cmd, 0);

@@ -112,9 +116,9 @@ static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
}

/* Send data to the LCD panel in 8 bit GPIO mode */
-static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
+static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, data, 1);

@@ -123,15 +127,25 @@ static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
}

static const struct charlcd_ops hd44780_ops_gpio8 = {
- .write_cmd = hd44780_write_cmd_gpio8,
- .write_data = hd44780_write_data_gpio8,
.backlight = hd44780_backlight,
+ .gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
-static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
+static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, cmd, 0);

@@ -140,10 +154,10 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
}

/* 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)
+static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd)
{
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;
unsigned int n;

/* Command nibble + RS, RW */
@@ -157,9 +171,9 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
}

/* Send data to the LCD panel in 4 bit GPIO mode */
-static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
+static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, data, 1);

@@ -168,10 +182,20 @@ static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
}

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,
+ .print = hd44780_common_print,
+ .gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static int hd44780_probe(struct platform_device *pdev)
@@ -179,8 +203,9 @@ static int hd44780_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
unsigned int i, base;
struct charlcd *lcd;
+ struct hd44780_common *hdc;
struct hd44780 *hd;
- int ifwidth, ret;
+ int ifwidth, ret = -ENOMEM;

/* Required pins */
ifwidth = gpiod_count(dev, "data");
@@ -198,31 +223,39 @@ static int hd44780_probe(struct platform_device *pdev)
return -EINVAL;
}

- lcd = charlcd_alloc(sizeof(struct hd44780));
- if (!lcd)
+ hdc = hd44780_common_alloc();
+ if (!hdc)
return -ENOMEM;

- hd = lcd->drvdata;
+ lcd = charlcd_alloc();
+ if (!lcd)
+ goto fail1;

+ hd = kzalloc(sizeof(struct hd44780), GFP_KERNEL);
+ if (!hd)
+ goto fail2;
+
+ hdc->hd44780 = hd;
+ lcd->drvdata = hdc;
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;
+ goto fail3;
}
}

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;
+ goto fail3;
}

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;
+ goto fail3;
}

/* Optional pins */
@@ -230,47 +263,60 @@ static int hd44780_probe(struct platform_device *pdev)
GPIOD_OUT_LOW);
if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
- goto fail;
+ goto fail3;
}

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;
+ goto fail3;
}

/* Required properties */
ret = device_property_read_u32(dev, "display-height-chars",
&lcd->height);
if (ret)
- goto fail;
+ goto fail3;
ret = device_property_read_u32(dev, "display-width-chars", &lcd->width);
if (ret)
- goto fail;
+ goto fail3;

/*
* 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;
+ hdc->bwidth = lcd->width;

/* Optional properties */
- device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
-
- lcd->ifwidth = ifwidth;
- lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
+ device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);
+
+ hdc->ifwidth = ifwidth;
+ if (ifwidth == 8) {
+ lcd->ops = &hd44780_ops_gpio8;
+ hdc->write_data = hd44780_write_data_gpio8;
+ hdc->write_cmd = hd44780_write_cmd_gpio8;
+ } else {
+ lcd->ops = &hd44780_ops_gpio4;
+ hdc->write_data = hd44780_write_data_gpio4;
+ hdc->write_cmd = hd44780_write_cmd_gpio4;
+ hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4;
+ }

ret = charlcd_register(lcd);
if (ret)
- goto fail;
+ goto fail3;

platform_set_drvdata(pdev, lcd);
return 0;

-fail:
- charlcd_free(lcd);
+fail3:
+ kfree(hd);
+fail2:
+ kfree(lcd);
+fail1:
+ kfree(hdc);
return ret;
}

@@ -278,9 +324,10 @@ static int hd44780_remove(struct platform_device *pdev)
{
struct charlcd *lcd = platform_get_drvdata(pdev);

+ kfree(lcd->drvdata);
charlcd_unregister(lcd);

- charlcd_free(lcd);
+ kfree(lcd);
return 0;
}

diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
new file mode 100644
index 000000000000..91b7365e1ca8
--- /dev/null
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "charlcd.h"
+#include "hd44780_common.h"
+
+/* 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 DEFAULT_LCD_BWIDTH 40
+#define DEFAULT_LCD_HWIDTH 64
+
+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+ schedule_timeout_interruptible(msecs_to_jiffies(ms));
+}
+
+int hd44780_common_print(struct charlcd *lcd, int c)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (lcd->addr.x < hdc->bwidth)
+ hdc->write_data(hdc, c);
+
+ return 0;
+}
+
+int hd44780_common_gotoxy(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+ unsigned int addr;
+
+ /*
+ * we force the cursor to stay at the end of the
+ * line if it wants to go farther
+ */
+ addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
+ : hdc->bwidth - 1;
+ if (lcd->addr.y & 1)
+ addr += hdc->hwidth;
+ if (lcd->addr.y & 2)
+ addr += hdc->bwidth;
+ hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);
+
+int hd44780_common_home(struct charlcd *lcd)
+{
+ return hd44780_common_gotoxy(lcd);
+}
+EXPORT_SYMBOL_GPL(hd44780_common_home);
+
+/* clears the display and resets X/Y */
+int hd44780_common_clear_display(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
+ /* we must wait a few milliseconds (15) */
+ long_sleep(15);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
+
+int hd44780_common_init_display(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
+ u8 init;
+
+ if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
+ return -EINVAL;
+
+ hdc->hd44780_common_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 */
+
+ /*
+ * 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 (hdc->ifwidth == 4) {
+ init >>= 4;
+ write_cmd_raw = hdc->write_cmd_raw4;
+ } else {
+ write_cmd_raw = hdc->write_cmd;
+ }
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+
+ if (hdc->ifwidth == 4) {
+ /* Switch to 4-bit mode, 1 line, small fonts */
+ hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
+ long_sleep(10);
+ }
+
+ /* set font height and lines number */
+ hdc->write_cmd(hdc,
+ LCD_CMD_FUNCTION_SET |
+ ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
+ LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
+ LCD_CMD_TWO_LINES : 0));
+ long_sleep(10);
+
+ /* display off, cursor off, blink off */
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
+ long_sleep(10);
+
+ hdc->write_cmd(hdc,
+ LCD_CMD_DISPLAY_CTRL | /* set display mode */
+ ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
+ LCD_CMD_DISPLAY_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
+ LCD_CMD_CURSOR_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
+ LCD_CMD_BLINK_ON : 0));
+
+ charlcd_backlight(lcd,
+ (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0);
+
+ long_sleep(10);
+
+ /* entry mode set : increment, cursor shifting */
+ hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+ hd44780_common_clear_display(lcd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_init_display);
+
+int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT) {
+ /* back one char if not at end of line */
+ if (lcd->addr.x < hdc->bwidth)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT);
+ } else if (dir == CHARLCD_SHIFT_RIGHT) {
+ /* allow the cursor to pass the end of the line */
+ if (lcd->addr.x < (hdc->bwidth - 1))
+ hdc->write_cmd(hdc,
+ LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor);
+
+int hd44780_common_shift_display(struct charlcd *lcd,
+ enum charlcd_shift_dir dir)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ else if (dir == CHARLCD_SHIFT_RIGHT)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+ LCD_CMD_SHIFT_RIGHT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_shift_display);
+
+static void hd44780_common_set_mode(struct hd44780_common *hdc)
+{
+ hdc->write_cmd(hdc,
+ LCD_CMD_DISPLAY_CTRL |
+ ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
+ LCD_CMD_DISPLAY_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
+ LCD_CMD_CURSOR_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
+ LCD_CMD_BLINK_ON : 0));
+}
+
+int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_D;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_D;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_display);
+
+int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_C;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_C;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_cursor);
+
+int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_B;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_B;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_blink);
+
+static void hd44780_common_set_function(struct hd44780_common *hdc)
+{
+ hdc->write_cmd(hdc,
+ LCD_CMD_FUNCTION_SET |
+ ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
+ LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
+ LCD_CMD_TWO_LINES : 0));
+}
+
+int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (size == CHARLCD_FONTSIZE_LARGE)
+ hdc->hd44780_common_flags |= LCD_FLAG_F;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_F;
+
+ hd44780_common_set_function(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_fontsize);
+
+int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (lines == CHARLCD_LINES_2)
+ hdc->hd44780_common_flags |= LCD_FLAG_N;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_N;
+
+ hd44780_common_set_function(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_lines);
+
+int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
+{
+ /* 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.
+ */
+
+ struct hd44780_common *hdc = lcd->drvdata;
+ unsigned char cgbytes[8];
+ unsigned char cgaddr;
+ int cgoffset;
+ int shift;
+ char value;
+ int addr;
+
+ if (!strchr(esc, ';'))
+ return 0;
+
+ esc++;
+
+ cgaddr = *(esc++) - '0';
+ if (cgaddr > 7)
+ return 1;
+
+ 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 <= 'F') {
+ value |= (*esc - 'A' + 10) << shift;
+ } else if (*esc >= 'a' && *esc <= 'f') {
+ value |= (*esc - 'a' + 10) << shift;
+ } else {
+ esc++;
+ continue;
+ }
+
+ if (shift == 0) {
+ cgbytes[cgoffset++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+ for (addr = 0; addr < cgoffset; addr++)
+ hdc->write_data(hdc, cgbytes[addr]);
+
+ /* ensures that we stop writing to CGRAM */
+ lcd->ops->gotoxy(lcd);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
+
+struct hd44780_common *hd44780_common_alloc(void)
+{
+ struct hd44780_common *hd;
+
+ hd = kzalloc(sizeof(*hd), GFP_KERNEL);
+ if (!hd)
+ return NULL;
+
+ hd->ifwidth = 8;
+ hd->bwidth = DEFAULT_LCD_BWIDTH;
+ hd->hwidth = DEFAULT_LCD_HWIDTH;
+ return hd;
+
+}
+EXPORT_SYMBOL_GPL(hd44780_common_alloc);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
new file mode 100644
index 000000000000..a713b468f44c
--- /dev/null
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+struct hd44780_common;
+struct hd44780_common {
+ int ifwidth; /* 4-bit or 8-bit (default) */
+ int bwidth; /* Default set by hd44780_alloc() */
+ int hwidth; /* Default set by hd44780_alloc() */
+ unsigned long hd44780_common_flags;
+ void (*write_data)(struct hd44780_common *hdc, int data);
+ void (*write_cmd)(struct hd44780_common *hdc, int cmd);
+ /* write_cmd_raw4 is for 4-bit connected displays only */
+ void (*write_cmd_raw4)(struct hd44780_common *hdc, int cmd);
+ void *hd44780;
+};
+
+int hd44780_common_print(struct charlcd *lcd, int c);
+int hd44780_common_gotoxy(struct charlcd *lcd);
+int hd44780_common_home(struct charlcd *lcd);
+int hd44780_common_clear_display(struct charlcd *lcd);
+int hd44780_common_init_display(struct charlcd *lcd);
+int hd44780_common_shift_cursor(struct charlcd *lcd,
+ enum charlcd_shift_dir dir);
+int hd44780_common_shift_display(struct charlcd *lcd,
+ enum charlcd_shift_dir dir);
+int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
+int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines);
+int hd44780_common_redefine_char(struct charlcd *lcd, char *esc);
+struct hd44780_common *hd44780_common_alloc(void);
+
diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 85965953683e..616e3f746a4a 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -56,6 +56,7 @@
#include <linux/uaccess.h>

#include "charlcd.h"
+#include "hd44780_common.h"

#define KEYPAD_MINOR 185

@@ -710,7 +711,7 @@ static void lcd_send_serial(int byte)
}

/* turn the backlight on or off */
-static void lcd_backlight(struct charlcd *charlcd, int on)
+static void lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on)
{
if (lcd.pins.bl == PIN_NONE)
return;
@@ -726,7 +727,7 @@ static void lcd_backlight(struct charlcd *charlcd, int on)
}

/* send a command to the LCD panel in serial mode */
-static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_s(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x1F); /* R/W=W, RS=0 */
@@ -737,7 +738,7 @@ static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
}

/* send data to the LCD panel in serial mode */
-static void lcd_write_data_s(struct charlcd *charlcd, int data)
+static void lcd_write_data_s(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
@@ -748,7 +749,7 @@ static void lcd_write_data_s(struct charlcd *charlcd, int data)
}

/* send a command to the LCD panel in 8 bits parallel mode */
-static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -770,7 +771,7 @@ static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
}

/* send data to the LCD panel in 8 bits parallel mode */
-static void lcd_write_data_p8(struct charlcd *charlcd, int data)
+static void lcd_write_data_p8(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -792,7 +793,7 @@ static void lcd_write_data_p8(struct charlcd *charlcd, int data)
}

/* send a command to the TI LCD panel */
-static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the control port */
@@ -802,7 +803,7 @@ static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
}

/* send data to the TI LCD panel */
-static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
+static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -811,105 +812,81 @@ static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
spin_unlock_irq(&pprt_lock);
}

-/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_s(struct charlcd *charlcd)
-{
- int pos;
-
- spin_lock_irq(&pprt_lock);
- 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);
- /* the shortest data takes at least 40 us */
- udelay(40);
- }
- spin_unlock_irq(&pprt_lock);
-}
-
-/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_p8(struct charlcd *charlcd)
-{
- int pos;
-
- spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
- /* present the data to the data port */
- w_dtr(pprt, ' ');
-
- /* maintain the data during 20 us before the strobe */
- udelay(20);
-
- set_bit(LCD_BIT_E, bits);
- set_bit(LCD_BIT_RS, bits);
- clear_bit(LCD_BIT_RW, bits);
- set_ctrl_bits();
-
- /* maintain the strobe during 40 us */
- udelay(40);
-
- clear_bit(LCD_BIT_E, bits);
- set_ctrl_bits();
-
- /* the shortest data takes at least 45 us */
- udelay(45);
- }
- spin_unlock_irq(&pprt_lock);
-}
-
-/* fills the display with spaces and resets X/Y */
-static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
-{
- int pos;
-
- spin_lock_irq(&pprt_lock);
- 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);
-}
-
static const 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,
+ .gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static const 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,
+ .gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static const 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,
+ .gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

/* initialize the LCD driver */
static void lcd_init(void)
{
struct charlcd *charlcd;
+ struct hd44780_common *hdc;

- charlcd = charlcd_alloc(0);
- if (!charlcd)
+ hdc = hd44780_common_alloc();
+ if (!hdc)
return;

+ charlcd = charlcd_alloc();
+ if (!charlcd) {
+ kfree(hdc);
+ return;
+ }
+
+ hdc->hd44780 = &lcd;
+ charlcd->drvdata = hdc;
+
/*
* Init lcd struct with load-time values to preserve exact
* current functionality (at least for now).
*/
charlcd->height = lcd_height;
charlcd->width = lcd_width;
- charlcd->bwidth = lcd_bwidth;
- charlcd->hwidth = lcd_hwidth;
+ hdc->bwidth = lcd_bwidth;
+ hdc->hwidth = lcd_hwidth;

switch (selected_lcd_type) {
case LCD_TYPE_OLD:
@@ -920,8 +897,8 @@ static void lcd_init(void)
lcd.pins.rs = PIN_AUTOLF;

charlcd->width = 40;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
case LCD_TYPE_KS0074:
@@ -933,8 +910,8 @@ static void lcd_init(void)
lcd.pins.da = PIN_D0;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 16;
+ hdc->bwidth = 40;
+ hdc->hwidth = 16;
charlcd->height = 2;
break;
case LCD_TYPE_NEXCOM:
@@ -946,8 +923,8 @@ static void lcd_init(void)
lcd.pins.rw = PIN_INITP;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
case LCD_TYPE_CUSTOM:
@@ -965,8 +942,8 @@ static void lcd_init(void)
lcd.pins.rs = PIN_SELECP;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
}
@@ -977,9 +954,9 @@ static void lcd_init(void)
if (lcd_width != NOT_SET)
charlcd->width = lcd_width;
if (lcd_bwidth != NOT_SET)
- charlcd->bwidth = lcd_bwidth;
+ hdc->bwidth = lcd_bwidth;
if (lcd_hwidth != NOT_SET)
- charlcd->hwidth = lcd_hwidth;
+ hdc->hwidth = lcd_hwidth;
if (lcd_charset != NOT_SET)
lcd.charset = lcd_charset;
if (lcd_proto != NOT_SET)
@@ -1000,15 +977,17 @@ static void lcd_init(void)
/* this is used to catch wrong and default values */
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 (hdc->bwidth <= 0)
+ hdc->bwidth = DEFAULT_LCD_BWIDTH;
+ if (hdc->hwidth <= 0)
+ hdc->hwidth = DEFAULT_LCD_HWIDTH;
if (charlcd->height <= 0)
charlcd->height = DEFAULT_LCD_HEIGHT;

if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
charlcd->ops = &charlcd_serial_ops;
+ hdc->write_data = lcd_write_cmd_s;
+ hdc->write_cmd = lcd_write_data_s;

if (lcd.pins.cl == PIN_NOT_SET)
lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1017,6 +996,8 @@ static void lcd_init(void)

} else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
charlcd->ops = &charlcd_parallel_ops;
+ hdc->write_data = lcd_write_cmd_p8;
+ hdc->write_cmd = lcd_write_data_p8;

if (lcd.pins.e == PIN_NOT_SET)
lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1026,6 +1007,8 @@ static void lcd_init(void)
lcd.pins.rw = DEFAULT_LCD_PIN_RW;
} else {
charlcd->ops = &charlcd_tilcd_ops;
+ hdc->write_data = lcd_write_cmd_tilcd;
+ hdc->write_cmd = lcd_write_data_tilcd;
}

if (lcd.pins.bl == PIN_NOT_SET)
@@ -1622,7 +1605,7 @@ static void panel_attach(struct parport *port)
if (lcd.enabled)
charlcd_unregister(lcd.charlcd);
err_unreg_device:
- charlcd_free(lcd.charlcd);
+ kfree(lcd.charlcd);
lcd.charlcd = NULL;
parport_unregister_device(pprt);
pprt = NULL;
@@ -1649,7 +1632,8 @@ static void panel_detach(struct parport *port)
if (lcd.enabled) {
charlcd_unregister(lcd.charlcd);
lcd.initialized = false;
- charlcd_free(lcd.charlcd);
+ kfree(lcd.charlcd->drvdata);
+ kfree(lcd.charlcd);
lcd.charlcd = NULL;
}

--
2.23.0


2019-10-16 12:53:10

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH 2/3] auxdisplay: lcd2s DT binding doc

Add a binding doc for the modtronix lcd2s auxdisplay driver. It also
adds modtronix to the list of known vendor-prefixes.

Signed-off-by: Lars Poeschel <[email protected]>
---
.../bindings/auxdisplay/modtronix,lcd2s.txt | 24 +++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 ++
2 files changed, 26 insertions(+)
create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.txt

diff --git a/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.txt b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.txt
new file mode 100644
index 000000000000..2d4235482658
--- /dev/null
+++ b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.txt
@@ -0,0 +1,24 @@
+DT bindings for the LCD2S Character LCD Display
+
+The LCD2S is a Character LCD Display manufactured by Modtronix Engineering.
+The display supports a serial I2C and SPI interface. The driver currently
+only supports the I2C interface.
+
+Required properties:
+ - compatible: Must contain "modtronix,lcd2s",
+ - reg: I2C bus address of the display,
+ - display-height-chars: Height of the display, in character cells,
+ - display-width-chars: Width of the display, in character cells.
+
+Example:
+
+i2c1: i2c@0 {
+ compatible = "ti,omap3-i2c";
+
+ lcd2s: auxdisplay@28 {
+ reg = <0x28>;
+ compatible = "modtronix,lcd2s";
+ display-height-chars = <4>;
+ display-width-chars = <20>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 967e78c5ec0a..b853974956f1 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -603,6 +603,8 @@ patternProperties:
description: MiraMEMS Sensing Technology Co., Ltd.
"^mitsubishi,.*":
description: Mitsubishi Electric Corporation
+ "^modtronix,.*":
+ description: Modtronix Engineering
"^mosaixtech,.*":
description: Mosaix Technologies, Inc.
"^motorola,.*":
--
2.23.0

2019-10-16 12:54:55

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH 3/3] auxdisplay: add a driver for lcd2s character display

This driver allows to use a lcd2s 20x4 character display from modtronix
engineering as an auxdisplay charlcd device.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/Kconfig | 11 +
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/lcd2s.c | 411 ++++++++++++++++++++++++++++++++++++
3 files changed, 423 insertions(+)
create mode 100644 drivers/auxdisplay/lcd2s.c

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 5fb6784c6579..b658915945ca 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -169,6 +169,17 @@ config HT16K33
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
LED controller driver with keyscan.

+config LCD2S
+ tristate "lcd2s 20x4 character display over I2C console"
+ depends on I2C
+ select CHARLCD
+ default n
+ help
+ This is a driver that lets you use the lcd2s 20x4 character display
+ from modtronix engineering as a console output device. The display
+ is a simple single color character display. You have to connect it
+ to an I2C bus.
+
config ARM_CHARLCD
bool "ARM Ltd. Character LCD Driver"
depends on PLAT_VERSATILE
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 7e8a8c3eb3c3..307771027c89 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
obj-$(CONFIG_HD44780) += hd44780.o
obj-$(CONFIG_HT16K33) += ht16k33.o
obj-$(CONFIG_PARPORT_PANEL) += panel.o
+obj-$(CONFIG_LCD2S) += lcd2s.o
diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c
new file mode 100644
index 000000000000..f207876f7f95
--- /dev/null
+++ b/drivers/auxdisplay/lcd2s.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * console driver for LCD2S 4x20 character displays connected through i2c.
+ * The display also has a spi interface, but the driver does not support
+ * this yet.
+ *
+ * This is a driver allowing you to use a LCD2S 4x20 from modtronix
+ * engineering as auxdisplay character device.
+ *
+ * (C) 2019 by Lemonage Software GmbH
+ * Author: Lars Pöschel <[email protected]>
+ * All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "charlcd.h"
+
+#define LCD2S_CMD_CUR_MOVES_FWD 0x09
+#define LCD2S_CMD_CUR_BLINK_OFF 0x10
+#define LCD2S_CMD_CUR_UL_OFF 0x11
+#define LCD2S_CMD_DISPLAY_OFF 0x12
+#define LCD2S_CMD_CUR_BLINK_ON 0x18
+#define LCD2S_CMD_CUR_UL_ON 0x19
+#define LCD2S_CMD_DISPLAY_ON 0x1a
+#define LCD2S_CMD_BACKLIGHT_OFF 0x20
+#define LCD2S_CMD_BACKLIGHT_ON 0x28
+#define LCD2S_CMD_WRITE 0x80
+#define LCD2S_CMD_MOV_CUR_RIGHT 0x83
+#define LCD2S_CMD_MOV_CUR_LEFT 0x84
+#define LCD2S_CMD_SHIFT_RIGHT 0x85
+#define LCD2S_CMD_SHIFT_LEFT 0x86
+#define LCD2S_CMD_SHIFT_UP 0x87
+#define LCD2S_CMD_SHIFT_DOWN 0x88
+#define LCD2S_CMD_CUR_ADDR 0x89
+#define LCD2S_CMD_CUR_POS 0x8a
+#define LCD2S_CMD_CUR_RESET 0x8b
+#define LCD2S_CMD_CLEAR 0x8c
+#define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92
+#define LCD2S_CMD_READ_STATUS 0xd0
+
+#define LCD2S_CHARACTER_SIZE 8
+
+#define LCD2S_STATUS_BUF_MASK 0x7f
+
+struct lcd2s_data {
+ struct i2c_client *i2c;
+ struct charlcd *charlcd;
+};
+
+static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count)
+{
+ s32 status;
+
+ status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS);
+ if (status < 0)
+ return status;
+
+ while ((status & LCD2S_STATUS_BUF_MASK) < count) {
+ mdelay(1);
+ status = i2c_smbus_read_byte_data(client,
+ LCD2S_CMD_READ_STATUS);
+ if (status < 0)
+ return status;
+ }
+ return 0;
+}
+
+static int lcd2s_i2c_master_send(const struct i2c_client *client,
+ const char *buf, int count)
+{
+ s32 status;
+
+ status = lcd2s_wait_buf_free(client, count);
+ if (status < 0)
+ return status;
+
+ return i2c_master_send(client, buf, count);
+}
+
+static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
+{
+ s32 status;
+
+ status = lcd2s_wait_buf_free(client, 1);
+ if (status < 0)
+ return status;
+
+ return i2c_smbus_write_byte(client, value);
+}
+
+static int lcd2s_print(struct charlcd *lcd, int c)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[2] = { LCD2S_CMD_WRITE, c };
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+ return 0;
+}
+
+static int lcd2s_gotoxy(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[] = { LCD2S_CMD_CUR_POS, lcd->addr.y + 1, lcd->addr.x + 1};
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+
+ return 0;
+}
+
+static int lcd2s_home(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET);
+ return 0;
+}
+
+static int lcd2s_init_display(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ /* turn everything off, but display on */
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
+
+ return 0;
+}
+
+static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT);
+
+ return 0;
+}
+
+static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT);
+
+ return 0;
+}
+
+static int lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
+
+ return 0;
+}
+
+static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF);
+
+ return 0;
+}
+
+static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
+
+ return 0;
+}
+
+static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
+
+ return 0;
+}
+
+static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
+{
+ return 0;
+}
+
+static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines)
+{
+ return 0;
+}
+
+static int lcd2s_redefine_char(struct charlcd *lcd, char *esc)
+{
+ /* 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.
+ */
+
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR };
+ u8 value;
+ int shift, i;
+
+ if (!strchr(esc, ';'))
+ return 0;
+
+ esc++;
+
+ buf[1] = *(esc++) - '0';
+ if (buf[1] > 7)
+ return 1;
+
+ i = 0;
+ shift = 0;
+ value = 0;
+ while (*esc && i < LCD2S_CHARACTER_SIZE + 2) {
+ 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) {
+ buf[i++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+ return 1;
+}
+
+static int lcd2s_clear_display(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ /* This implicitly sets cursor to first row and column */
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
+ return 0;
+}
+
+static const struct charlcd_ops lcd2s_ops = {
+ .print = lcd2s_print,
+ .backlight = lcd2s_backlight,
+ .gotoxy = lcd2s_gotoxy,
+ .home = lcd2s_home,
+ .clear_display = lcd2s_clear_display,
+ .init_display = lcd2s_init_display,
+ .shift_cursor = lcd2s_shift_cursor,
+ .shift_display = lcd2s_shift_display,
+ .display = lcd2s_display,
+ .cursor = lcd2s_cursor,
+ .blink = lcd2s_blink,
+ .fontsize = lcd2s_fontsize,
+ .lines = lcd2s_lines,
+ .redefine_char = lcd2s_redefine_char,
+};
+
+static int lcd2s_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct charlcd *lcd;
+ struct lcd2s_data *lcd2s;
+ int err;
+
+ if (!i2c_check_functionality(i2c->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ return -EIO;
+
+ /* Test, if the display is responding */
+ err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF);
+ if (err < 0)
+ return err;
+
+ lcd = charlcd_alloc();
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd2s = kzalloc(sizeof(struct lcd2s_data), GFP_KERNEL);
+ if (!lcd2s) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ lcd->drvdata = lcd2s;
+ lcd2s->i2c = i2c;
+ lcd2s->charlcd = lcd;
+
+ /* Required properties */
+ err = device_property_read_u32(&i2c->dev, "display-height-chars",
+ &lcd->height);
+ if (err)
+ goto fail2;
+
+ err = device_property_read_u32(&i2c->dev, "display-width-chars",
+ &lcd->width);
+ if (err)
+ goto fail2;
+
+ lcd->ops = &lcd2s_ops;
+
+ err = charlcd_register(lcd2s->charlcd);
+ if (err)
+ goto fail2;
+
+ i2c_set_clientdata(i2c, lcd2s);
+ return 0;
+
+fail2:
+ kfree(lcd2s);
+fail1:
+ kfree(lcd);
+ return err;
+}
+
+static int lcd2s_i2c_remove(struct i2c_client *i2c)
+{
+ struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c);
+
+ kfree(lcd2s->charlcd);
+ charlcd_unregister(lcd2s->charlcd);
+ return 0;
+}
+
+static const struct i2c_device_id lcd2s_i2c_id[] = {
+ { "lcd2s", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lcd2s_of_table[] = {
+ { .compatible = "modtronix,lcd2s" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lcd2s_of_table);
+#endif
+
+static struct i2c_driver lcd2s_i2c_driver = {
+ .driver = {
+ .name = "lcd2s",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(lcd2s_of_table),
+#endif
+ },
+ .probe = lcd2s_i2c_probe,
+ .remove = lcd2s_i2c_remove,
+ .id_table = lcd2s_i2c_id,
+};
+
+static int __init lcd2s_modinit(void)
+{
+ int ret = 0;
+
+ ret = i2c_add_driver(&lcd2s_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register lcd2s driver\n");
+
+ return ret;
+}
+module_init(lcd2s_modinit)
+
+static void __exit lcd2s_exit(void)
+{
+ i2c_del_driver(&lcd2s_i2c_driver);
+}
+module_exit(lcd2s_exit)
+
+MODULE_DESCRIPTION("LCD2S character display driver");
+MODULE_AUTHOR("Lars Poeschel");
+MODULE_LICENSE("GPL");
--
2.23.0

2019-10-17 12:39:04

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Wed, Oct 16, 2019 at 10:24 AM Lars Poeschel <[email protected]> wrote:
>
> charlcd.c contains lots of hd44780 hardware specific stuff. It is nearly
> impossible to reuse the interface for other character based displays.
> The current users of charlcd are the hd44780 and the panel drivers.
> This does factor out the hd44780 specific stuff out of charlcd into a
> new module called hd44780_common.
> charlcd gets rid of the hd44780 specfics and more generally useable.
> The hd44780 and panel drivers are modified to use the new
> hd44780_common.
> This is tested on a hd44780 connected through the gpios of a pcf8574.
>
> Signed-off-by: Lars Poeschel <[email protected]>
> ---
> drivers/auxdisplay/Kconfig | 16 +
> drivers/auxdisplay/Makefile | 1 +
> drivers/auxdisplay/charlcd.c | 591 ++++++++--------------------
> drivers/auxdisplay/charlcd.h | 109 ++++-
> drivers/auxdisplay/hd44780.c | 121 ++++--
> drivers/auxdisplay/hd44780_common.c | 370 +++++++++++++++++
> drivers/auxdisplay/hd44780_common.h | 32 ++
> drivers/auxdisplay/panel.c | 178 ++++-----
> 8 files changed, 851 insertions(+), 567 deletions(-)

Thanks Lars, CC'ing Geert since he wrote a large portion of this, as
well as Andy.

From a cursory look (sorry, not doing it inline since it is a pain to
edit in this UI given the size...):
* panel.c doesn't compile since lcd_backlight's return type did not
get updated, which makes me uneasy. I am not sure why you changed the
return type anyway, since callers ignore it and callees always return
0.
* Declared and then immediately defined hd44780_common in the header...?
* Some things (e.g. the addition of enums like charlcd_onoff) seem
like could have been done other patches (since they are not really
related to the reorganization).
* From checkpatch.pl: DOS line endings and trailing whitespace

I am not capable of testing this, so extra testing by anyone who has
the different hardware affected around is very welcome.

Cheers,
Miguel

2019-10-18 10:14:58

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Wed, Oct 16, 2019 at 06:53:20PM +0200, Miguel Ojeda wrote:
> On Wed, Oct 16, 2019 at 10:24 AM Lars Poeschel <[email protected]> wrote:
> >
> > charlcd.c contains lots of hd44780 hardware specific stuff. It is nearly
> > impossible to reuse the interface for other character based displays.
> > The current users of charlcd are the hd44780 and the panel drivers.
> > This does factor out the hd44780 specific stuff out of charlcd into a
> > new module called hd44780_common.
> > charlcd gets rid of the hd44780 specfics and more generally useable.
> > The hd44780 and panel drivers are modified to use the new
> > hd44780_common.
> > This is tested on a hd44780 connected through the gpios of a pcf8574.
> >
> > Signed-off-by: Lars Poeschel <[email protected]>
> > ---
> > drivers/auxdisplay/Kconfig | 16 +
> > drivers/auxdisplay/Makefile | 1 +
> > drivers/auxdisplay/charlcd.c | 591 ++++++++--------------------
> > drivers/auxdisplay/charlcd.h | 109 ++++-
> > drivers/auxdisplay/hd44780.c | 121 ++++--
> > drivers/auxdisplay/hd44780_common.c | 370 +++++++++++++++++
> > drivers/auxdisplay/hd44780_common.h | 32 ++
> > drivers/auxdisplay/panel.c | 178 ++++-----
> > 8 files changed, 851 insertions(+), 567 deletions(-)
>
> Thanks Lars, CC'ing Geert since he wrote a large portion of this, as
> well as Andy.
>
> From a cursory look (sorry, not doing it inline since it is a pain to
> edit in this UI given the size...):

I am okay with this. I know, what you are talking about, since I know
the code very well. But maybe it is a bit harder to follow for others.

> * panel.c doesn't compile since lcd_backlight's return type did not
> get updated, which makes me uneasy. I am not sure why you changed the
> return type anyway, since callers ignore it and callees always return
> 0.

That panel.c doesn't compile is of course a no-go. Sorry. I missed
something and I will fix this in a next version of the patch. But before
submitting a next version, I will wait some time, if there is more
feedback.
The idea with changing the return types: It seems a bit, that with this
patch charlcd is becoming more of an universal interface and maybe more
display backends get added - maybe with displays, that can report
failure of operations. And I thought, it will be better to have this
earlier and have the "interface" stable and more uniform. But you are
the maintainer. If you don't like the changed return types I happily
revert back to the original ones in the next version of the patch.

> * Declared and then immediately defined hd44780_common in the header...?

This is not intended. I'll change it.

> * Some things (e.g. the addition of enums like charlcd_onoff) seem
> like could have been done other patches (since they are not really
> related to the reorganization).

I can split this out into separate patches. It'd be good know what else
you mean by "some things" so I can do this as well. Do you want each
enum as a separate patch or one big enum patch ?

> * From checkpatch.pl: DOS line endings and trailing whitespace

Strange. I did indeed checkpatch.pl the patches before submitting and I
got no complaints about whitespace or line endings. There was "WARNING:
added, moved or deleted file(s), does MAINTAINERS need updating?" and
patch 1 also has "WARNING: please write a paragraph that describes the
config symbol fully". I submitted the patches with git send-email so it
is very unlikely, that the mailer messed up the patches. Strange...
Oh by the way: Do you know what I can do to make checkpatch happy with
its describing of the config symbol ? I tried writing a help paragraph
for the config symbols in Kconfig, but that did not help.

> I am not capable of testing this, so extra testing by anyone who has
> the different hardware affected around is very welcome.

Are you able to test the panel driver ?

Thank you for your prompt feedback!

Lars

2019-10-18 15:42:32

by Andy Shevchenko

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Thu, Oct 17, 2019 at 11:07 AM Lars Poeschel <[email protected]> wrote:
> On Wed, Oct 16, 2019 at 06:53:20PM +0200, Miguel Ojeda wrote:
> > On Wed, Oct 16, 2019 at 10:24 AM Lars Poeschel <[email protected]> wrote:

> > Thanks Lars, CC'ing Geert since he wrote a large portion of this, as
> > well as Andy.

I would be able to test next version if you Cc it to me (better to my
@linux.intel.com address).


> The idea with changing the return types: It seems a bit, that with this
> patch charlcd is becoming more of an universal interface and maybe more
> display backends get added - maybe with displays, that can report
> failure of operations. And I thought, it will be better to have this
> earlier and have the "interface" stable and more uniform. But you are
> the maintainer. If you don't like the changed return types I happily
> revert back to the original ones in the next version of the patch.

First rule of all, split as much as one logical change per patch. If
you wish to split, do simple split and nothing else. But I think you
also need to prepare something for the split. I would think of
introducing ops structure beforehand.

> Are you able to test the panel driver ?
I would be able to test on HD44780 connected via GPIO (4-bit mode).

--
With Best Regards,
Andy Shevchenko

2019-10-19 08:31:47

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Thu, Oct 17, 2019 at 10:07 AM Lars Poeschel <[email protected]> wrote:
>
> That panel.c doesn't compile is of course a no-go. Sorry. I missed
> something and I will fix this in a next version of the patch. But before
> submitting a next version, I will wait some time, if there is more
> feedback.

I meant I wasn't sure if the overall patch as that well tested ;-)

> The idea with changing the return types: It seems a bit, that with this
> patch charlcd is becoming more of an universal interface and maybe more
> display backends get added - maybe with displays, that can report
> failure of operations. And I thought, it will be better to have this
> earlier and have the "interface" stable and more uniform. But you are
> the maintainer. If you don't like the changed return types I happily
> revert back to the original ones in the next version of the patch.

I see the value of the idea, but given kernel APIs are not stable
anyway, I prefer not to have things that we do not use yet. If someone
requires returning an error code in a future driver, they can change
it later on. I don't have a strong opinion on this anyway.

> > * Some things (e.g. the addition of enums like charlcd_onoff) seem
> > like could have been done other patches (since they are not really
> > related to the reorganization).
>
> I can split this out into separate patches. It'd be good know what else
> you mean by "some things" so I can do this as well. Do you want each
> enum as a separate patch or one big enum patch ?

As Andy said, ideally patches/commits implement logical changes, i.e.
broken down as much as reasonably possible. For instance, changing the
interface from int/bool to an enum is probably a good idea, but it is
not related to the overall reorganization. In fact, it is orthogonal,
since it could be done either before or after the reorganization.

In general, it is much more easy to understand and review changes if
you split them. For instance, I noticed the enum change by chance, I
may have not noticed it; but if it was in a different patch, it would
have been clear for everyone. It also allows you to explain each
change individually in the commit message. Another of those "things"
is what Andy suggested. Of course, after you split things, it will
become easier to spot other things too, so we might have to do another
round etc.

A single patch for all the enums is fine. We could split those too,
but given the change has the same rationale, and is small enough, we
can put them together and share the commit message + is easier for you
and us to handle too. That is the "as much as reasonably possible"
part of the guideline ;-)

> Strange. I did indeed checkpatch.pl the patches before submitting and I
> got no complaints about whitespace or line endings. There was "WARNING:
> added, moved or deleted file(s), does MAINTAINERS need updating?" and
> patch 1 also has "WARNING: please write a paragraph that describes the
> config symbol fully". I submitted the patches with git send-email so it
> is very unlikely, that the mailer messed up the patches. Strange...

Hm... Try sending the series to yourself and see if you can reproduce it.

> Oh by the way: Do you know what I can do to make checkpatch happy with
> its describing of the config symbol ? I tried writing a help paragraph
> for the config symbols in Kconfig, but that did not help.

CC'ing Joe.

> > I am not capable of testing this, so extra testing by anyone who has
> > the different hardware affected around is very welcome.
>
> Are you able to test the panel driver ?

Even worse: at the moment I cannot test any of the drivers (and more
have been added over time). The 3 of them I could test are very
old/obsolete and I will be dropping them, so my role here is basically
"abstract" :-)

If there is anyone that can actually test most of the drivers in
auxdisplay and wants to help maintaining, it would be great, in fact.

> Thank you for your prompt feedback!

You're welcome!

Cheers,
Miguel

2019-10-19 08:34:35

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Fri, 2019-10-18 at 17:08 +0200, Miguel Ojeda wrote:
> On Thu, Oct 17, 2019 at 10:07 AM Lars Poeschel <[email protected]> wrote:
[]
> > Oh by the way: Do you know what I can do to make checkpatch happy with
> > its describing of the config symbol ? I tried writing a help paragraph
> > for the config symbols in Kconfig, but that did not help.
>
> CC'ing Joe.

add
--ignore=CONFIG_DESCRIPTION
or
--min-conf-desc-length=1 (default is 4)

to the checkpatch command line, or just ignore it.

AK: I guess there's still some debate as to the proper
minimum length of a Kconfig help section paragraph.

commit 3354957a4f8b9bb4b43625232acdf0626851c82f
Author: Andi Kleen <[email protected]>
Date: Mon May 24 14:33:29 2010 -0700

checkpatch: add check for too short Kconfig descriptions

I've seen various new Kconfigs with rather unhelpful one liner
descriptions. Add a Kconfig warning for a minimum length of the Kconfig
help section.

Right now I arbitarily chose 4. The exact value can be debated.


2019-10-19 09:20:58

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 1/3] auxdisplay: Make charlcd.[ch] more general

On Fri, Oct 18, 2019 at 08:33:26AM -0700, Joe Perches wrote:
> On Fri, 2019-10-18 at 17:08 +0200, Miguel Ojeda wrote:
> > On Thu, Oct 17, 2019 at 10:07 AM Lars Poeschel <[email protected]> wrote:
> []
> > > Oh by the way: Do you know what I can do to make checkpatch happy with
> > > its describing of the config symbol ? I tried writing a help paragraph
> > > for the config symbols in Kconfig, but that did not help.
> >
> > CC'ing Joe.
>
> add
> --ignore=CONFIG_DESCRIPTION
> or
> --min-conf-desc-length=1 (default is 4)
>
> to the checkpatch command line, or just ignore it.
>
> AK: I guess there's still some debate as to the proper
> minimum length of a Kconfig help section paragraph.

I still think four lines is a good minimum

If it's not worth writing 4 lines for, perhaps the Kconfig
symbol is not needed at all?

-Andi

2019-10-29 09:20:54

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 2/3] auxdisplay: lcd2s DT binding doc

On Wed, 16 Oct 2019 10:26:47 +0200, Lars Poeschel wrote:
> Add a binding doc for the modtronix lcd2s auxdisplay driver. It also
> adds modtronix to the list of known vendor-prefixes.
>
> Signed-off-by: Lars Poeschel <[email protected]>
> ---
> .../bindings/auxdisplay/modtronix,lcd2s.txt | 24 +++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 ++
> 2 files changed, 26 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.txt
>

Reviewed-by: Rob Herring <[email protected]>

2020-09-21 14:48:55

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 00/33] Make charlcd device independent

From: Lars Poeschel <[email protected]>

This tries to make charlcd device independent. At the moment hd44780
device specific code is contained deep in charlcd. This moves this out
into a hd44780_common module, where the two hd44780 drivers we have at
the moment (hd44780 and panel) can use this from. The goal is that at
the end other drivers can use the charlcd interface.
I add one such driver at the end with the last patch.
I submitted this already some time ago [1], where the wish was so split
this into smaller chunks what I try to do with this new patchset.
Most of the patches pick one specific function in charlcd and move the
device specific code into hd44780_common.

[1] https://marc.info/?l=linux-kernel&m=157121432124507

Lars Poeschel (33):
auxdisplay: Use an enum for charlcd backlight on/off ops
auxdisplay: Introduce hd44780_common.[ch]
auxdisplay: Move hwidth and bwidth to struct hd44780_common
auxdisplay: Move ifwidth to struct hd44780_common
auxdisplay: Move write_data pointer to hd44780_common
auxdisplay: Move write_cmd pointers to hd44780 drivers
auxdisplay: Move addr out of charlcd_priv
auxdisplay: hd44780_common_print
auxdisplay: provide hd44780_common_gotoxy
auxdisplay: add home to charlcd_ops
auxdisplay: Move clear_display to hd44780_common
auxdisplay: make charlcd_backlight visible to hd44780_common
auxdisplay: Make use of enum for backlight on / off
auxdisplay: Move init_display to hd44780_common
auxdisplay: implement hd44780_common_shift_cursor
auxdisplay: Implement hd44780_common_display_shift
auxdisplay: Implement a hd44780_common_display
auxdisplay: Implement hd44780_common_cursor
auxdisplay: Implement hd44780_common_blink
auxdisplay: cleanup unnecessary hd44780 code in charlcd
auxdisplay: Implement hd44780_common_fontsize
auxdisplay: Implement hd44780_common_lines
auxdisplay: Remove unnecessary hd44780 from charlcd
auxdisplay: Move char redefine code to hd44780_common
auxdisplay: Call charlcd_backlight in place
auxdisplay: Move clear_fast to hd44780
auxdisplay: remove naive display clear impl
auxdisplay: hd44780: Remove clear_fast
auxdisplay: charlcd: replace last device specific stuff
auxdisplay: Change gotoxy calling interface
auxdisplay: charlcd: Do not print chars at end of line
auxdisplay: lcd2s DT binding doc
auxdisplay: add a driver for lcd2s character display

.../bindings/auxdisplay/modtronix,lcd2s.yaml | 55 +++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
drivers/auxdisplay/Kconfig | 31 ++
drivers/auxdisplay/Makefile | 2 +
drivers/auxdisplay/charlcd.c | 406 +++++------------
drivers/auxdisplay/charlcd.h | 86 +++-
drivers/auxdisplay/hd44780.c | 120 +++--
drivers/auxdisplay/hd44780_common.c | 367 ++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 34 ++
drivers/auxdisplay/lcd2s.c | 409 ++++++++++++++++++
drivers/auxdisplay/panel.c | 125 ++++--
11 files changed, 1254 insertions(+), 383 deletions(-)
create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
create mode 100644 drivers/auxdisplay/hd44780_common.c
create mode 100644 drivers/auxdisplay/hd44780_common.h
create mode 100644 drivers/auxdisplay/lcd2s.c

--
2.28.0

2020-09-21 14:48:59

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 01/32] auxdisplay: Use an enum for charlcd backlight on/off ops

From: Lars Poeschel <[email protected]>

We use an enum for calling the functions in charlcd, that turn the
backlight on or off. This enum is generic and can be used for other
charlcd turn of / turn off operations as well.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 2 +-
drivers/auxdisplay/charlcd.h | 7 ++++++-
drivers/auxdisplay/hd44780.c | 2 +-
drivers/auxdisplay/panel.c | 2 +-
4 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 5aee0f546351..8aaee0fea9ab 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -101,7 +101,7 @@ static void long_sleep(int ms)
}

/* turn the backlight on or off */
-static void charlcd_backlight(struct charlcd *lcd, int on)
+static void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);

diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 00911ad0f3de..c66f038e5d2b 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -9,6 +9,11 @@
#ifndef _CHARLCD_H
#define _CHARLCD_H

+enum charlcd_onoff {
+ CHARLCD_OFF = 0,
+ CHARLCD_ON,
+};
+
struct charlcd {
const struct charlcd_ops *ops;
const unsigned char *char_conv; /* Optional */
@@ -30,7 +35,7 @@ struct charlcd_ops {
/* 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);
+ void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
};

struct charlcd *charlcd_alloc(unsigned int drvdata_size);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index bcbe13092327..5982158557bb 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -37,7 +37,7 @@ struct hd44780 {
struct gpio_desc *pins[PIN_NUM];
};

-static void hd44780_backlight(struct charlcd *lcd, int on)
+static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
struct hd44780 *hd = lcd->drvdata;

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 1c82d824ae00..de623ae219f1 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -708,7 +708,7 @@ static void lcd_send_serial(int byte)
}

/* turn the backlight on or off */
-static void lcd_backlight(struct charlcd *charlcd, int on)
+static void lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on)
{
if (lcd.pins.bl == PIN_NONE)
return;
--
2.28.0

2020-09-21 14:49:07

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 04/32] auxdisplay: Move ifwidth to struct hd44780_common

From: Lars Poeschel <[email protected]>

Move struct charlcd member ifwidth to our new struct hd44780_common.
ifwidth is hd44780 device specific and is used by two drivers at the
moment, so we move it to a common place, where both can use this.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 12 ++++++------
drivers/auxdisplay/charlcd.h | 1 -
drivers/auxdisplay/hd44780.c | 2 +-
drivers/auxdisplay/hd44780_common.c | 1 +
drivers/auxdisplay/hd44780_common.h | 1 +
5 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 02392336d7d3..59e0a815bf3d 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -223,9 +223,10 @@ static int charlcd_init_display(struct charlcd *lcd)
{
void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;
u8 init;

- if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
+ if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
return -EINVAL;

priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
@@ -238,7 +239,7 @@ static int charlcd_init_display(struct charlcd *lcd)
* the LCD is in 8-bit mode afterwards
*/
init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
- if (lcd->ifwidth == 4) {
+ if (hdc->ifwidth == 4) {
init >>= 4;
write_cmd_raw = lcd->ops->write_cmd_raw4;
} else {
@@ -251,7 +252,7 @@ static int charlcd_init_display(struct charlcd *lcd)
write_cmd_raw(lcd, init);
long_sleep(10);

- if (lcd->ifwidth == 4) {
+ if (hdc->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);
@@ -260,7 +261,7 @@ static int charlcd_init_display(struct charlcd *lcd)
/* set font height and lines number */
lcd->ops->write_cmd(lcd,
LCD_CMD_FUNCTION_SET |
- ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->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);
@@ -543,7 +544,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
lcd->ops->write_cmd(lcd,
LCD_CMD_FUNCTION_SET |
- ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->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 */
@@ -794,7 +795,6 @@ struct charlcd *charlcd_alloc(void)
priv->esc_seq.len = -1;

lcd = &priv->lcd;
- lcd->ifwidth = 8;

return lcd;
}
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 2a12d07705a3..5dce9dd36562 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -18,7 +18,6 @@ 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;

diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 0603af8f2336..f6786239c36f 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -275,7 +275,7 @@ static int hd44780_probe(struct platform_device *pdev)
/* Optional properties */
device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);

- lcd->ifwidth = ifwidth;
+ hdc->ifwidth = ifwidth;
lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;

ret = charlcd_register(lcd);
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 34e6d292fde8..285073a00302 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -12,6 +12,7 @@ struct hd44780_common *hd44780_common_alloc(void)
if (!hd)
return NULL;

+ hd->ifwidth = 8;
hd->bwidth = DEFAULT_LCD_BWIDTH;
hd->hwidth = DEFAULT_LCD_HWIDTH;
return hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 3ff47d2c5691..1100e0a32394 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -4,6 +4,7 @@
#define DEFAULT_LCD_HWIDTH 64

struct hd44780_common {
+ int ifwidth; /* 4-bit or 8-bit (default) */
int bwidth; /* Default set by hd44780_alloc() */
int hwidth; /* Default set by hd44780_alloc() */
void *hd44780;
--
2.28.0

2020-09-21 14:49:17

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 07/32] auxdisplay: Move addr out of charlcd_priv

From: Lars Poeschel <[email protected]>

Move out the struct addr from struct charlcd_priv into the less private
struct charlcd. This member is used to pass position information. The
individual drivers need to be able to read this information, so we move
this out of charlcd_priv to charlcd structure.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 61 +++++++++++++++---------------------
drivers/auxdisplay/charlcd.h | 6 ++++
2 files changed, 31 insertions(+), 36 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index ce6622f71c34..1b37d4bc38f9 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -72,12 +72,6 @@ struct charlcd_priv {
/* 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];
@@ -148,7 +142,6 @@ EXPORT_SYMBOL_GPL(charlcd_poke);

static void charlcd_gotoxy(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
struct hd44780_common *hdc = lcd->drvdata;
unsigned int addr;

@@ -156,37 +149,34 @@ static void charlcd_gotoxy(struct charlcd *lcd)
* we force the cursor to stay at the end of the
* line if it wants to go farther
*/
- addr = priv->addr.x < hdc->bwidth ? priv->addr.x & (hdc->hwidth - 1)
+ addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
: hdc->bwidth - 1;
- if (priv->addr.y & 1)
+ if (lcd->addr.y & 1)
addr += hdc->hwidth;
- if (priv->addr.y & 2)
+ if (lcd->addr.y & 2)
addr += hdc->bwidth;
hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
}

static void charlcd_home(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
-
- priv->addr.x = 0;
- priv->addr.y = 0;
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
charlcd_gotoxy(lcd);
}

static void charlcd_print(struct charlcd *lcd, char c)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
struct hd44780_common *hdc = lcd->drvdata;

- if (priv->addr.x < hdc->bwidth) {
+ if (lcd->addr.x < hdc->bwidth) {
if (lcd->char_conv)
c = lcd->char_conv[(unsigned char)c];
hdc->write_data(hdc, c);
- priv->addr.x++;
+ lcd->addr.x++;

/* prevents the cursor from wrapping onto the next line */
- if (priv->addr.x == hdc->bwidth)
+ if (lcd->addr.x == hdc->bwidth)
charlcd_gotoxy(lcd);
}
}
@@ -210,12 +200,11 @@ static void charlcd_clear_fast(struct charlcd *lcd)
/* clears the display and resets X/Y */
static void charlcd_clear_display(struct charlcd *lcd)
{
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
struct hd44780_common *hdc = lcd->drvdata;

hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
- priv->addr.x = 0;
- priv->addr.y = 0;
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
/* we must wait a few milliseconds (15) */
long_sleep(15);
}
@@ -415,21 +404,21 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
processed = 1;
break;
case 'l': /* Shift Cursor Left */
- if (priv->addr.x > 0) {
+ if (lcd->addr.x > 0) {
/* back one char if not at end of line */
- if (priv->addr.x < hdc->bwidth)
+ if (lcd->addr.x < hdc->bwidth)
hdc->write_cmd(hdc, LCD_CMD_SHIFT);
- priv->addr.x--;
+ lcd->addr.x--;
}
processed = 1;
break;
case 'r': /* shift cursor right */
- if (priv->addr.x < lcd->width) {
+ if (lcd->addr.x < lcd->width) {
/* allow the cursor to pass the end of the line */
- if (priv->addr.x < (hdc->bwidth - 1))
+ if (lcd->addr.x < (hdc->bwidth - 1))
hdc->write_cmd(hdc,
LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
- priv->addr.x++;
+ lcd->addr.x++;
}
processed = 1;
break;
@@ -446,7 +435,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
case 'k': { /* kill end of line */
int x;

- for (x = priv->addr.x; x < hdc->bwidth; x++)
+ for (x = lcd->addr.x; x < hdc->bwidth; x++)
hdc->write_data(hdc, ' ');

/* restore cursor position */
@@ -519,7 +508,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;

/* If the command is valid, move to the new address */
- if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
+ if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
charlcd_gotoxy(lcd);

/* Regardless of its validity, mark as processed */
@@ -577,15 +566,15 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
break;
case '\b':
/* go back one char and clear it */
- if (priv->addr.x > 0) {
+ if (lcd->addr.x > 0) {
/*
* check if we're not at the
* end of the line
*/
- if (priv->addr.x < hdc->bwidth)
+ if (lcd->addr.x < hdc->bwidth)
/* back one char */
hdc->write_cmd(hdc, LCD_CMD_SHIFT);
- priv->addr.x--;
+ lcd->addr.x--;
}
/* replace with a space */
hdc->write_data(hdc, ' ');
@@ -601,15 +590,15 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* flush the remainder of the current line and
* go to the beginning of the next line
*/
- for (; priv->addr.x < hdc->bwidth; priv->addr.x++)
+ for (; lcd->addr.x < hdc->bwidth; lcd->addr.x++)
hdc->write_data(hdc, ' ');
- priv->addr.x = 0;
- priv->addr.y = (priv->addr.y + 1) % lcd->height;
+ lcd->addr.x = 0;
+ lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
charlcd_gotoxy(lcd);
break;
case '\r':
/* go to the beginning of the same line */
- priv->addr.x = 0;
+ lcd->addr.x = 0;
charlcd_gotoxy(lcd);
break;
case '\t':
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index ad6fd2733523..ff4896af2189 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -21,6 +21,12 @@ struct charlcd {
int height;
int width;

+ /* Contains the LCD X and Y offset */
+ struct {
+ unsigned long x;
+ unsigned long y;
+ } addr;
+
void *drvdata;
};

--
2.28.0

2020-09-21 14:49:22

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 08/32] auxdisplay: hd44780_common_print

From: Lars Poeschel <[email protected]>

We create a hd44780_common_print function. It is derived from the
original charlcd_print. charlcd_print becomes a device independent print
function, that then only calles via it's ops function pointers, into the
print function offered by drivers.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 28 +++++++++++++++-------------
drivers/auxdisplay/charlcd.h | 11 +++++++++++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 13 +++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
5 files changed, 42 insertions(+), 13 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 1b37d4bc38f9..72ed004a8980 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -167,18 +167,15 @@ static void charlcd_home(struct charlcd *lcd)

static void charlcd_print(struct charlcd *lcd, char c)
{
- struct hd44780_common *hdc = lcd->drvdata;
+ if (lcd->char_conv)
+ c = lcd->char_conv[(unsigned char)c];

- if (lcd->addr.x < hdc->bwidth) {
- if (lcd->char_conv)
- c = lcd->char_conv[(unsigned char)c];
- hdc->write_data(hdc, c);
+ if (!lcd->ops->print(lcd, c))
lcd->addr.x++;

- /* prevents the cursor from wrapping onto the next line */
- if (lcd->addr.x == hdc->bwidth)
- charlcd_gotoxy(lcd);
- }
+ /* prevents the cursor from wrapping onto the next line */
+ if (lcd->addr.x == lcd->width)
+ charlcd_gotoxy(lcd);
}

static void charlcd_clear_fast(struct charlcd *lcd)
@@ -192,7 +189,7 @@ static void charlcd_clear_fast(struct charlcd *lcd)
lcd->ops->clear_fast(lcd);
else
for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
- hdc->write_data(hdc, ' ');
+ lcd->ops->print(lcd, ' ');

charlcd_home(lcd);
}
@@ -433,12 +430,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
processed = 1;
break;
case 'k': { /* kill end of line */
- int x;
+ int x, xs, ys;

+ xs = lcd->addr.x;
+ ys = lcd->addr.y;
for (x = lcd->addr.x; x < hdc->bwidth; x++)
- hdc->write_data(hdc, ' ');
+ lcd->ops->print(lcd, ' ');

/* restore cursor position */
+ lcd->addr.x = xs;
+ lcd->addr.y = ys;
charlcd_gotoxy(lcd);
processed = 1;
break;
@@ -591,7 +592,8 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* go to the beginning of the next line
*/
for (; lcd->addr.x < hdc->bwidth; lcd->addr.x++)
- hdc->write_data(hdc, ' ');
+ lcd->ops->print(lcd, ' ');
+
lcd->addr.x = 0;
lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
charlcd_gotoxy(lcd);
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index ff4896af2189..874519f079b4 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -30,9 +30,20 @@ struct charlcd {
void *drvdata;
};

+/**
+ * struct charlcd_ops - Functions used by charlcd. Drivers have to implement
+ * these.
+ * @clear_fast: Clear the whole display and set cursor to position 0, 0.
+ * @backlight: Turn backlight on or off. Optional.
+ * @print: just Print one character to the display at current cursor position.
+ * The cursor is advanced by charlcd.
+ * The buffered cursor position is advanced by charlcd. The cursor should not
+ * wrap to the next line at the end of a line.
+ */
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*print)(struct charlcd *lcd, int c);
};

struct charlcd *charlcd_alloc(void);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index dc4738563298..9680bb611639 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -126,6 +126,7 @@ static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)

static const struct charlcd_ops hd44780_ops_gpio8 = {
.backlight = hd44780_backlight,
+ .print = hd44780_common_print,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -169,6 +170,7 @@ static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)

static const struct charlcd_ops hd44780_ops_gpio4 = {
.backlight = hd44780_backlight,
+ .print = hd44780_common_print,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 285073a00302..da296c5ad7a5 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -2,8 +2,21 @@
#include <linux/module.h>
#include <linux/slab.h>

+#include "charlcd.h"
#include "hd44780_common.h"

+int hd44780_common_print(struct charlcd *lcd, int c)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (lcd->addr.x < hdc->bwidth) {
+ hdc->write_data(hdc, c);
+ return 0;
+ }
+
+ return 1;
+}
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 6c38ddf8f2ce..02d650903e35 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -14,5 +14,6 @@ struct hd44780_common {
void *hd44780;
};

+int hd44780_common_print(struct charlcd *lcd, int c);
struct hd44780_common *hd44780_common_alloc(void);

--
2.28.0

2020-09-21 14:49:38

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

From: Lars Poeschel <[email protected]>

Skip printing characters at the end of a display line. This fits to the
behaviour we already had, that the cursor is nailed to last position of
a line.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index cd3a304c08ec..1d5b3af9cca7 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -111,6 +111,9 @@ static void charlcd_home(struct charlcd *lcd)

static void charlcd_print(struct charlcd *lcd, char c)
{
+ if (lcd->addr.x >= lcd->width)
+ return;
+
if (lcd->char_conv)
c = lcd->char_conv[(unsigned char)c];

--
2.28.0

2020-09-21 14:50:01

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 32/32] auxdisplay: lcd2s DT binding doc

From: Lars Poeschel <[email protected]>

Add a binding doc for the modtronix lcd2s auxdisplay driver. It also
adds modtronix to the list of known vendor-prefixes.

Signed-off-by: Lars Poeschel <[email protected]>
--
Changes in v2:
- Adopted yaml based file format
---
.../bindings/auxdisplay/modtronix,lcd2s.yaml | 55 +++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
2 files changed, 57 insertions(+)
create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml

diff --git a/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
new file mode 100644
index 000000000000..9a70e28d2e61
--- /dev/null
+++ b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/auxdisplay/modtronix,lcd2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Modtronix engineering LCD2S Character LCD Display
+
+description:
+ The LCD2S is a Character LCD Display manufactured by Modtronix Engineering.
+ The display supports a serial I2C and SPI interface. The driver currently
+ only supports the I2C interface.
+
+properties:
+ compatible:
+ const: modtronix,lcd2s
+
+ reg:
+ maxItems: 1
+ description:
+ I2C bus address of the display.
+
+ display-height-chars:
+ description: Height of the display, in character cells.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 1
+ maximum: 4
+
+ display-width-chars:
+ description: Width of the display, in character cells.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 16
+ maximum: 20
+
+required:
+ - compatible
+ - reg
+ - display-height-chars
+ - display-width-chars
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ lcd2s: auxdisplay@28 {
+ compatible = "modtronix,lcd2s";
+ reg = <0x28>;
+ display-height-chars = <4>;
+ display-width-chars = <20>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 63996ab03521..a9dd168db6ce 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -667,6 +667,8 @@ patternProperties:
description: MiraMEMS Sensing Technology Co., Ltd.
"^mitsubishi,.*":
description: Mitsubishi Electric Corporation
+ "^modtronix,.*":
+ description: Modtronix Engineering
"^mosaixtech,.*":
description: Mosaix Technologies, Inc.
"^motorola,.*":
--
2.28.0

2020-09-21 14:50:10

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 27/32] auxdisplay: remove naive display clear impl

From: Lars Poeschel <[email protected]>

Remove the naive (character after character from upper left to lower
right) display clear implementation in charlcd_clear_fast. It was a
fallback and we fall back to using the clear_display implementation
instead.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index ef448c42abbd..0137e741c5bb 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -127,21 +127,13 @@ static void charlcd_print(struct charlcd *lcd, char c)

static void charlcd_clear_fast(struct charlcd *lcd)
{
- int x, y;
-
if (lcd->ops->clear_fast)
lcd->ops->clear_fast(lcd);
- else {
- for (y = 0; y < lcd->height; y++) {
- lcd->addr.x = 0;
- lcd->addr.y = y;
- lcd->ops->gotoxy(lcd);
- for (x = 0; x < lcd->width; x++)
- lcd->ops->print(lcd, ' ');
- }
+ else
+ lcd->ops->clear_display(lcd);

- lcd->ops->home(lcd);
- }
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
}

/*
--
2.28.0

2020-09-21 14:50:48

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 03/32] auxdisplay: Move hwidth and bwidth to struct hd44780_common

From: Lars Poeschel <[email protected]>

hwidth is for the hardware buffer size and bwidth is for the buffer
width of one single line. This is specific to the hd44780 displays and
so it is moved out from charlcd to struct hd44780_common.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 40 ++++++++++++-------------
drivers/auxdisplay/charlcd.h | 6 ++--
drivers/auxdisplay/hd44780.c | 24 +++++++++------
drivers/auxdisplay/hd44780_common.c | 3 +-
drivers/auxdisplay/hd44780_common.h | 5 ++++
drivers/auxdisplay/panel.c | 45 +++++++++++++++--------------
6 files changed, 67 insertions(+), 56 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 8aaee0fea9ab..02392336d7d3 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -21,9 +21,7 @@
#include <generated/utsrelease.h>

#include "charlcd.h"
-
-#define DEFAULT_LCD_BWIDTH 40
-#define DEFAULT_LCD_HWIDTH 64
+#include "hd44780_common.h"

/* Keep the backlight on this many seconds for each flash */
#define LCD_BL_TEMPO_PERIOD 4
@@ -151,18 +149,19 @@ EXPORT_SYMBOL_GPL(charlcd_poke);
static void charlcd_gotoxy(struct charlcd *lcd)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;
unsigned int addr;

/*
* 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;
+ addr = priv->addr.x < hdc->bwidth ? priv->addr.x & (hdc->hwidth - 1)
+ : hdc->bwidth - 1;
if (priv->addr.y & 1)
- addr += lcd->hwidth;
+ addr += hdc->hwidth;
if (priv->addr.y & 2)
- addr += lcd->bwidth;
+ addr += hdc->bwidth;
lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
}

@@ -178,21 +177,23 @@ static void charlcd_home(struct charlcd *lcd)
static void charlcd_print(struct charlcd *lcd, char c)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;

- if (priv->addr.x < lcd->bwidth) {
+ if (priv->addr.x < hdc->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)
+ if (priv->addr.x == hdc->bwidth)
charlcd_gotoxy(lcd);
}
}

static void charlcd_clear_fast(struct charlcd *lcd)
{
+ struct hd44780_common *hdc = lcd->drvdata;
int pos;

charlcd_home(lcd);
@@ -200,7 +201,7 @@ static void charlcd_clear_fast(struct charlcd *lcd)
if (lcd->ops->clear_fast)
lcd->ops->clear_fast(lcd);
else
- for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
+ for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
lcd->ops->write_data(lcd, ' ');

charlcd_home(lcd);
@@ -348,6 +349,7 @@ static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
static inline int handle_lcd_special_code(struct charlcd *lcd)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;

/* LCD special codes */

@@ -413,7 +415,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
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)
+ if (priv->addr.x < hdc->bwidth)
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
priv->addr.x--;
}
@@ -422,7 +424,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
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))
+ if (priv->addr.x < (hdc->bwidth - 1))
lcd->ops->write_cmd(lcd,
LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
priv->addr.x++;
@@ -442,7 +444,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
case 'k': { /* kill end of line */
int x;

- for (x = priv->addr.x; x < lcd->bwidth; x++)
+ for (x = priv->addr.x; x < hdc->bwidth; x++)
lcd->ops->write_data(lcd, ' ');

/* restore cursor position */
@@ -554,6 +556,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
static void charlcd_write_char(struct charlcd *lcd, char c)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;

/* first, we'll test if we're in escape mode */
if ((c != '\n') && priv->esc_seq.len >= 0) {
@@ -577,7 +580,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* check if we're not at the
* end of the line
*/
- if (priv->addr.x < lcd->bwidth)
+ if (priv->addr.x < hdc->bwidth)
/* back one char */
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
priv->addr.x--;
@@ -596,7 +599,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* 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++)
+ for (; priv->addr.x < hdc->bwidth; priv->addr.x++)
lcd->ops->write_data(lcd, ' ');
priv->addr.x = 0;
priv->addr.y = (priv->addr.y + 1) % lcd->height;
@@ -779,12 +782,12 @@ static int charlcd_init(struct charlcd *lcd)
return 0;
}

-struct charlcd *charlcd_alloc(unsigned int drvdata_size)
+struct charlcd *charlcd_alloc(void)
{
struct charlcd_priv *priv;
struct charlcd *lcd;

- priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return NULL;

@@ -792,9 +795,6 @@ struct charlcd *charlcd_alloc(unsigned int drvdata_size)

lcd = &priv->lcd;
lcd->ifwidth = 8;
- lcd->bwidth = DEFAULT_LCD_BWIDTH;
- lcd->hwidth = DEFAULT_LCD_HWIDTH;
- lcd->drvdata = priv->drvdata;

return lcd;
}
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index c66f038e5d2b..2a12d07705a3 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -21,10 +21,8 @@ struct charlcd {
int ifwidth; /* 4-bit or 8-bit (default) */
int height;
int width;
- int bwidth; /* Default set by charlcd_alloc() */
- int hwidth; /* Default set by charlcd_alloc() */

- void *drvdata; /* Set by charlcd_alloc() */
+ void *drvdata;
};

struct charlcd_ops {
@@ -38,7 +36,7 @@ struct charlcd_ops {
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
};

-struct charlcd *charlcd_alloc(unsigned int drvdata_size);
+struct charlcd *charlcd_alloc(void);
void charlcd_free(struct charlcd *lcd);

int charlcd_register(struct charlcd *lcd);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 271dba9cd108..0603af8f2336 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -40,7 +40,8 @@ struct hd44780 {

static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

if (hd->pins[PIN_CTRL_BL])
gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
@@ -104,7 +105,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
/* 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;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, cmd, 0);

@@ -115,7 +117,8 @@ static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
/* 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;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, data, 1);

@@ -132,7 +135,8 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
/* 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;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, cmd, 0);

@@ -144,7 +148,8 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
- struct hd44780 *hd = lcd->drvdata;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;
unsigned int n;

/* Command nibble + RS, RW */
@@ -160,7 +165,8 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
/* 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;
+ struct hd44780_common *hdc = lcd->drvdata;
+ struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, data, 1);

@@ -204,7 +210,7 @@ static int hd44780_probe(struct platform_device *pdev)
if (!hdc)
return -ENOMEM;

- lcd = charlcd_alloc(sizeof(struct hd44780));
+ lcd = charlcd_alloc();
if (!lcd)
goto fail1;

@@ -264,10 +270,10 @@ static int hd44780_probe(struct platform_device *pdev)
* usually equal to the display width
*/
if (lcd->height > 2)
- lcd->bwidth = lcd->width;
+ hdc->bwidth = lcd->width;

/* Optional properties */
- device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
+ device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);

lcd->ifwidth = ifwidth;
lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 073f47397f7d..34e6d292fde8 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -12,10 +12,11 @@ struct hd44780_common *hd44780_common_alloc(void)
if (!hd)
return NULL;

+ hd->bwidth = DEFAULT_LCD_BWIDTH;
+ hd->hwidth = DEFAULT_LCD_HWIDTH;
return hd;

}
EXPORT_SYMBOL_GPL(hd44780_common_alloc);

MODULE_LICENSE("GPL");
-
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 767bbda91744..3ff47d2c5691 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -1,6 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */

+#define DEFAULT_LCD_BWIDTH 40
+#define DEFAULT_LCD_HWIDTH 64
+
struct hd44780_common {
+ int bwidth; /* Default set by hd44780_alloc() */
+ int hwidth; /* Default set by hd44780_alloc() */
void *hd44780;
};

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index c3a60e190a7a..cec6b729d668 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -299,8 +299,6 @@ static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];
#define DEFAULT_LCD_TYPE LCD_TYPE_OLD
#define DEFAULT_LCD_HEIGHT 2
#define DEFAULT_LCD_WIDTH 40
-#define DEFAULT_LCD_BWIDTH 40
-#define DEFAULT_LCD_HWIDTH 64
#define DEFAULT_LCD_CHARSET LCD_CHARSET_NORMAL
#define DEFAULT_LCD_PROTO LCD_PROTO_PARALLEL

@@ -813,10 +811,11 @@ static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
/* fills the display with spaces and resets X/Y */
static void lcd_clear_fast_s(struct charlcd *charlcd)
{
+ struct hd44780_common *hdc = charlcd->drvdata;
int pos;

spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * hdc->hwidth; pos++) {
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
lcd_send_serial(' ' & 0x0F);
lcd_send_serial((' ' >> 4) & 0x0F);
@@ -829,10 +828,11 @@ static void lcd_clear_fast_s(struct charlcd *charlcd)
/* fills the display with spaces and resets X/Y */
static void lcd_clear_fast_p8(struct charlcd *charlcd)
{
+ struct hd44780_common *hdc = charlcd->drvdata;
int pos;

spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * hdc->hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');

@@ -859,10 +859,11 @@ static void lcd_clear_fast_p8(struct charlcd *charlcd)
/* fills the display with spaces and resets X/Y */
static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
{
+ struct hd44780_common *hdc = charlcd->drvdata;
int pos;

spin_lock_irq(&pprt_lock);
- for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
+ for (pos = 0; pos < charlcd->height * hdc->hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');
udelay(60);
@@ -902,7 +903,7 @@ static void lcd_init(void)
if (!hdc)
return;

- charlcd = charlcd_alloc(0);
+ charlcd = charlcd_alloc();
if (!charlcd) {
kfree(hdc);
return;
@@ -917,8 +918,8 @@ static void lcd_init(void)
*/
charlcd->height = lcd_height;
charlcd->width = lcd_width;
- charlcd->bwidth = lcd_bwidth;
- charlcd->hwidth = lcd_hwidth;
+ hdc->bwidth = lcd_bwidth;
+ hdc->hwidth = lcd_hwidth;

switch (selected_lcd_type) {
case LCD_TYPE_OLD:
@@ -929,8 +930,8 @@ static void lcd_init(void)
lcd.pins.rs = PIN_AUTOLF;

charlcd->width = 40;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
case LCD_TYPE_KS0074:
@@ -942,8 +943,8 @@ static void lcd_init(void)
lcd.pins.da = PIN_D0;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 16;
+ hdc->bwidth = 40;
+ hdc->hwidth = 16;
charlcd->height = 2;
break;
case LCD_TYPE_NEXCOM:
@@ -955,8 +956,8 @@ static void lcd_init(void)
lcd.pins.rw = PIN_INITP;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
case LCD_TYPE_CUSTOM:
@@ -974,8 +975,8 @@ static void lcd_init(void)
lcd.pins.rs = PIN_SELECP;

charlcd->width = 16;
- charlcd->bwidth = 40;
- charlcd->hwidth = 64;
+ hdc->bwidth = 40;
+ hdc->hwidth = 64;
charlcd->height = 2;
break;
}
@@ -986,9 +987,9 @@ static void lcd_init(void)
if (lcd_width != NOT_SET)
charlcd->width = lcd_width;
if (lcd_bwidth != NOT_SET)
- charlcd->bwidth = lcd_bwidth;
+ hdc->bwidth = lcd_bwidth;
if (lcd_hwidth != NOT_SET)
- charlcd->hwidth = lcd_hwidth;
+ hdc->hwidth = lcd_hwidth;
if (lcd_charset != NOT_SET)
lcd.charset = lcd_charset;
if (lcd_proto != NOT_SET)
@@ -1009,10 +1010,10 @@ static void lcd_init(void)
/* this is used to catch wrong and default values */
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 (hdc->bwidth <= 0)
+ hdc->bwidth = DEFAULT_LCD_BWIDTH;
+ if (hdc->hwidth <= 0)
+ hdc->hwidth = DEFAULT_LCD_HWIDTH;
if (charlcd->height <= 0)
charlcd->height = DEFAULT_LCD_HEIGHT;

--
2.28.0

2020-09-21 14:50:50

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 26/32] auxdisplay: Move clear_fast to hd44780

From: Lars Poeschel <[email protected]>

Move device specific clear_fast to hd44780. The other hd44780 driver is
panel and it provides it's own implementations of clear_fast, so we do
not move to hd44780_common in this case.
This also provides a standard naive fallback implementation in charlcd
if a driver does not provide clear_fast.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 19 +++++++++++--------
drivers/auxdisplay/hd44780.c | 14 ++++++++++++++
2 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 0f0568a4bd35..ef448c42abbd 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -127,18 +127,21 @@ static void charlcd_print(struct charlcd *lcd, char c)

static void charlcd_clear_fast(struct charlcd *lcd)
{
- struct hd44780_common *hdc = lcd->drvdata;
- int pos;
-
- charlcd_home(lcd);
+ int x, y;

if (lcd->ops->clear_fast)
lcd->ops->clear_fast(lcd);
- else
- for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
- lcd->ops->print(lcd, ' ');
+ else {
+ for (y = 0; y < lcd->height; y++) {
+ lcd->addr.x = 0;
+ lcd->addr.y = y;
+ lcd->ops->gotoxy(lcd);
+ for (x = 0; x < lcd->width; x++)
+ lcd->ops->print(lcd, ' ');
+ }

- charlcd_home(lcd);
+ lcd->ops->home(lcd);
+ }
}

/*
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 2e5e7c993933..207ed23e02ce 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -38,6 +38,18 @@ struct hd44780 {
struct gpio_desc *pins[PIN_NUM];
};

+static void hd44780_clear_fast(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+ int pos;
+
+ hd44780_common_home(lcd);
+ for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
+ hd44780_common_print(lcd, ' ');
+
+ hd44780_common_home(lcd);
+}
+
static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
struct hd44780_common *hdc = lcd->drvdata;
@@ -125,6 +137,7 @@ static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio8 = {
+ .clear_fast = hd44780_clear_fast,
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
@@ -181,6 +194,7 @@ static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio4 = {
+ .clear_fast = hd44780_clear_fast,
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
--
2.28.0

2020-09-21 14:50:54

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 17/32] auxdisplay: Implement a hd44780_common_display

From: Lars Poeschel <[email protected]>

Implement a hd44780_common_display function to turn the whole display on
or off. The hd44780 drivers can use this and charlcd uses this through
its ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 ++++++
drivers/auxdisplay/charlcd.h | 2 ++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 26 ++++++++++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 40 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index e48287197732..9bd3c0f2c470 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -232,10 +232,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
switch (*esc) {
case 'D': /* Display ON */
priv->flags |= LCD_FLAG_D;
+ if (priv->flags != oldflags)
+ lcd->ops->display(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'd': /* Display OFF */
priv->flags &= ~LCD_FLAG_D;
+ if (priv->flags != oldflags)
+ lcd->ops->display(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case 'C': /* Cursor ON */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 6fd4a77f5794..ec739a25195e 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -60,6 +60,7 @@ struct charlcd {
* @init_display: Initialize the display.
* @shift_cursor: Shift cursor left or right one position.
* @shift_display: Shift whole display content left or right.
+ * @display: Turn display on or off.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -71,6 +72,7 @@ struct charlcd_ops {
int (*init_display)(struct charlcd *lcd);
int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
+ int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index a3b18bbc2e4a..0f76b812fa20 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -133,6 +133,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -183,6 +184,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 318ad81a39ed..d375a621c52d 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -195,6 +195,32 @@ int hd44780_common_shift_display(struct charlcd *lcd,
}
EXPORT_SYMBOL_GPL(hd44780_common_shift_display);

+static void hd44780_common_set_mode(struct hd44780_common *hdc)
+{
+ hdc->write_cmd(hdc,
+ LCD_CMD_DISPLAY_CTRL |
+ ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
+ LCD_CMD_DISPLAY_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
+ LCD_CMD_CURSOR_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
+ LCD_CMD_BLINK_ON : 0));
+}
+
+int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_D;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_D;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_display);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 7badb96cb8a9..1550fa77b956 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -24,5 +24,6 @@ int hd44780_common_shift_cursor(struct charlcd *lcd,
enum charlcd_shift_dir dir);
int hd44780_common_shift_display(struct charlcd *lcd,
enum charlcd_shift_dir dir);
+int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 7ebbfb338518..9cb895c74cfb 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -881,6 +881,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -892,6 +893,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -903,6 +905,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
+ .display = hd44780_common_display,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:51:04

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 02/32] auxdisplay: Introduce hd44780_common.[ch]

From: Lars Poeschel <[email protected]>

There is some hd44780 specific code in charlcd and this code is used by
multiple drivers. To make charlcd independent from this device specific
code this has to be moved to a place where the multiple drivers can
share their common code. This common place is now introduced as
hd44780_common.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/Kconfig | 20 ++++++++++++++
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/hd44780.c | 43 +++++++++++++++++++----------
drivers/auxdisplay/hd44780_common.c | 21 ++++++++++++++
drivers/auxdisplay/hd44780_common.h | 8 ++++++
drivers/auxdisplay/panel.c | 18 ++++++++++--
6 files changed, 94 insertions(+), 17 deletions(-)
create mode 100644 drivers/auxdisplay/hd44780_common.c
create mode 100644 drivers/auxdisplay/hd44780_common.h

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 81757eeded68..153fa426ae7d 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -14,12 +14,31 @@ 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
+ help
+ This is the base system for character based lcd displays.
+ It makes no sense to have this alone, you select your display driver
+ and if it needs the charlcd core, it will select it automatically.
+ This is some character lcd core interface that multiple drivers can
+ use.
+
+config HD44780_COMMON
+ tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST
+ help
+ This is a module with the common symbols for HD44780 (and compatibles)
+ displays. This is the code that multiple other modules use. It is not
+ useful alone. If you have some sort of HD44780 compatible display,
+ you very likely use this. It is selected automatically by selecting
+ your concrete display.
+
if AUXDISPLAY

config HD44780
tristate "HD44780 Character LCD support"
depends on GPIOLIB || COMPILE_TEST
select CHARLCD
+ select HD44780_COMMON
help
Enable support for Character LCDs using a HD44780 controller.
The LCD is accessible through the /dev/lcd char device (10, 156).
@@ -168,6 +187,7 @@ menuconfig PARPORT_PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
select CHARLCD
+ select HD44780_COMMON
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/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index cf54b5efb07e..7e8a8c3eb3c3 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -4,6 +4,7 @@
#

obj-$(CONFIG_CHARLCD) += charlcd.o
+obj-$(CONFIG_HD44780_COMMON) += hd44780_common.o
obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
obj-$(CONFIG_KS0108) += ks0108.o
obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 5982158557bb..271dba9cd108 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>

#include "charlcd.h"
+#include "hd44780_common.h"

enum hd44780_pin {
/* Order does matter due to writing to GPIO array subsets! */
@@ -179,8 +180,9 @@ static int hd44780_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
unsigned int i, base;
struct charlcd *lcd;
+ struct hd44780_common *hdc;
struct hd44780 *hd;
- int ifwidth, ret;
+ int ifwidth, ret = -ENOMEM;

/* Required pins */
ifwidth = gpiod_count(dev, "data");
@@ -198,31 +200,39 @@ static int hd44780_probe(struct platform_device *pdev)
return -EINVAL;
}

+ hdc = hd44780_common_alloc();
+ if (!hdc)
+ return -ENOMEM;
+
lcd = charlcd_alloc(sizeof(struct hd44780));
if (!lcd)
- return -ENOMEM;
+ goto fail1;

- hd = lcd->drvdata;
+ hd = kzalloc(sizeof(struct hd44780), GFP_KERNEL);
+ if (!hd)
+ goto fail2;

+ hdc->hd44780 = hd;
+ lcd->drvdata = hdc;
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;
+ goto fail3;
}
}

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;
+ goto fail3;
}

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;
+ goto fail3;
}

/* Optional pins */
@@ -230,24 +240,24 @@ static int hd44780_probe(struct platform_device *pdev)
GPIOD_OUT_LOW);
if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
- goto fail;
+ goto fail3;
}

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;
+ goto fail3;
}

/* Required properties */
ret = device_property_read_u32(dev, "display-height-chars",
&lcd->height);
if (ret)
- goto fail;
+ goto fail3;
ret = device_property_read_u32(dev, "display-width-chars", &lcd->width);
if (ret)
- goto fail;
+ goto fail3;

/*
* On displays with more than two rows, the internal buffer width is
@@ -264,13 +274,17 @@ static int hd44780_probe(struct platform_device *pdev)

ret = charlcd_register(lcd);
if (ret)
- goto fail;
+ goto fail3;

platform_set_drvdata(pdev, lcd);
return 0;

-fail:
- charlcd_free(lcd);
+fail3:
+ kfree(hd);
+fail2:
+ kfree(lcd);
+fail1:
+ kfree(hdc);
return ret;
}

@@ -278,9 +292,10 @@ static int hd44780_remove(struct platform_device *pdev)
{
struct charlcd *lcd = platform_get_drvdata(pdev);

+ kfree(lcd->drvdata);
charlcd_unregister(lcd);

- charlcd_free(lcd);
+ kfree(lcd);
return 0;
}

diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
new file mode 100644
index 000000000000..073f47397f7d
--- /dev/null
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "hd44780_common.h"
+
+struct hd44780_common *hd44780_common_alloc(void)
+{
+ struct hd44780_common *hd;
+
+ hd = kzalloc(sizeof(*hd), GFP_KERNEL);
+ if (!hd)
+ return NULL;
+
+ return hd;
+
+}
+EXPORT_SYMBOL_GPL(hd44780_common_alloc);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
new file mode 100644
index 000000000000..767bbda91744
--- /dev/null
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+struct hd44780_common {
+ void *hd44780;
+};
+
+struct hd44780_common *hd44780_common_alloc(void);
+
diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index de623ae219f1..c3a60e190a7a 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -56,6 +56,7 @@
#include <linux/uaccess.h>

#include "charlcd.h"
+#include "hd44780_common.h"

#define LCD_MAXBYTES 256 /* max burst write */

@@ -895,10 +896,20 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
static void lcd_init(void)
{
struct charlcd *charlcd;
+ struct hd44780_common *hdc;
+
+ hdc = hd44780_common_alloc();
+ if (!hdc)
+ return;

charlcd = charlcd_alloc(0);
- if (!charlcd)
+ if (!charlcd) {
+ kfree(hdc);
return;
+ }
+
+ hdc->hd44780 = &lcd;
+ charlcd->drvdata = hdc;

/*
* Init lcd struct with load-time values to preserve exact
@@ -1620,7 +1631,7 @@ static void panel_attach(struct parport *port)
if (lcd.enabled)
charlcd_unregister(lcd.charlcd);
err_unreg_device:
- charlcd_free(lcd.charlcd);
+ kfree(lcd.charlcd);
lcd.charlcd = NULL;
parport_unregister_device(pprt);
pprt = NULL;
@@ -1647,7 +1658,8 @@ static void panel_detach(struct parport *port)
if (lcd.enabled) {
charlcd_unregister(lcd.charlcd);
lcd.initialized = false;
- charlcd_free(lcd.charlcd);
+ kfree(lcd.charlcd->drvdata);
+ kfree(lcd.charlcd);
lcd.charlcd = NULL;
}

--
2.28.0

2020-09-21 14:51:08

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 33/33] auxdisplay: add a driver for lcd2s character display

From: Lars Poeschel <[email protected]>

This driver allows to use a lcd2s 20x4 character display from modtronix
engineering as an auxdisplay charlcd device.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/Kconfig | 11 +
drivers/auxdisplay/Makefile | 1 +
drivers/auxdisplay/lcd2s.c | 409 ++++++++++++++++++++++++++++++++++++
3 files changed, 421 insertions(+)
create mode 100644 drivers/auxdisplay/lcd2s.c

diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 153fa426ae7d..9c338af59589 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -173,6 +173,17 @@ config HT16K33
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
LED controller driver with keyscan.

+config LCD2S
+ tristate "lcd2s 20x4 character display over I2C console"
+ depends on I2C
+ select CHARLCD
+ default n
+ help
+ This is a driver that lets you use the lcd2s 20x4 character display
+ from modtronix engineering as a console output device. The display
+ is a simple single color character display. You have to connect it
+ to an I2C bus.
+
config ARM_CHARLCD
bool "ARM Ltd. Character LCD Driver"
depends on PLAT_VERSATILE
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index 7e8a8c3eb3c3..307771027c89 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
obj-$(CONFIG_HD44780) += hd44780.o
obj-$(CONFIG_HT16K33) += ht16k33.o
obj-$(CONFIG_PARPORT_PANEL) += panel.o
+obj-$(CONFIG_LCD2S) += lcd2s.o
diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c
new file mode 100644
index 000000000000..4b8c597a26fa
--- /dev/null
+++ b/drivers/auxdisplay/lcd2s.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * console driver for LCD2S 4x20 character displays connected through i2c.
+ * The display also has a spi interface, but the driver does not support
+ * this yet.
+ *
+ * This is a driver allowing you to use a LCD2S 4x20 from modtronix
+ * engineering as auxdisplay character device.
+ *
+ * (C) 2019 by Lemonage Software GmbH
+ * Author: Lars Pöschel <[email protected]>
+ * All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "charlcd.h"
+
+#define LCD2S_CMD_CUR_MOVES_FWD 0x09
+#define LCD2S_CMD_CUR_BLINK_OFF 0x10
+#define LCD2S_CMD_CUR_UL_OFF 0x11
+#define LCD2S_CMD_DISPLAY_OFF 0x12
+#define LCD2S_CMD_CUR_BLINK_ON 0x18
+#define LCD2S_CMD_CUR_UL_ON 0x19
+#define LCD2S_CMD_DISPLAY_ON 0x1a
+#define LCD2S_CMD_BACKLIGHT_OFF 0x20
+#define LCD2S_CMD_BACKLIGHT_ON 0x28
+#define LCD2S_CMD_WRITE 0x80
+#define LCD2S_CMD_MOV_CUR_RIGHT 0x83
+#define LCD2S_CMD_MOV_CUR_LEFT 0x84
+#define LCD2S_CMD_SHIFT_RIGHT 0x85
+#define LCD2S_CMD_SHIFT_LEFT 0x86
+#define LCD2S_CMD_SHIFT_UP 0x87
+#define LCD2S_CMD_SHIFT_DOWN 0x88
+#define LCD2S_CMD_CUR_ADDR 0x89
+#define LCD2S_CMD_CUR_POS 0x8a
+#define LCD2S_CMD_CUR_RESET 0x8b
+#define LCD2S_CMD_CLEAR 0x8c
+#define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92
+#define LCD2S_CMD_READ_STATUS 0xd0
+
+#define LCD2S_CHARACTER_SIZE 8
+
+#define LCD2S_STATUS_BUF_MASK 0x7f
+
+struct lcd2s_data {
+ struct i2c_client *i2c;
+ struct charlcd *charlcd;
+};
+
+static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count)
+{
+ s32 status;
+
+ status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS);
+ if (status < 0)
+ return status;
+
+ while ((status & LCD2S_STATUS_BUF_MASK) < count) {
+ mdelay(1);
+ status = i2c_smbus_read_byte_data(client,
+ LCD2S_CMD_READ_STATUS);
+ if (status < 0)
+ return status;
+ }
+ return 0;
+}
+
+static int lcd2s_i2c_master_send(const struct i2c_client *client,
+ const char *buf, int count)
+{
+ s32 status;
+
+ status = lcd2s_wait_buf_free(client, count);
+ if (status < 0)
+ return status;
+
+ return i2c_master_send(client, buf, count);
+}
+
+static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
+{
+ s32 status;
+
+ status = lcd2s_wait_buf_free(client, 1);
+ if (status < 0)
+ return status;
+
+ return i2c_smbus_write_byte(client, value);
+}
+
+static int lcd2s_print(struct charlcd *lcd, int c)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[2] = { LCD2S_CMD_WRITE, c };
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+ return 0;
+}
+
+static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[] = { LCD2S_CMD_CUR_POS, y + 1, x + 1};
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+
+ return 0;
+}
+
+static int lcd2s_home(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET);
+ return 0;
+}
+
+static int lcd2s_init_display(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ /* turn everything off, but display on */
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
+
+ return 0;
+}
+
+static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT);
+
+ return 0;
+}
+
+static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT);
+
+ return 0;
+}
+
+static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
+}
+
+static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF);
+
+ return 0;
+}
+
+static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
+
+ return 0;
+}
+
+static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ if (on)
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON);
+ else
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
+
+ return 0;
+}
+
+static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
+{
+ return 0;
+}
+
+static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines)
+{
+ return 0;
+}
+
+static int lcd2s_redefine_char(struct charlcd *lcd, char *esc)
+{
+ /* 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.
+ */
+
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+ u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR };
+ u8 value;
+ int shift, i;
+
+ if (!strchr(esc, ';'))
+ return 0;
+
+ esc++;
+
+ buf[1] = *(esc++) - '0';
+ if (buf[1] > 7)
+ return 1;
+
+ i = 0;
+ shift = 0;
+ value = 0;
+ while (*esc && i < LCD2S_CHARACTER_SIZE + 2) {
+ 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) {
+ buf[i++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
+ return 1;
+}
+
+static int lcd2s_clear_display(struct charlcd *lcd)
+{
+ struct lcd2s_data *lcd2s = lcd->drvdata;
+
+ /* This implicitly sets cursor to first row and column */
+ lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
+ return 0;
+}
+
+static const struct charlcd_ops lcd2s_ops = {
+ .print = lcd2s_print,
+ .backlight = lcd2s_backlight,
+ .gotoxy = lcd2s_gotoxy,
+ .home = lcd2s_home,
+ .clear_display = lcd2s_clear_display,
+ .init_display = lcd2s_init_display,
+ .shift_cursor = lcd2s_shift_cursor,
+ .shift_display = lcd2s_shift_display,
+ .display = lcd2s_display,
+ .cursor = lcd2s_cursor,
+ .blink = lcd2s_blink,
+ .fontsize = lcd2s_fontsize,
+ .lines = lcd2s_lines,
+ .redefine_char = lcd2s_redefine_char,
+};
+
+static int lcd2s_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct charlcd *lcd;
+ struct lcd2s_data *lcd2s;
+ int err;
+
+ if (!i2c_check_functionality(i2c->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ return -EIO;
+
+ /* Test, if the display is responding */
+ err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF);
+ if (err < 0)
+ return err;
+
+ lcd = charlcd_alloc();
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd2s = kzalloc(sizeof(struct lcd2s_data), GFP_KERNEL);
+ if (!lcd2s) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ lcd->drvdata = lcd2s;
+ lcd2s->i2c = i2c;
+ lcd2s->charlcd = lcd;
+
+ /* Required properties */
+ err = device_property_read_u32(&i2c->dev, "display-height-chars",
+ &lcd->height);
+ if (err)
+ goto fail2;
+
+ err = device_property_read_u32(&i2c->dev, "display-width-chars",
+ &lcd->width);
+ if (err)
+ goto fail2;
+
+ lcd->ops = &lcd2s_ops;
+
+ err = charlcd_register(lcd2s->charlcd);
+ if (err)
+ goto fail2;
+
+ i2c_set_clientdata(i2c, lcd2s);
+ return 0;
+
+fail2:
+ kfree(lcd2s);
+fail1:
+ kfree(lcd);
+ return err;
+}
+
+static int lcd2s_i2c_remove(struct i2c_client *i2c)
+{
+ struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c);
+
+ kfree(lcd2s->charlcd);
+ charlcd_unregister(lcd2s->charlcd);
+ return 0;
+}
+
+static const struct i2c_device_id lcd2s_i2c_id[] = {
+ { "lcd2s", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lcd2s_of_table[] = {
+ { .compatible = "modtronix,lcd2s" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lcd2s_of_table);
+#endif
+
+static struct i2c_driver lcd2s_i2c_driver = {
+ .driver = {
+ .name = "lcd2s",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(lcd2s_of_table),
+#endif
+ },
+ .probe = lcd2s_i2c_probe,
+ .remove = lcd2s_i2c_remove,
+ .id_table = lcd2s_i2c_id,
+};
+
+static int __init lcd2s_modinit(void)
+{
+ int ret = 0;
+
+ ret = i2c_add_driver(&lcd2s_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register lcd2s driver\n");
+
+ return ret;
+}
+module_init(lcd2s_modinit)
+
+static void __exit lcd2s_exit(void)
+{
+ i2c_del_driver(&lcd2s_i2c_driver);
+}
+module_exit(lcd2s_exit)
+
+MODULE_DESCRIPTION("LCD2S character display driver");
+MODULE_AUTHOR("Lars Poeschel");
+MODULE_LICENSE("GPL");
--
2.28.0

2020-09-21 14:51:09

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 30/32] auxdisplay: Change gotoxy calling interface

From: Lars Poeschel <[email protected]>

Change the calling interface for gotoxy from supplying the x and y
coordinates in the charlcd struct to explicitly supplying x and y in
the function arguments. This is more intuitive and allows for moving
the cursor to positions independent from the position saved in the
charlcd struct.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 10 +++++-----
drivers/auxdisplay/charlcd.h | 2 +-
drivers/auxdisplay/hd44780_common.c | 15 ++++++---------
drivers/auxdisplay/hd44780_common.h | 2 +-
4 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 78a61c3f1eb0..cd3a304c08ec 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -119,7 +119,7 @@ static void charlcd_print(struct charlcd *lcd, char c)

/* prevents the cursor from wrapping onto the next line */
if (lcd->addr.x == lcd->width)
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x - 1, lcd->addr.y);
}

static void charlcd_clear_fast(struct charlcd *lcd)
@@ -329,7 +329,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
/* restore cursor position */
lcd->addr.x = xs;
lcd->addr.y = ys;
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
processed = 1;
break;
}
@@ -353,7 +353,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)

/* If the command is valid, move to the new address */
if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);

/* Regardless of its validity, mark as processed */
processed = 1;
@@ -411,12 +411,12 @@ static void charlcd_write_char(struct charlcd *lcd, char c)

lcd->addr.x = 0;
lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
break;
case '\r':
/* go to the beginning of the same line */
lcd->addr.x = 0;
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
break;
case '\t':
/* print a space instead of the tab */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 83fa989d37fa..7cddcbba4f2a 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -80,7 +80,7 @@ struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
int (*print)(struct charlcd *lcd, int c);
- int (*gotoxy)(struct charlcd *lcd);
+ int (*gotoxy)(struct charlcd *lcd, unsigned int x, unsigned int y);
int (*home)(struct charlcd *lcd);
int (*clear_display)(struct charlcd *lcd);
int (*init_display)(struct charlcd *lcd);
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 46d86c9dbc44..89f202377ba5 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -48,7 +48,7 @@ int hd44780_common_print(struct charlcd *lcd, int c)
return 1;
}

-int hd44780_common_gotoxy(struct charlcd *lcd)
+int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
{
struct hd44780_common *hdc = lcd->drvdata;
unsigned int addr;
@@ -57,11 +57,10 @@ int hd44780_common_gotoxy(struct charlcd *lcd)
* we force the cursor to stay at the end of the
* line if it wants to go farther
*/
- addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
- : hdc->bwidth - 1;
- if (lcd->addr.y & 1)
+ addr = x < hdc->bwidth ? x & (hdc->hwidth - 1) : hdc->bwidth - 1;
+ if (y & 1)
addr += hdc->hwidth;
- if (lcd->addr.y & 2)
+ if (y & 2)
addr += hdc->bwidth;
hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
return 0;
@@ -70,9 +69,7 @@ EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);

int hd44780_common_home(struct charlcd *lcd)
{
- lcd->addr.x = 0;
- lcd->addr.y = 0;
- return hd44780_common_gotoxy(lcd);
+ return hd44780_common_gotoxy(lcd, 0, 0);
}
EXPORT_SYMBOL_GPL(hd44780_common_home);

@@ -346,7 +343,7 @@ int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
hdc->write_data(hdc, cgbytes[addr]);

/* ensures that we stop writing to CGRAM */
- lcd->ops->gotoxy(lcd);
+ lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
return 1;
}
EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index d3758230b7fd..7a7f316d15aa 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -16,7 +16,7 @@ struct hd44780_common {
};

int hd44780_common_print(struct charlcd *lcd, int c);
-int hd44780_common_gotoxy(struct charlcd *lcd);
+int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y);
int hd44780_common_home(struct charlcd *lcd);
int hd44780_common_clear_display(struct charlcd *lcd);
int hd44780_common_init_display(struct charlcd *lcd);
--
2.28.0

2020-09-21 14:51:16

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 05/32] auxdisplay: Move write_data pointer to hd44780_common

From: Lars Poeschel <[email protected]>

This moves the write_data function pointer from struct charlcd_ops to
struct hd44780_common. This is the function that actually writes the
character to the display. This hd44780 hardware specific function is
used by two drivers at the moment.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 12 ++++++------
drivers/auxdisplay/charlcd.h | 1 -
drivers/auxdisplay/hd44780.c | 16 +++++++++-------
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 12 ++++++------
5 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 59e0a815bf3d..df54078656c1 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -182,7 +182,7 @@ static void charlcd_print(struct charlcd *lcd, char c)
if (priv->addr.x < hdc->bwidth) {
if (lcd->char_conv)
c = lcd->char_conv[(unsigned char)c];
- lcd->ops->write_data(lcd, c);
+ hdc->write_data(hdc, c);
priv->addr.x++;

/* prevents the cursor from wrapping onto the next line */
@@ -202,7 +202,7 @@ static void charlcd_clear_fast(struct charlcd *lcd)
lcd->ops->clear_fast(lcd);
else
for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
- lcd->ops->write_data(lcd, ' ');
+ hdc->write_data(hdc, ' ');

charlcd_home(lcd);
}
@@ -446,7 +446,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
int x;

for (x = priv->addr.x; x < hdc->bwidth; x++)
- lcd->ops->write_data(lcd, ' ');
+ hdc->write_data(hdc, ' ');

/* restore cursor position */
charlcd_gotoxy(lcd);
@@ -505,7 +505,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)

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]);
+ hdc->write_data(hdc, cgbytes[addr]);

/* ensures that we stop writing to CGRAM */
charlcd_gotoxy(lcd);
@@ -587,7 +587,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
priv->addr.x--;
}
/* replace with a space */
- lcd->ops->write_data(lcd, ' ');
+ hdc->write_data(hdc, ' ');
/* back one char again */
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
break;
@@ -601,7 +601,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* go to the beginning of the next line
*/
for (; priv->addr.x < hdc->bwidth; priv->addr.x++)
- lcd->ops->write_data(lcd, ' ');
+ hdc->write_data(hdc, ' ');
priv->addr.x = 0;
priv->addr.y = (priv->addr.y + 1) % lcd->height;
charlcd_gotoxy(lcd);
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 5dce9dd36562..fba4f2cd42c4 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -27,7 +27,6 @@ struct charlcd {
struct charlcd_ops {
/* Required */
void (*write_cmd)(struct charlcd *lcd, int cmd);
- void (*write_data)(struct charlcd *lcd, int data);

/* Optional */
void (*write_cmd_raw4)(struct charlcd *lcd, int cmd); /* 4-bit only */
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f6786239c36f..922f0e0d2e6d 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -115,9 +115,8 @@ static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
}

/* Send data to the LCD panel in 8 bit GPIO mode */
-static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
+static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
{
- struct hd44780_common *hdc = lcd->drvdata;
struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, data, 1);
@@ -128,7 +127,6 @@ static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)

static const struct charlcd_ops hd44780_ops_gpio8 = {
.write_cmd = hd44780_write_cmd_gpio8,
- .write_data = hd44780_write_data_gpio8,
.backlight = hd44780_backlight,
};

@@ -163,9 +161,8 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
}

/* Send data to the LCD panel in 4 bit GPIO mode */
-static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
+static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
{
- struct hd44780_common *hdc = lcd->drvdata;
struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, data, 1);
@@ -177,7 +174,6 @@ static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
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,
};

@@ -276,7 +272,13 @@ static int hd44780_probe(struct platform_device *pdev)
device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);

hdc->ifwidth = ifwidth;
- lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
+ if (ifwidth == 8) {
+ lcd->ops = &hd44780_ops_gpio8;
+ hdc->write_data = hd44780_write_data_gpio8;
+ } else {
+ lcd->ops = &hd44780_ops_gpio4;
+ hdc->write_data = hd44780_write_data_gpio4;
+ }

ret = charlcd_register(lcd);
if (ret)
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 1100e0a32394..1d686c99b2c1 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -7,6 +7,7 @@ struct hd44780_common {
int ifwidth; /* 4-bit or 8-bit (default) */
int bwidth; /* Default set by hd44780_alloc() */
int hwidth; /* Default set by hd44780_alloc() */
+ void (*write_data)(struct hd44780_common *hdc, int data);
void *hd44780;
};

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index cec6b729d668..15100d21a6e9 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -734,7 +734,7 @@ static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
}

/* send data to the LCD panel in serial mode */
-static void lcd_write_data_s(struct charlcd *charlcd, int data)
+static void lcd_write_data_s(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
@@ -767,7 +767,7 @@ static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
}

/* send data to the LCD panel in 8 bits parallel mode */
-static void lcd_write_data_p8(struct charlcd *charlcd, int data)
+static void lcd_write_data_p8(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -799,7 +799,7 @@ static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
}

/* send data to the TI LCD panel */
-static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
+static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -874,21 +874,18 @@ static void lcd_clear_fast_tilcd(struct charlcd *charlcd)

static const 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,
};

static const 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,
};

static const 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,
};
@@ -1019,6 +1016,7 @@ static void lcd_init(void)

if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
charlcd->ops = &charlcd_serial_ops;
+ hdc->write_data = lcd_write_data_s;

if (lcd.pins.cl == PIN_NOT_SET)
lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1027,6 +1025,7 @@ static void lcd_init(void)

} else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
charlcd->ops = &charlcd_parallel_ops;
+ hdc->write_data = lcd_write_data_p8;

if (lcd.pins.e == PIN_NOT_SET)
lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1036,6 +1035,7 @@ static void lcd_init(void)
lcd.pins.rw = DEFAULT_LCD_PIN_RW;
} else {
charlcd->ops = &charlcd_tilcd_ops;
+ hdc->write_data = lcd_write_data_tilcd;
}

if (lcd.pins.bl == PIN_NOT_SET)
--
2.28.0

2020-09-21 14:51:25

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 22/32] auxdisplay: Implement hd44780_common_lines

From: Lars Poeschel <[email protected]>

This implements hd44780_common_lines to switch the display between one
and two lines mode. The hd44780 drivers use this function by adding it
to their ops structure and charlcd now calls through its ops function
pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 ++++++
drivers/auxdisplay/charlcd.h | 7 +++++++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 14 ++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 33 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index f4400a2c1ba5..d672e4c371e1 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -295,10 +295,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case 'n': /* One Line */
priv->flags &= ~LCD_FLAG_N;
+ if (priv->flags != oldflags)
+ lcd->ops->lines(lcd, CHARLCD_LINES_1);
+
processed = 1;
break;
case 'N': /* Two Lines */
priv->flags |= LCD_FLAG_N;
+ if (priv->flags != oldflags)
+ lcd->ops->lines(lcd, CHARLCD_LINES_2);
+
processed = 1;
break;
case 'l': /* Shift Cursor Left */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index ff223ed59bd9..5b0717c7482b 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -31,6 +31,11 @@ enum charlcd_fontsize {
CHARLCD_FONTSIZE_LARGE,
};

+enum charlcd_lines {
+ CHARLCD_LINES_1,
+ CHARLCD_LINES_2,
+};
+
struct charlcd {
const struct charlcd_ops *ops;
const unsigned char *char_conv; /* Optional */
@@ -68,6 +73,7 @@ struct charlcd {
* @display: Turn display on or off.
* @cursor: Turn cursor on or off.
* @blink: Turn cursor blink on or off.
+ * @lines: One or two lines.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -83,6 +89,7 @@ struct charlcd_ops {
int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size);
+ int (*lines)(struct charlcd *lcd, enum charlcd_lines lines);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 2b5f8984fcd4..7b7b28d72198 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -137,6 +137,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -191,6 +192,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index f2f58ae1a285..b079dd1e6865 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -274,6 +274,20 @@ int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
}
EXPORT_SYMBOL_GPL(hd44780_common_fontsize);

+int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (lines == CHARLCD_LINES_2)
+ hdc->hd44780_common_flags |= LCD_FLAG_N;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_N;
+
+ hd44780_common_set_function(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_lines);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 65d513efcc43..419065bfaea2 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -28,5 +28,6 @@ int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
+int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 6e13806af497..3d33d7cc979c 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -885,6 +885,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -900,6 +901,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -915,6 +917,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
+ .lines = hd44780_common_lines,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:51:32

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 18/32] auxdisplay: Implement hd44780_common_cursor

From: Lars Poeschel <[email protected]>

Implement a hd44780_common_cursor function to turn the cursor on and
off. The hd44780 drivers can use this function and charlcd calls it
through its ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 ++++++
drivers/auxdisplay/charlcd.h | 2 ++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 14 ++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 28 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 9bd3c0f2c470..a2f0becb1a9f 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -246,10 +246,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case 'C': /* Cursor ON */
priv->flags |= LCD_FLAG_C;
+ if (priv->flags != oldflags)
+ lcd->ops->cursor(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'c': /* Cursor OFF */
priv->flags &= ~LCD_FLAG_C;
+ if (priv->flags != oldflags)
+ lcd->ops->cursor(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case 'B': /* Blink ON */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index ec739a25195e..5aab359ebd91 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -61,6 +61,7 @@ struct charlcd {
* @shift_cursor: Shift cursor left or right one position.
* @shift_display: Shift whole display content left or right.
* @display: Turn display on or off.
+ * @cursor: Turn cursor on or off.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -73,6 +74,7 @@ struct charlcd_ops {
int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 0f76b812fa20..d5ce3955b577 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -134,6 +134,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -185,6 +186,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index d375a621c52d..af68eb5f3422 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -221,6 +221,20 @@ int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on)
}
EXPORT_SYMBOL_GPL(hd44780_common_display);

+int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_C;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_C;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_cursor);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 1550fa77b956..7327a9b2e997 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -25,5 +25,6 @@ int hd44780_common_shift_cursor(struct charlcd *lcd,
int hd44780_common_shift_display(struct charlcd *lcd,
enum charlcd_shift_dir dir);
int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 9cb895c74cfb..26fbf91c1501 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -882,6 +882,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -894,6 +895,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -906,6 +908,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.shift_cursor = hd44780_common_shift_cursor,
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
+ .cursor = hd44780_common_cursor,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:51:41

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 21/32] auxdisplay: Implement hd44780_common_fontsize

From: Lars Poeschel <[email protected]>

This implements hd44780_common_fontsize to switch between two fontsizes.
The hd44780 drivers can just set this function to their ops structure
and charlcd uses it through this ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 ++++++
drivers/auxdisplay/charlcd.h | 6 ++++++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 25 +++++++++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 43 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index c04aaa4d66a5..f4400a2c1ba5 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -281,10 +281,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case 'f': /* Small Font */
priv->flags &= ~LCD_FLAG_F;
+ if (priv->flags != oldflags)
+ lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_SMALL);
+
processed = 1;
break;
case 'F': /* Large Font */
priv->flags |= LCD_FLAG_F;
+ if (priv->flags != oldflags)
+ lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_LARGE);
+
processed = 1;
break;
case 'n': /* One Line */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index d9d907db2724..ff223ed59bd9 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -26,6 +26,11 @@ enum charlcd_shift_dir {
CHARLCD_SHIFT_RIGHT,
};

+enum charlcd_fontsize {
+ CHARLCD_FONTSIZE_SMALL,
+ CHARLCD_FONTSIZE_LARGE,
+};
+
struct charlcd {
const struct charlcd_ops *ops;
const unsigned char *char_conv; /* Optional */
@@ -77,6 +82,7 @@ struct charlcd_ops {
int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index a8d6e5483a92..2b5f8984fcd4 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -136,6 +136,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -189,6 +190,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index ecd2c2118318..f2f58ae1a285 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -249,6 +249,31 @@ int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on)
}
EXPORT_SYMBOL_GPL(hd44780_common_blink);

+static void hd44780_common_set_function(struct hd44780_common *hdc)
+{
+ hdc->write_cmd(hdc,
+ LCD_CMD_FUNCTION_SET |
+ ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
+ LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
+ LCD_CMD_TWO_LINES : 0));
+}
+
+int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (size == CHARLCD_FONTSIZE_LARGE)
+ hdc->hd44780_common_flags |= LCD_FLAG_F;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_F;
+
+ hd44780_common_set_function(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_fontsize);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index b46171f21a05..65d513efcc43 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -27,5 +27,6 @@ int hd44780_common_shift_display(struct charlcd *lcd,
int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 2d2f59360e1a..6e13806af497 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -884,6 +884,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -898,6 +899,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -912,6 +914,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
.blink = hd44780_common_blink,
+ .fontsize = hd44780_common_fontsize,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:52:00

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 23/32] auxdisplay: Remove unnecessary hd44780 from charlcd

From: Lars Poeschel <[email protected]>

The last two commits made another chunk of hd44780 code unnecessary
which we remove here.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index d672e4c371e1..29737c3e18f6 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -25,16 +25,8 @@
/* Keep the backlight on this many seconds for each flash */
#define LCD_BL_TEMPO_PERIOD 4

-/* LCD commands */
-#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 */

@@ -426,14 +418,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
if (oldflags == priv->flags)
return processed;

- if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
- hdc->write_cmd(hdc,
- LCD_CMD_FUNCTION_SET |
- ((hdc->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 */
- else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
+ if ((oldflags ^ priv->flags) & LCD_FLAG_L)
charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));

return processed;
--
2.28.0

2020-09-21 14:52:04

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 29/32] auxdisplay: charlcd: replace last device specific stuff

From: Lars Poeschel <[email protected]>

These are the last bits left in charlcd.c that are device specific and
they are removed now.
In detail this is:
* bwidth, which is the width of the display buffer per line. This is
replaced by width of the display.
* hwidth, which is the size of the display buffer as a whole. This is
replaced by looping all chars of a line by all lines.
* the hd44780_common header include can go away.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 0137e741c5bb..78a61c3f1eb0 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -20,7 +20,6 @@
#include <generated/utsrelease.h>

#include "charlcd.h"
-#include "hd44780_common.h"

/* Keep the backlight on this many seconds for each flash */
#define LCD_BL_TEMPO_PERIOD 4
@@ -112,8 +111,6 @@ static void charlcd_home(struct charlcd *lcd)

static void charlcd_print(struct charlcd *lcd, char c)
{
- struct hd44780_common *hdc = lcd->drvdata;
-
if (lcd->char_conv)
c = lcd->char_conv[(unsigned char)c];

@@ -121,7 +118,7 @@ static void charlcd_print(struct charlcd *lcd, char c)
lcd->addr.x++;

/* prevents the cursor from wrapping onto the next line */
- if (lcd->addr.x == hdc->bwidth)
+ if (lcd->addr.x == lcd->width)
lcd->ops->gotoxy(lcd);
}

@@ -199,7 +196,6 @@ static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
static inline int handle_lcd_special_code(struct charlcd *lcd)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
- struct hd44780_common *hdc = lcd->drvdata;

/* LCD special codes */

@@ -327,7 +323,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)

xs = lcd->addr.x;
ys = lcd->addr.y;
- for (x = lcd->addr.x; x < hdc->bwidth; x++)
+ for (x = lcd->addr.x; x < lcd->width; x++)
lcd->ops->print(lcd, ' ');

/* restore cursor position */
@@ -370,7 +366,6 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
static void charlcd_write_char(struct charlcd *lcd, char c)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
- struct hd44780_common *hdc = lcd->drvdata;

/* first, we'll test if we're in escape mode */
if ((c != '\n') && priv->esc_seq.len >= 0) {
@@ -411,7 +406,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
* flush the remainder of the current line and
* go to the beginning of the next line
*/
- for (; lcd->addr.x < hdc->bwidth; lcd->addr.x++)
+ for (; lcd->addr.x < lcd->width; lcd->addr.x++)
lcd->ops->print(lcd, ' ');

lcd->addr.x = 0;
--
2.28.0

2020-09-21 14:52:06

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 28/32] auxdisplay: hd44780: Remove clear_fast

From: Lars Poeschel <[email protected]>

We remove the hd44780_clear_fast (display) clear implementation. charlcd
will fall back to use hd44780_common_clear_display then, which is much
much faster.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/hd44780.c | 14 --------------
1 file changed, 14 deletions(-)

diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 207ed23e02ce..2e5e7c993933 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -38,18 +38,6 @@ struct hd44780 {
struct gpio_desc *pins[PIN_NUM];
};

-static void hd44780_clear_fast(struct charlcd *lcd)
-{
- struct hd44780_common *hdc = lcd->drvdata;
- int pos;
-
- hd44780_common_home(lcd);
- for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
- hd44780_common_print(lcd, ' ');
-
- hd44780_common_home(lcd);
-}
-
static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
struct hd44780_common *hdc = lcd->drvdata;
@@ -137,7 +125,6 @@ static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio8 = {
- .clear_fast = hd44780_clear_fast,
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
@@ -194,7 +181,6 @@ static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio4 = {
- .clear_fast = hd44780_clear_fast,
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
--
2.28.0

2020-09-21 14:52:07

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 24/32] auxdisplay: Move char redefine code to hd44780_common

From: Lars Poeschel <[email protected]>

Take the code to redefine characters out of charlcd and move it to
hd44780_common, as this is hd44780 specific.
There is now a function hd44780_common_redefine_char that drivers use
and charlcd calls it through its ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 60 +++------------------------
drivers/auxdisplay/charlcd.h | 2 +
drivers/auxdisplay/hd44780.c | 2 +
drivers/auxdisplay/hd44780_common.c | 63 +++++++++++++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 ++
6 files changed, 76 insertions(+), 55 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 29737c3e18f6..dca1b138a239 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -25,8 +25,6 @@
/* Keep the backlight on this many seconds for each flash */
#define LCD_BL_TEMPO_PERIOD 4

-#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
-
#define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
#define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */

@@ -344,61 +342,13 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
LCD_FLAG_C | LCD_FLAG_B;
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) {
+ case 'G':
+ if (lcd->ops->redefine_char)
+ processed = lcd->ops->redefine_char(lcd, esc);
+ else
processed = 1;
- break;
- }
-
- cgoffset = 0;
- shift = 0;
- value = 0;
- while (*esc && cgoffset < 8) {
- int half;
-
- shift ^= 4;
-
- half = hex_to_bin(*esc++);
- if (half < 0)
- continue;
-
- value |= half << shift;
- if (shift == 0) {
- cgbytes[cgoffset++] = value;
- value = 0;
- }
- }
-
- hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
- for (addr = 0; addr < cgoffset; addr++)
- hdc->write_data(hdc, cgbytes[addr]);
-
- /* ensures that we stop writing to CGRAM */
- lcd->ops->gotoxy(lcd);
- processed = 1;
break;
- }
+
case 'x': /* gotoxy : LxXXX[yYYY]; */
case 'y': /* gotoxy : LyYYY[xXXX]; */
if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 5b0717c7482b..83fa989d37fa 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -74,6 +74,7 @@ struct charlcd {
* @cursor: Turn cursor on or off.
* @blink: Turn cursor blink on or off.
* @lines: One or two lines.
+ * @redefine_char: Redefine the actual pixel matrix of character.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -90,6 +91,7 @@ struct charlcd_ops {
int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size);
int (*lines)(struct charlcd *lcd, enum charlcd_lines lines);
+ int (*redefine_char)(struct charlcd *lcd, char *esc);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 7b7b28d72198..2e5e7c993933 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -138,6 +138,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
.lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -193,6 +194,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
.lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index b079dd1e6865..46d86c9dbc44 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -26,6 +26,8 @@
#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 */

/* sleeps that many milliseconds with a reschedule */
@@ -288,6 +290,67 @@ int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines)
}
EXPORT_SYMBOL_GPL(hd44780_common_lines);

+int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
+{
+ /* 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.
+ */
+
+ struct hd44780_common *hdc = lcd->drvdata;
+ unsigned char cgbytes[8];
+ unsigned char cgaddr;
+ int cgoffset;
+ int shift;
+ char value;
+ int addr;
+
+ if (!strchr(esc, ';'))
+ return 0;
+
+ esc++;
+
+ cgaddr = *(esc++) - '0';
+ if (cgaddr > 7)
+ return 1;
+
+ 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 <= 'F') {
+ value |= (*esc - 'A' + 10) << shift;
+ } else if (*esc >= 'a' && *esc <= 'f') {
+ value |= (*esc - 'a' + 10) << shift;
+ } else {
+ esc++;
+ continue;
+ }
+
+ if (shift == 0) {
+ cgbytes[cgoffset++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+ for (addr = 0; addr < cgoffset; addr++)
+ hdc->write_data(hdc, cgbytes[addr]);
+
+ /* ensures that we stop writing to CGRAM */
+ lcd->ops->gotoxy(lcd);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 419065bfaea2..d3758230b7fd 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -29,5 +29,6 @@ int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines);
+int hd44780_common_redefine_char(struct charlcd *lcd, char *esc);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 3d33d7cc979c..b0d2ae5b9be8 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -886,6 +886,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
.lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -902,6 +903,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
.lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -918,6 +920,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.blink = hd44780_common_blink,
.fontsize = hd44780_common_fontsize,
.lines = hd44780_common_lines,
+ .redefine_char = hd44780_common_redefine_char,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:52:14

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 14/32] auxdisplay: Move init_display to hd44780_common

From: Lars Poeschel <[email protected]>

The init_display function is moved over to hd44780_common. charlcd uses
it via it's ops function pointer and drivers initialize the ops with the
common hd44780_common_init_display function.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 95 ++---------------------------
drivers/auxdisplay/charlcd.h | 9 +++
drivers/auxdisplay/hd44780.c | 2 +
drivers/auxdisplay/hd44780_common.c | 88 ++++++++++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 2 +
drivers/auxdisplay/panel.c | 3 +
6 files changed, 110 insertions(+), 89 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 9631f70e8128..94f6b0afab13 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -8,7 +8,6 @@

#include <linux/atomic.h>
#include <linux/ctype.h>
-#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -26,17 +25,7 @@
/* 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_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 */
@@ -84,12 +73,6 @@ struct charlcd_priv {
/* 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)
-{
- schedule_timeout_interruptible(msecs_to_jiffies(ms));
-}
-
/* turn the backlight on or off */
void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
@@ -177,76 +160,6 @@ static void charlcd_clear_fast(struct charlcd *lcd)
charlcd_home(lcd);
}

-static int charlcd_init_display(struct charlcd *lcd)
-{
- void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
- struct charlcd_priv *priv = charlcd_to_priv(lcd);
- struct hd44780_common *hdc = lcd->drvdata;
- u8 init;
-
- if (hdc->ifwidth != 4 && hdc->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 */
-
- /*
- * 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 (hdc->ifwidth == 4) {
- init >>= 4;
- write_cmd_raw = hdc->write_cmd_raw4;
- } else {
- write_cmd_raw = hdc->write_cmd;
- }
- write_cmd_raw(hdc, init);
- long_sleep(10);
- write_cmd_raw(hdc, init);
- long_sleep(10);
- write_cmd_raw(hdc, init);
- long_sleep(10);
-
- if (hdc->ifwidth == 4) {
- /* Switch to 4-bit mode, 1 line, small fonts */
- hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
- long_sleep(10);
- }
-
- /* set font height and lines number */
- hdc->write_cmd(hdc,
- LCD_CMD_FUNCTION_SET |
- ((hdc->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);
-
- /* display off, cursor off, blink off */
- hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
- long_sleep(10);
-
- hdc->write_cmd(hdc,
- 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 */
- hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
-
- lcd->ops->clear_display(lcd);
- lcd->addr.x = 0;
- lcd->addr.y = 0;
- return 0;
-}
-
/*
* Parses a movement command of the form "(.*);", where the group can be
* any number of subcommands of the form "(x|y)[0-9]+".
@@ -418,7 +331,9 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
}
case 'I': /* reinitialize display */
- charlcd_init_display(lcd);
+ lcd->ops->init_display(lcd);
+ priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+ LCD_FLAG_C | LCD_FLAG_B;
processed = 1;
break;
case 'G': {
@@ -727,6 +642,8 @@ static int charlcd_init(struct charlcd *lcd)
struct charlcd_priv *priv = charlcd_to_priv(lcd);
int ret;

+ priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
+ LCD_FLAG_C | LCD_FLAG_B;
if (lcd->ops->backlight) {
mutex_init(&priv->bl_tempo_lock);
INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
@@ -737,7 +654,7 @@ static int charlcd_init(struct charlcd *lcd)
* Since charlcd_init_display() needs to write data, we have to
* enable mark the LCD initialized just before.
*/
- ret = charlcd_init_display(lcd);
+ ret = lcd->ops->init_display(lcd);
if (ret)
return ret;

diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index e5b22e72fdc8..6281cf299a6d 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -9,6 +9,13 @@
#ifndef _CHARLCD_H
#define _CHARLCD_H

+#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 */
+
enum charlcd_onoff {
CHARLCD_OFF = 0,
CHARLCD_ON,
@@ -45,6 +52,7 @@ struct charlcd {
* @clear_display: Again clear the whole display, set the cursor to 0, 0. The
* values in addr.x and addr.y are set to 0, 0 by charlcd prior to calling this
* function.
+ * @init_display: Initialize the display.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -53,6 +61,7 @@ struct charlcd_ops {
int (*gotoxy)(struct charlcd *lcd);
int (*home)(struct charlcd *lcd);
int (*clear_display)(struct charlcd *lcd);
+ int (*init_display)(struct charlcd *lcd);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 40ea6d25dbe1..5916da09f738 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -130,6 +130,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -177,6 +178,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index fd638d2091e4..4f478ab66ed9 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -9,6 +9,19 @@
/* 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_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_DDRAM_ADDR 0x80 /* Set display data RAM address */

/* sleeps that many milliseconds with a reschedule */
@@ -69,6 +82,81 @@ int hd44780_common_clear_display(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(hd44780_common_clear_display);

+int hd44780_common_init_display(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
+ u8 init;
+
+ if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
+ return -EINVAL;
+
+ hdc->hd44780_common_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 */
+
+ /*
+ * 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 (hdc->ifwidth == 4) {
+ init >>= 4;
+ write_cmd_raw = hdc->write_cmd_raw4;
+ } else {
+ write_cmd_raw = hdc->write_cmd;
+ }
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+ write_cmd_raw(hdc, init);
+ long_sleep(10);
+
+ if (hdc->ifwidth == 4) {
+ /* Switch to 4-bit mode, 1 line, small fonts */
+ hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
+ long_sleep(10);
+ }
+
+ /* set font height and lines number */
+ hdc->write_cmd(hdc,
+ LCD_CMD_FUNCTION_SET |
+ ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
+ LCD_CMD_FONT_5X10_DOTS : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
+ LCD_CMD_TWO_LINES : 0));
+ long_sleep(10);
+
+ /* display off, cursor off, blink off */
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
+ long_sleep(10);
+
+ hdc->write_cmd(hdc,
+ LCD_CMD_DISPLAY_CTRL | /* set display mode */
+ ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
+ LCD_CMD_DISPLAY_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
+ LCD_CMD_CURSOR_ON : 0) |
+ ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
+ LCD_CMD_BLINK_ON : 0));
+
+ charlcd_backlight(lcd,
+ (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0);
+
+ long_sleep(10);
+
+ /* entry mode set : increment, cursor shifting */
+ hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+ hd44780_common_clear_display(lcd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_init_display);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 11ec4baf6997..cefd78d67675 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -7,6 +7,7 @@ struct hd44780_common {
int ifwidth; /* 4-bit or 8-bit (default) */
int bwidth; /* Default set by hd44780_alloc() */
int hwidth; /* Default set by hd44780_alloc() */
+ unsigned long hd44780_common_flags;
void (*write_data)(struct hd44780_common *hdc, int data);
void (*write_cmd)(struct hd44780_common *hdc, int cmd);
/* write_cmd_raw4 is for 4-bit connected displays only */
@@ -18,5 +19,6 @@ int hd44780_common_print(struct charlcd *lcd, int c);
int hd44780_common_gotoxy(struct charlcd *lcd);
int hd44780_common_home(struct charlcd *lcd);
int hd44780_common_clear_display(struct charlcd *lcd);
+int hd44780_common_init_display(struct charlcd *lcd);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 8adf627529f1..583bd22d3abd 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -878,6 +878,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -886,6 +887,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -894,6 +896,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
+ .init_display = hd44780_common_init_display,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:52:26

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 12/32] auxdisplay: make charlcd_backlight visible to hd44780_common

From: Lars Poeschel <[email protected]>

hd44780_common wants to use the charlcd_backlight function, so make it
visible.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 3 ++-
drivers/auxdisplay/charlcd.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index fc0daf4987d5..154419513186 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -91,7 +91,7 @@ static void long_sleep(int ms)
}

/* turn the backlight on or off */
-static void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
+void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);

@@ -103,6 +103,7 @@ static void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
lcd->ops->backlight(lcd, on);
mutex_unlock(&priv->bl_tempo_lock);
}
+EXPORT_SYMBOL_GPL(charlcd_backlight);

static void charlcd_bl_off(struct work_struct *work)
{
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 0b3dafbbae72..e5b22e72fdc8 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -55,6 +55,7 @@ struct charlcd_ops {
int (*clear_display)(struct charlcd *lcd);
};

+void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
struct charlcd *charlcd_alloc(void);
void charlcd_free(struct charlcd *lcd);

--
2.28.0

2020-09-21 14:52:46

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 25/32] auxdisplay: Call charlcd_backlight in place

From: Lars Poeschel <[email protected]>

This moves the call to charlcd_backlight from the end of the switch
into the actual case statement that originates the change of the
backlight. This is more consistent to what is now found in this switch.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index dca1b138a239..0f0568a4bd35 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -259,10 +259,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case '+': /* Back light ON */
priv->flags |= LCD_FLAG_L;
+ if (priv->flags != oldflags)
+ charlcd_backlight(lcd, CHARLCD_ON);
+
processed = 1;
break;
case '-': /* Back light OFF */
priv->flags &= ~LCD_FLAG_L;
+ if (priv->flags != oldflags)
+ charlcd_backlight(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case '*': /* Flash back light */
@@ -363,14 +369,6 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
}

- /* TODO: This indent party here got ugly, clean it! */
- /* Check whether one flag was changed */
- if (oldflags == priv->flags)
- return processed;
-
- if ((oldflags ^ priv->flags) & LCD_FLAG_L)
- charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
-
return processed;
}

--
2.28.0

2020-09-21 14:52:49

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 06/32] auxdisplay: Move write_cmd pointers to hd44780 drivers

From: Lars Poeschel <[email protected]>

The write_cmd function is used to send commands to hd44780 displays.
The individual hd44780 drivers then implement their appropriate way of
doing this with their supported displays. So we move this pointer so
hd44780_common.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 45 +++++++++++++++--------------
drivers/auxdisplay/charlcd.h | 5 ----
drivers/auxdisplay/hd44780.c | 15 ++++------
drivers/auxdisplay/hd44780_common.h | 3 ++
drivers/auxdisplay/panel.c | 12 ++++----
5 files changed, 38 insertions(+), 42 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index df54078656c1..ce6622f71c34 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -162,7 +162,7 @@ static void charlcd_gotoxy(struct charlcd *lcd)
addr += hdc->hwidth;
if (priv->addr.y & 2)
addr += hdc->bwidth;
- lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
+ hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
}

static void charlcd_home(struct charlcd *lcd)
@@ -211,8 +211,9 @@ static void charlcd_clear_fast(struct charlcd *lcd)
static void charlcd_clear_display(struct charlcd *lcd)
{
struct charlcd_priv *priv = charlcd_to_priv(lcd);
+ struct hd44780_common *hdc = lcd->drvdata;

- lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
priv->addr.x = 0;
priv->addr.y = 0;
/* we must wait a few milliseconds (15) */
@@ -221,7 +222,7 @@ 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);
+ void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
struct charlcd_priv *priv = charlcd_to_priv(lcd);
struct hd44780_common *hdc = lcd->drvdata;
u8 init;
@@ -241,25 +242,25 @@ static int charlcd_init_display(struct charlcd *lcd)
init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
if (hdc->ifwidth == 4) {
init >>= 4;
- write_cmd_raw = lcd->ops->write_cmd_raw4;
+ write_cmd_raw = hdc->write_cmd_raw4;
} else {
- write_cmd_raw = lcd->ops->write_cmd;
+ write_cmd_raw = hdc->write_cmd;
}
- write_cmd_raw(lcd, init);
+ write_cmd_raw(hdc, init);
long_sleep(10);
- write_cmd_raw(lcd, init);
+ write_cmd_raw(hdc, init);
long_sleep(10);
- write_cmd_raw(lcd, init);
+ write_cmd_raw(hdc, init);
long_sleep(10);

if (hdc->ifwidth == 4) {
/* Switch to 4-bit mode, 1 line, small fonts */
- lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
+ hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
long_sleep(10);
}

/* set font height and lines number */
- lcd->ops->write_cmd(lcd,
+ hdc->write_cmd(hdc,
LCD_CMD_FUNCTION_SET |
((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
@@ -267,10 +268,10 @@ static int charlcd_init_display(struct charlcd *lcd)
long_sleep(10);

/* display off, cursor off, blink off */
- lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
long_sleep(10);

- lcd->ops->write_cmd(lcd,
+ hdc->write_cmd(hdc,
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) |
@@ -281,7 +282,7 @@ static int charlcd_init_display(struct charlcd *lcd)
long_sleep(10);

/* entry mode set : increment, cursor shifting */
- lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+ hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);

charlcd_clear_display(lcd);
return 0;
@@ -417,7 +418,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
if (priv->addr.x > 0) {
/* back one char if not at end of line */
if (priv->addr.x < hdc->bwidth)
- lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT);
priv->addr.x--;
}
processed = 1;
@@ -426,18 +427,18 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
if (priv->addr.x < lcd->width) {
/* allow the cursor to pass the end of the line */
if (priv->addr.x < (hdc->bwidth - 1))
- lcd->ops->write_cmd(lcd,
+ hdc->write_cmd(hdc,
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);
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
processed = 1;
break;
case 'R': /* shift display right */
- lcd->ops->write_cmd(lcd,
+ hdc->write_cmd(hdc,
LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
LCD_CMD_SHIFT_RIGHT);
processed = 1;
@@ -503,7 +504,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
}
}

- lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+ hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
for (addr = 0; addr < cgoffset; addr++)
hdc->write_data(hdc, cgbytes[addr]);

@@ -535,14 +536,14 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
if ((oldflags ^ priv->flags) &
(LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
/* set display mode */
- lcd->ops->write_cmd(lcd,
+ hdc->write_cmd(hdc,
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,
+ hdc->write_cmd(hdc,
LCD_CMD_FUNCTION_SET |
((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
@@ -583,13 +584,13 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
*/
if (priv->addr.x < hdc->bwidth)
/* back one char */
- lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT);
priv->addr.x--;
}
/* replace with a space */
hdc->write_data(hdc, ' ');
/* back one char again */
- lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT);
break;
case '\f':
/* quickly clear the display */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index fba4f2cd42c4..ad6fd2733523 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -25,11 +25,6 @@ struct charlcd {
};

struct charlcd_ops {
- /* Required */
- void (*write_cmd)(struct charlcd *lcd, int cmd);
-
- /* Optional */
- void (*write_cmd_raw4)(struct charlcd *lcd, int cmd); /* 4-bit only */
void (*clear_fast)(struct charlcd *lcd);
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
};
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 922f0e0d2e6d..dc4738563298 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -103,9 +103,8 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
}

/* Send a command to the LCD panel in 8 bit GPIO mode */
-static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
+static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd)
{
- struct hd44780_common *hdc = lcd->drvdata;
struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio8(hd, cmd, 0);
@@ -126,14 +125,12 @@ static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio8 = {
- .write_cmd = hd44780_write_cmd_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)
+static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd)
{
- struct hd44780_common *hdc = lcd->drvdata;
struct hd44780 *hd = hdc->hd44780;

hd44780_write_gpio4(hd, cmd, 0);
@@ -143,10 +140,9 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
}

/* 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)
+static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd)
{
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
- struct hd44780_common *hdc = lcd->drvdata;
struct hd44780 *hd = hdc->hd44780;
unsigned int n;

@@ -172,8 +168,6 @@ static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
}

static const struct charlcd_ops hd44780_ops_gpio4 = {
- .write_cmd = hd44780_write_cmd_gpio4,
- .write_cmd_raw4 = hd44780_write_cmd_raw_gpio4,
.backlight = hd44780_backlight,
};

@@ -275,9 +269,12 @@ static int hd44780_probe(struct platform_device *pdev)
if (ifwidth == 8) {
lcd->ops = &hd44780_ops_gpio8;
hdc->write_data = hd44780_write_data_gpio8;
+ hdc->write_cmd = hd44780_write_cmd_gpio8;
} else {
lcd->ops = &hd44780_ops_gpio4;
hdc->write_data = hd44780_write_data_gpio4;
+ hdc->write_cmd = hd44780_write_cmd_gpio4;
+ hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4;
}

ret = charlcd_register(lcd);
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 1d686c99b2c1..6c38ddf8f2ce 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -8,6 +8,9 @@ struct hd44780_common {
int bwidth; /* Default set by hd44780_alloc() */
int hwidth; /* Default set by hd44780_alloc() */
void (*write_data)(struct hd44780_common *hdc, int data);
+ void (*write_cmd)(struct hd44780_common *hdc, int cmd);
+ /* write_cmd_raw4 is for 4-bit connected displays only */
+ void (*write_cmd_raw4)(struct hd44780_common *hdc, int cmd);
void *hd44780;
};

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 15100d21a6e9..7ef9bc7188ca 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -723,7 +723,7 @@ static void lcd_backlight(struct charlcd *charlcd, enum charlcd_onoff on)
}

/* send a command to the LCD panel in serial mode */
-static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_s(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
lcd_send_serial(0x1F); /* R/W=W, RS=0 */
@@ -745,7 +745,7 @@ static void lcd_write_data_s(struct hd44780_common *hdc, int data)
}

/* send a command to the LCD panel in 8 bits parallel mode */
-static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the data port */
@@ -789,7 +789,7 @@ static void lcd_write_data_p8(struct hd44780_common *hdc, int data)
}

/* send a command to the TI LCD panel */
-static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
+static void lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd)
{
spin_lock_irq(&pprt_lock);
/* present the data to the control port */
@@ -873,19 +873,16 @@ static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
}

static const struct charlcd_ops charlcd_serial_ops = {
- .write_cmd = lcd_write_cmd_s,
.clear_fast = lcd_clear_fast_s,
.backlight = lcd_backlight,
};

static const struct charlcd_ops charlcd_parallel_ops = {
- .write_cmd = lcd_write_cmd_p8,
.clear_fast = lcd_clear_fast_p8,
.backlight = lcd_backlight,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
- .write_cmd = lcd_write_cmd_tilcd,
.clear_fast = lcd_clear_fast_tilcd,
.backlight = lcd_backlight,
};
@@ -1017,6 +1014,7 @@ static void lcd_init(void)
if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
charlcd->ops = &charlcd_serial_ops;
hdc->write_data = lcd_write_data_s;
+ hdc->write_cmd = lcd_write_cmd_s;

if (lcd.pins.cl == PIN_NOT_SET)
lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
@@ -1026,6 +1024,7 @@ static void lcd_init(void)
} else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
charlcd->ops = &charlcd_parallel_ops;
hdc->write_data = lcd_write_data_p8;
+ hdc->write_cmd = lcd_write_cmd_p8;

if (lcd.pins.e == PIN_NOT_SET)
lcd.pins.e = DEFAULT_LCD_PIN_E;
@@ -1036,6 +1035,7 @@ static void lcd_init(void)
} else {
charlcd->ops = &charlcd_tilcd_ops;
hdc->write_data = lcd_write_data_tilcd;
+ hdc->write_cmd = lcd_write_cmd_tilcd;
}

if (lcd.pins.bl == PIN_NOT_SET)
--
2.28.0

2020-09-21 14:52:49

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 10/32] auxdisplay: add home to charlcd_ops

From: Lars Poeschel <[email protected]>

This adds a home function to the charlcd_ops struct and offer an
implementation for hd44780_common. This implementation is used by our
two hd44780 drivers. This is to make charlcd device independent.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 2 +-
drivers/auxdisplay/charlcd.h | 3 +++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 8 ++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index d6f971eea6ae..44dd6e02eaf9 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -144,7 +144,7 @@ static void charlcd_home(struct charlcd *lcd)
{
lcd->addr.x = 0;
lcd->addr.y = 0;
- lcd->ops->gotoxy(lcd);
+ lcd->ops->home(lcd);
}

static void charlcd_print(struct charlcd *lcd, char c)
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 04fd241fe6d6..12c348c1a0ae 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -40,12 +40,15 @@ struct charlcd {
* wrap to the next line at the end of a line.
* @gotoxy: Set cursor to x, y. The x and y values to set the cursor to are
* previously set in addr.x and addr.y by charlcd.
+ * @home: Set cursor to 0, 0. The values in addr.x and addr.y are set to 0, 0 by
+ * charlcd prior to calling this function.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
int (*print)(struct charlcd *lcd, int c);
int (*gotoxy)(struct charlcd *lcd);
+ int (*home)(struct charlcd *lcd);
};

struct charlcd *charlcd_alloc(void);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 4d9478f6e5ff..b0893ea49165 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -128,6 +128,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -173,6 +174,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.backlight = hd44780_backlight,
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index e04c81bbd333..e659f16e9139 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -40,6 +40,14 @@ int hd44780_common_gotoxy(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);

+int hd44780_common_home(struct charlcd *lcd)
+{
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
+ return hd44780_common_gotoxy(lcd);
+}
+EXPORT_SYMBOL_GPL(hd44780_common_home);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 0bee6c22761e..1365484963d8 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -16,5 +16,6 @@ struct hd44780_common {

int hd44780_common_print(struct charlcd *lcd, int c);
int hd44780_common_gotoxy(struct charlcd *lcd);
+int hd44780_common_home(struct charlcd *lcd);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 75894eacd12f..b1e874f07456 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -876,18 +876,21 @@ static const struct charlcd_ops charlcd_serial_ops = {
.clear_fast = lcd_clear_fast_s,
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
};

static const struct charlcd_ops charlcd_parallel_ops = {
.clear_fast = lcd_clear_fast_p8,
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
.clear_fast = lcd_clear_fast_tilcd,
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
+ .home = hd44780_common_home,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:52:52

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 09/32] auxdisplay: provide hd44780_common_gotoxy

From: Lars Poeschel <[email protected]>

Provide a hd44780_common_gotoxy function and a pointer in the ops for
charlcd to use to move the cursor.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 38 +++++++++--------------------
drivers/auxdisplay/charlcd.h | 4 ++-
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 23 +++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 43 insertions(+), 28 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 72ed004a8980..d6f971eea6ae 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -55,7 +55,7 @@

#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_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 */
@@ -140,33 +140,17 @@ void charlcd_poke(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(charlcd_poke);

-static void charlcd_gotoxy(struct charlcd *lcd)
-{
- struct hd44780_common *hdc = lcd->drvdata;
- unsigned int addr;
-
- /*
- * we force the cursor to stay at the end of the
- * line if it wants to go farther
- */
- addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
- : hdc->bwidth - 1;
- if (lcd->addr.y & 1)
- addr += hdc->hwidth;
- if (lcd->addr.y & 2)
- addr += hdc->bwidth;
- hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
-}
-
static void charlcd_home(struct charlcd *lcd)
{
lcd->addr.x = 0;
lcd->addr.y = 0;
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);
}

static void charlcd_print(struct charlcd *lcd, char c)
{
+ struct hd44780_common *hdc = lcd->drvdata;
+
if (lcd->char_conv)
c = lcd->char_conv[(unsigned char)c];

@@ -174,8 +158,8 @@ static void charlcd_print(struct charlcd *lcd, char c)
lcd->addr.x++;

/* prevents the cursor from wrapping onto the next line */
- if (lcd->addr.x == lcd->width)
- charlcd_gotoxy(lcd);
+ if (lcd->addr.x == hdc->bwidth)
+ lcd->ops->gotoxy(lcd);
}

static void charlcd_clear_fast(struct charlcd *lcd)
@@ -440,7 +424,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
/* restore cursor position */
lcd->addr.x = xs;
lcd->addr.y = ys;
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);
processed = 1;
break;
}
@@ -499,7 +483,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
hdc->write_data(hdc, cgbytes[addr]);

/* ensures that we stop writing to CGRAM */
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);
processed = 1;
break;
}
@@ -510,7 +494,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)

/* If the command is valid, move to the new address */
if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);

/* Regardless of its validity, mark as processed */
processed = 1;
@@ -596,12 +580,12 @@ static void charlcd_write_char(struct charlcd *lcd, char c)

lcd->addr.x = 0;
lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);
break;
case '\r':
/* go to the beginning of the same line */
lcd->addr.x = 0;
- charlcd_gotoxy(lcd);
+ lcd->ops->gotoxy(lcd);
break;
case '\t':
/* print a space instead of the tab */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 874519f079b4..04fd241fe6d6 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -36,14 +36,16 @@ struct charlcd {
* @clear_fast: Clear the whole display and set cursor to position 0, 0.
* @backlight: Turn backlight on or off. Optional.
* @print: just Print one character to the display at current cursor position.
- * The cursor is advanced by charlcd.
* The buffered cursor position is advanced by charlcd. The cursor should not
* wrap to the next line at the end of a line.
+ * @gotoxy: Set cursor to x, y. The x and y values to set the cursor to are
+ * previously set in addr.x and addr.y by charlcd.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
int (*print)(struct charlcd *lcd, int c);
+ int (*gotoxy)(struct charlcd *lcd);
};

struct charlcd *charlcd_alloc(void);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 9680bb611639..4d9478f6e5ff 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -127,6 +127,7 @@ static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
static const struct charlcd_ops hd44780_ops_gpio8 = {
.backlight = hd44780_backlight,
.print = hd44780_common_print,
+ .gotoxy = hd44780_common_gotoxy,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -171,6 +172,7 @@ static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
static const struct charlcd_ops hd44780_ops_gpio4 = {
.backlight = hd44780_backlight,
.print = hd44780_common_print,
+ .gotoxy = hd44780_common_gotoxy,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index da296c5ad7a5..e04c81bbd333 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -5,6 +5,9 @@
#include "charlcd.h"
#include "hd44780_common.h"

+/* LCD commands */
+#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
+
int hd44780_common_print(struct charlcd *lcd, int c)
{
struct hd44780_common *hdc = lcd->drvdata;
@@ -17,6 +20,26 @@ int hd44780_common_print(struct charlcd *lcd, int c)
return 1;
}

+int hd44780_common_gotoxy(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+ unsigned int addr;
+
+ /*
+ * we force the cursor to stay at the end of the
+ * line if it wants to go farther
+ */
+ addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
+ : hdc->bwidth - 1;
+ if (lcd->addr.y & 1)
+ addr += hdc->hwidth;
+ if (lcd->addr.y & 2)
+ addr += hdc->bwidth;
+ hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 02d650903e35..0bee6c22761e 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -15,5 +15,6 @@ struct hd44780_common {
};

int hd44780_common_print(struct charlcd *lcd, int c);
+int hd44780_common_gotoxy(struct charlcd *lcd);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 7ef9bc7188ca..75894eacd12f 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -875,16 +875,19 @@ static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
static const struct charlcd_ops charlcd_serial_ops = {
.clear_fast = lcd_clear_fast_s,
.backlight = lcd_backlight,
+ .gotoxy = hd44780_common_gotoxy,
};

static const struct charlcd_ops charlcd_parallel_ops = {
.clear_fast = lcd_clear_fast_p8,
.backlight = lcd_backlight,
+ .gotoxy = hd44780_common_gotoxy,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
.clear_fast = lcd_clear_fast_tilcd,
.backlight = lcd_backlight,
+ .gotoxy = hd44780_common_gotoxy,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:53:04

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 19/32] auxdisplay: Implement hd44780_common_blink

From: Lars Poeschel <[email protected]>

Implement a hd44780_common_blink function to turn on or off the blinking
of the cursor. The hd44780 drivers just use this function and charlcd
calls it through its ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 ++++++
drivers/auxdisplay/charlcd.h | 2 ++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 14 ++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 28 insertions(+)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index a2f0becb1a9f..b099897e101b 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -260,10 +260,16 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case 'B': /* Blink ON */
priv->flags |= LCD_FLAG_B;
+ if (priv->flags != oldflags)
+ lcd->ops->blink(lcd, CHARLCD_ON);
+
processed = 1;
break;
case 'b': /* Blink OFF */
priv->flags &= ~LCD_FLAG_B;
+ if (priv->flags != oldflags)
+ lcd->ops->blink(lcd, CHARLCD_OFF);
+
processed = 1;
break;
case '+': /* Back light ON */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 5aab359ebd91..d9d907db2724 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -62,6 +62,7 @@ struct charlcd {
* @shift_display: Shift whole display content left or right.
* @display: Turn display on or off.
* @cursor: Turn cursor on or off.
+ * @blink: Turn cursor blink on or off.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -75,6 +76,7 @@ struct charlcd_ops {
int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
+ int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index d5ce3955b577..a8d6e5483a92 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -135,6 +135,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -187,6 +188,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index af68eb5f3422..ecd2c2118318 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -235,6 +235,20 @@ int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on)
}
EXPORT_SYMBOL_GPL(hd44780_common_cursor);

+int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (on == CHARLCD_ON)
+ hdc->hd44780_common_flags |= LCD_FLAG_B;
+ else
+ hdc->hd44780_common_flags &= ~LCD_FLAG_B;
+
+ hd44780_common_set_mode(hdc);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_blink);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 7327a9b2e997..b46171f21a05 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -26,5 +26,6 @@ int hd44780_common_shift_display(struct charlcd *lcd,
enum charlcd_shift_dir dir);
int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
+int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 26fbf91c1501..2d2f59360e1a 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -883,6 +883,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -896,6 +897,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -909,6 +911,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.shift_display = hd44780_common_shift_display,
.display = hd44780_common_display,
.cursor = hd44780_common_cursor,
+ .blink = hd44780_common_blink,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:53:15

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 20/32] auxdisplay: cleanup unnecessary hd44780 code in charlcd

From: Lars Poeschel <[email protected]>

This cleans up now unnecessary hd44780 specific code from charlcd. We
obsoleted this with the last three patches. So another chunk of hd44780
code can be dropped.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index b099897e101b..c04aaa4d66a5 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -26,11 +26,6 @@
#define LCD_BL_TEMPO_PERIOD 4

/* LCD commands */
-#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_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 */
@@ -419,17 +414,7 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
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 */
- hdc->write_cmd(hdc,
- 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))
+ if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
hdc->write_cmd(hdc,
LCD_CMD_FUNCTION_SET |
((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
--
2.28.0

2020-09-21 14:53:39

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 11/32] auxdisplay: Move clear_display to hd44780_common

From: Lars Poeschel <[email protected]>

This moves the clear_display function from charlcd to hd44780_common.
This is one more step to make charlcd independent from device specific
code. The two hd44780 drivers use the new function from hd44780_common
and charlcd calls this function through its function pointer in its ops
structure.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 22 ++++++----------------
drivers/auxdisplay/charlcd.h | 4 ++++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 21 +++++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 1 +
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 37 insertions(+), 16 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 44dd6e02eaf9..fc0daf4987d5 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -34,8 +34,6 @@
#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 */

@@ -178,18 +176,6 @@ static void charlcd_clear_fast(struct charlcd *lcd)
charlcd_home(lcd);
}

-/* clears the display and resets X/Y */
-static void charlcd_clear_display(struct charlcd *lcd)
-{
- struct hd44780_common *hdc = lcd->drvdata;
-
- hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
- lcd->addr.x = 0;
- lcd->addr.y = 0;
- /* we must wait a few milliseconds (15) */
- long_sleep(15);
-}
-
static int charlcd_init_display(struct charlcd *lcd)
{
void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
@@ -254,7 +240,9 @@ static int charlcd_init_display(struct charlcd *lcd)
/* entry mode set : increment, cursor shifting */
hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);

- charlcd_clear_display(lcd);
+ lcd->ops->clear_display(lcd);
+ lcd->addr.x = 0;
+ lcd->addr.y = 0;
return 0;
}

@@ -670,8 +658,10 @@ static int charlcd_open(struct inode *inode, struct file *file)
goto fail;

if (priv->must_clear) {
- charlcd_clear_display(&priv->lcd);
+ priv->lcd.ops->clear_display(&priv->lcd);
priv->must_clear = false;
+ priv->lcd.addr.x = 0;
+ priv->lcd.addr.y = 0;
}
return nonseekable_open(inode, file);

diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 12c348c1a0ae..0b3dafbbae72 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -42,6 +42,9 @@ struct charlcd {
* previously set in addr.x and addr.y by charlcd.
* @home: Set cursor to 0, 0. The values in addr.x and addr.y are set to 0, 0 by
* charlcd prior to calling this function.
+ * @clear_display: Again clear the whole display, set the cursor to 0, 0. The
+ * values in addr.x and addr.y are set to 0, 0 by charlcd prior to calling this
+ * function.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -49,6 +52,7 @@ struct charlcd_ops {
int (*print)(struct charlcd *lcd, int c);
int (*gotoxy)(struct charlcd *lcd);
int (*home)(struct charlcd *lcd);
+ int (*clear_display)(struct charlcd *lcd);
};

struct charlcd *charlcd_alloc(void);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index b0893ea49165..40ea6d25dbe1 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -129,6 +129,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -175,6 +176,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.print = hd44780_common_print,
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index e659f16e9139..fd638d2091e4 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -1,13 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/module.h>
+#include <linux/sched.h>
#include <linux/slab.h>

#include "charlcd.h"
#include "hd44780_common.h"

/* LCD commands */
+#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
+
#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */

+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+ schedule_timeout_interruptible(msecs_to_jiffies(ms));
+}
+
int hd44780_common_print(struct charlcd *lcd, int c)
{
struct hd44780_common *hdc = lcd->drvdata;
@@ -48,6 +57,18 @@ int hd44780_common_home(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(hd44780_common_home);

+/* clears the display and resets X/Y */
+int hd44780_common_clear_display(struct charlcd *lcd)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
+ /* we must wait a few milliseconds (15) */
+ long_sleep(15);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 1365484963d8..11ec4baf6997 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -17,5 +17,6 @@ struct hd44780_common {
int hd44780_common_print(struct charlcd *lcd, int c);
int hd44780_common_gotoxy(struct charlcd *lcd);
int hd44780_common_home(struct charlcd *lcd);
+int hd44780_common_clear_display(struct charlcd *lcd);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index b1e874f07456..8adf627529f1 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -877,6 +877,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -884,6 +885,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -891,6 +893,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.backlight = lcd_backlight,
.gotoxy = hd44780_common_gotoxy,
.home = hd44780_common_home,
+ .clear_display = hd44780_common_clear_display,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:54:06

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 16/32] auxdisplay: Implement hd44780_common_display_shift

From: Lars Poeschel <[email protected]>

Implement a hd44780_common_display_shift function for hd44780 drivers to
use. charlcd uses this through its ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 10 ++--------
drivers/auxdisplay/charlcd.h | 2 ++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 16 ++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 2 ++
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 85747e13bf48..e48287197732 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -31,10 +31,6 @@
#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 */
@@ -303,13 +299,11 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
processed = 1;
break;
case 'L': /* shift display left */
- hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT);
processed = 1;
break;
case 'R': /* shift display right */
- hdc->write_cmd(hdc,
- LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
- LCD_CMD_SHIFT_RIGHT);
+ lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT);
processed = 1;
break;
case 'k': { /* kill end of line */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 0a340beacd3e..6fd4a77f5794 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -59,6 +59,7 @@ struct charlcd {
* function.
* @init_display: Initialize the display.
* @shift_cursor: Shift cursor left or right one position.
+ * @shift_display: Shift whole display content left or right.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -69,6 +70,7 @@ struct charlcd_ops {
int (*clear_display)(struct charlcd *lcd);
int (*init_display)(struct charlcd *lcd);
int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
+ int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 6ffeeb3fb0d0..a3b18bbc2e4a 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -132,6 +132,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -181,6 +182,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index b2ed00635766..318ad81a39ed 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -18,6 +18,7 @@
#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 */
@@ -179,6 +180,21 @@ int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
}
EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor);

+int hd44780_common_shift_display(struct charlcd *lcd,
+ enum charlcd_shift_dir dir)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ else if (dir == CHARLCD_SHIFT_RIGHT)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+ LCD_CMD_SHIFT_RIGHT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_shift_display);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index 432fc37213cf..7badb96cb8a9 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -22,5 +22,7 @@ int hd44780_common_clear_display(struct charlcd *lcd);
int hd44780_common_init_display(struct charlcd *lcd);
int hd44780_common_shift_cursor(struct charlcd *lcd,
enum charlcd_shift_dir dir);
+int hd44780_common_shift_display(struct charlcd *lcd,
+ enum charlcd_shift_dir dir);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 4275223080e8..7ebbfb338518 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -880,6 +880,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -890,6 +891,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -900,6 +902,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
.shift_cursor = hd44780_common_shift_cursor,
+ .shift_display = hd44780_common_shift_display,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:54:06

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 15/32] auxdisplay: implement hd44780_common_shift_cursor

From: Lars Poeschel <[email protected]>

Implement a hd44780_common_shift_cursor function for drivers to use and
make charlcd use this function through ops function pointer.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 33 ++++++++++++-----------------
drivers/auxdisplay/charlcd.h | 7 ++++++
drivers/auxdisplay/hd44780.c | 2 ++
drivers/auxdisplay/hd44780_common.c | 22 +++++++++++++++++++
drivers/auxdisplay/hd44780_common.h | 2 ++
drivers/auxdisplay/panel.c | 3 +++
6 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 94f6b0afab13..85747e13bf48 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -288,21 +288,18 @@ static inline int handle_lcd_special_code(struct charlcd *lcd)
break;
case 'l': /* Shift Cursor Left */
if (lcd->addr.x > 0) {
- /* back one char if not at end of line */
- if (lcd->addr.x < hdc->bwidth)
- hdc->write_cmd(hdc, LCD_CMD_SHIFT);
- lcd->addr.x--;
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
+ 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 < (hdc->bwidth - 1))
- hdc->write_cmd(hdc,
- LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
- lcd->addr.x++;
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT))
+ lcd->addr.x++;
}
+
processed = 1;
break;
case 'L': /* shift display left */
@@ -456,19 +453,17 @@ static void charlcd_write_char(struct charlcd *lcd, char c)
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 < hdc->bwidth)
- /* back one char */
- hdc->write_cmd(hdc, LCD_CMD_SHIFT);
- lcd->addr.x--;
+ /* back one char */
+ if (!lcd->ops->shift_cursor(lcd,
+ CHARLCD_SHIFT_LEFT))
+ lcd->addr.x--;
}
/* replace with a space */
- hdc->write_data(hdc, ' ');
+ charlcd_print(lcd, ' ');
/* back one char again */
- hdc->write_cmd(hdc, LCD_CMD_SHIFT);
+ if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
+ lcd->addr.x--;
+
break;
case '\f':
/* quickly clear the display */
diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h
index 6281cf299a6d..0a340beacd3e 100644
--- a/drivers/auxdisplay/charlcd.h
+++ b/drivers/auxdisplay/charlcd.h
@@ -21,6 +21,11 @@ enum charlcd_onoff {
CHARLCD_ON,
};

+enum charlcd_shift_dir {
+ CHARLCD_SHIFT_LEFT,
+ CHARLCD_SHIFT_RIGHT,
+};
+
struct charlcd {
const struct charlcd_ops *ops;
const unsigned char *char_conv; /* Optional */
@@ -53,6 +58,7 @@ struct charlcd {
* values in addr.x and addr.y are set to 0, 0 by charlcd prior to calling this
* function.
* @init_display: Initialize the display.
+ * @shift_cursor: Shift cursor left or right one position.
*/
struct charlcd_ops {
void (*clear_fast)(struct charlcd *lcd);
@@ -62,6 +68,7 @@ struct charlcd_ops {
int (*home)(struct charlcd *lcd);
int (*clear_display)(struct charlcd *lcd);
int (*init_display)(struct charlcd *lcd);
+ int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
};

void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index 5916da09f738..6ffeeb3fb0d0 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -131,6 +131,7 @@ static const struct charlcd_ops hd44780_ops_gpio8 = {
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
};

/* Send a command to the LCD panel in 4 bit GPIO mode */
@@ -179,6 +180,7 @@ static const struct charlcd_ops hd44780_ops_gpio4 = {
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
};

static int hd44780_probe(struct platform_device *pdev)
diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c
index 4f478ab66ed9..b2ed00635766 100644
--- a/drivers/auxdisplay/hd44780_common.c
+++ b/drivers/auxdisplay/hd44780_common.c
@@ -17,6 +17,9 @@
#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_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 */
@@ -157,6 +160,25 @@ int hd44780_common_init_display(struct charlcd *lcd)
}
EXPORT_SYMBOL_GPL(hd44780_common_init_display);

+int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
+{
+ struct hd44780_common *hdc = lcd->drvdata;
+
+ if (dir == CHARLCD_SHIFT_LEFT) {
+ /* back one char if not at end of line */
+ if (lcd->addr.x < hdc->bwidth)
+ hdc->write_cmd(hdc, LCD_CMD_SHIFT);
+ } else if (dir == CHARLCD_SHIFT_RIGHT) {
+ /* allow the cursor to pass the end of the line */
+ if (lcd->addr.x < (hdc->bwidth - 1))
+ hdc->write_cmd(hdc,
+ LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor);
+
struct hd44780_common *hd44780_common_alloc(void)
{
struct hd44780_common *hd;
diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h
index cefd78d67675..432fc37213cf 100644
--- a/drivers/auxdisplay/hd44780_common.h
+++ b/drivers/auxdisplay/hd44780_common.h
@@ -20,5 +20,7 @@ int hd44780_common_gotoxy(struct charlcd *lcd);
int hd44780_common_home(struct charlcd *lcd);
int hd44780_common_clear_display(struct charlcd *lcd);
int hd44780_common_init_display(struct charlcd *lcd);
+int hd44780_common_shift_cursor(struct charlcd *lcd,
+ enum charlcd_shift_dir dir);
struct hd44780_common *hd44780_common_alloc(void);

diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c
index 583bd22d3abd..4275223080e8 100644
--- a/drivers/auxdisplay/panel.c
+++ b/drivers/auxdisplay/panel.c
@@ -879,6 +879,7 @@ static const struct charlcd_ops charlcd_serial_ops = {
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
};

static const struct charlcd_ops charlcd_parallel_ops = {
@@ -888,6 +889,7 @@ static const struct charlcd_ops charlcd_parallel_ops = {
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
};

static const struct charlcd_ops charlcd_tilcd_ops = {
@@ -897,6 +899,7 @@ static const struct charlcd_ops charlcd_tilcd_ops = {
.home = hd44780_common_home,
.clear_display = hd44780_common_clear_display,
.init_display = hd44780_common_init_display,
+ .shift_cursor = hd44780_common_shift_cursor,
};

/* initialize the LCD driver */
--
2.28.0

2020-09-21 14:54:32

by Lars Poeschel

[permalink] [raw]
Subject: [PATCH v2 13/32] auxdisplay: Make use of enum for backlight on / off

From: Lars Poeschel <[email protected]>

To turn the backlight on or off use our new enum CHARLCD_ON /
CHARLCD_OFF.

Signed-off-by: Lars Poeschel <[email protected]>
---
drivers/auxdisplay/charlcd.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c
index 154419513186..9631f70e8128 100644
--- a/drivers/auxdisplay/charlcd.c
+++ b/drivers/auxdisplay/charlcd.c
@@ -115,7 +115,7 @@ static void charlcd_bl_off(struct work_struct *work)
if (priv->bl_tempo) {
priv->bl_tempo = false;
if (!(priv->flags & LCD_FLAG_L))
- priv->lcd.ops->backlight(&priv->lcd, 0);
+ priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
}
mutex_unlock(&priv->bl_tempo_lock);
}
@@ -132,7 +132,7 @@ void charlcd_poke(struct charlcd *lcd)

mutex_lock(&priv->bl_tempo_lock);
if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
- lcd->ops->backlight(lcd, 1);
+ lcd->ops->backlight(lcd, CHARLCD_ON);
priv->bl_tempo = true;
schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
mutex_unlock(&priv->bl_tempo_lock);
@@ -829,7 +829,7 @@ int charlcd_unregister(struct charlcd *lcd)
the_charlcd = NULL;
if (lcd->ops->backlight) {
cancel_delayed_work_sync(&priv->bl_work);
- priv->lcd.ops->backlight(&priv->lcd, 0);
+ priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
}

return 0;
--
2.28.0

2020-09-21 15:32:42

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH v2 02/32] auxdisplay: Introduce hd44780_common.[ch]

Hi,

On 9/21/20 7:46 AM, [email protected] wrote:
> diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
> index 81757eeded68..153fa426ae7d 100644
> --- a/drivers/auxdisplay/Kconfig
> +++ b/drivers/auxdisplay/Kconfig
> @@ -14,12 +14,31 @@ 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
> + help
> + This is the base system for character based lcd displays.

character-based LCD

> + It makes no sense to have this alone, you select your display driver
> + and if it needs the charlcd core, it will select it automatically.
> + This is some character lcd core interface that multiple drivers can

LCD

> + use.
> +
> +config HD44780_COMMON
> + tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST
> + help
> + This is a module with the common symbols for HD44780 (and compatibles)
> + displays. This is the code that multiple other modules use. It is not
> + useful alone. If you have some sort of HD44780 compatible display,
> + you very likely use this. It is selected automatically by selecting
> + your concrete display.


thanks.
--
~Randy

2020-09-21 15:37:14

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH v2 33/33] auxdisplay: add a driver for lcd2s character display

On 9/21/20 7:46 AM, [email protected] wrote:
> diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
> index 153fa426ae7d..9c338af59589 100644
> --- a/drivers/auxdisplay/Kconfig
> +++ b/drivers/auxdisplay/Kconfig
> @@ -173,6 +173,17 @@ config HT16K33
> Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
> LED controller driver with keyscan.
>
> +config LCD2S
> + tristate "lcd2s 20x4 character display over I2C console"
> + depends on I2C
> + select CHARLCD
> + default n

Drop "default n". That is already the default.

> + help
> + This is a driver that lets you use the lcd2s 20x4 character display
> + from modtronix engineering as a console output device. The display

Modtronix

> + is a simple single color character display. You have to connect it
> + to an I2C bus.

thanks.
--
~Randy

2020-09-22 08:27:13

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v2 00/33] Make charlcd device independent

Hi Lars,

On Mon, Sep 21, 2020 at 04:46:12PM +0200, [email protected] wrote:
> This tries to make charlcd device independent. At the moment hd44780
> device specific code is contained deep in charlcd. This moves this out
> into a hd44780_common module, where the two hd44780 drivers we have at
> the moment (hd44780 and panel) can use this from. The goal is that at
> the end other drivers can use the charlcd interface.
> I add one such driver at the end with the last patch.
> I submitted this already some time ago [1], where the wish was so split
> this into smaller chunks what I try to do with this new patchset.
> Most of the patches pick one specific function in charlcd and move the
> device specific code into hd44780_common.

Regardless of my two comments, this series looks very clean to me, nice
job! For 1..32, feel free to add: Reviewed-by: Willy Tarreau <[email protected]>

Just be careful in your commit messages, I spotted a few "it's own"
instead of "its own", but that's a very minor detail :-)

Willy

2020-09-22 08:27:14

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v2 28/32] auxdisplay: hd44780: Remove clear_fast

Hi Lars,

On Mon, Sep 21, 2020 at 04:46:40PM +0200, [email protected] wrote:
> From: Lars Poeschel <[email protected]>
>
> We remove the hd44780_clear_fast (display) clear implementation. charlcd
> will fall back to use hd44780_common_clear_display then, which is much
> much faster.

I might have got confused, but it looks to me like patches 27 and 28
basically undo patch 26: in 26 you moved code to hd44780 and wrote a
fallback, just to later delete that code.

I seem to remember that the reason for the clear_fast() implementation
is that the default clear_display() is quite slow for small displays,
compared to what can be achieved by just filling the display with spaces
(in the order of tens of milliseconds vs hundreds of microseconds). But
I could be mistaken given how old this is, so please take my comment
with a grain of salt.

Willy

2020-09-22 08:27:15

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

On Mon, Sep 21, 2020 at 04:46:43PM +0200, [email protected] wrote:
> From: Lars Poeschel <[email protected]>
>
> Skip printing characters at the end of a display line. This fits to the
> behaviour we already had, that the cursor is nailed to last position of
> a line.

Just very old memories, but wasn't this used with the ability to shift
the display ? IIRC the HD44780 has a 2x64 chars buffer and you can make
the buffered characters appear when you shift the display. That's akin
to seeing the display as an adjustable window over the buffer. I don't
remember having used that feature, so if it didn't previously work, please
disregard my comment, I just want to be sure we don't break a feature
others might be relying on.

Willy

2020-09-22 08:51:13

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 28/32] auxdisplay: hd44780: Remove clear_fast

Hi Willy,

On Tue, Sep 22, 2020 at 07:22:27AM +0200, Willy Tarreau wrote:
> Hi Lars,
>
> On Mon, Sep 21, 2020 at 04:46:40PM +0200, [email protected] wrote:
> > From: Lars Poeschel <[email protected]>
> >
> > We remove the hd44780_clear_fast (display) clear implementation. charlcd
> > will fall back to use hd44780_common_clear_display then, which is much
> > much faster.
>
> I might have got confused, but it looks to me like patches 27 and 28
> basically undo patch 26: in 26 you moved code to hd44780 and wrote a
> fallback, just to later delete that code.

To be honest: I also got confused by this whole clear code and why are
there 3 different clear variants. ;-)
The reason why I did it this way is to show what happened to the
original code. The original code has a fallback that kind of does the
same like my naive implementation but it uses the fact, that on hd44780
displays the underlying buffer can be accessed in a linear manner to
speed things up a little. This then also clears the not visible area of
the buffer (which my naive implementation does not). To become
independent of hd44780 code at this point I had to do something.
I opted to for this naive implementation as a replacement for the old
optimized clear loop. The fallback was already there.
And then I did a separate step to remove it because I found it a bit
suboptimal. All current users have the clear command...

> I seem to remember that the reason for the clear_fast() implementation
> is that the default clear_display() is quite slow for small displays,
> compared to what can be achieved by just filling the display with spaces
> (in the order of tens of milliseconds vs hundreds of microseconds). But
> I could be mistaken given how old this is, so please take my comment
> with a grain of salt.

... well, and what the hd44780 controller does when it executes the
clear command is also writing a space character to all character
locations (also to the not visible ones). Probably very much the same
than the original fallback implementation.
For me issuing the clear command is faster by at least one order!
I have one of these cheap china displays with 4x20 characters. The flow
is like this: i2c -> gpio -> hd44780 in 4 bit mode. And gpio is issuing
a i2c request for EVERY SINGLE gpio. This is slow as hell. But it works.
:-)
As I said I also was a bit confused by the different clean
implementations, but the only real user of the clear_fast is the panel
driver. The hd44780 one I use did not provide a clear_fast.

So I would opt for the way I proposed in the patch series with the clear
command instead of the loop. And let the panel driver use its optimized
clear.

I am open about squashing things together if that is wanted, so we don't
have the step inbetween with the naive implementation and to not
confuse people. ;-)

Regards,
Lars

2020-09-22 09:22:46

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v2 28/32] auxdisplay: hd44780: Remove clear_fast

On Tue, Sep 22, 2020 at 10:49:12AM +0200, Lars Poeschel wrote:
> > I might have got confused, but it looks to me like patches 27 and 28
> > basically undo patch 26: in 26 you moved code to hd44780 and wrote a
> > fallback, just to later delete that code.
>
> To be honest: I also got confused by this whole clear code and why are
> there 3 different clear variants. ;-)
> The reason why I did it this way is to show what happened to the
> original code. The original code has a fallback that kind of does the
> same like my naive implementation but it uses the fact, that on hd44780
> displays the underlying buffer can be accessed in a linear manner to
> speed things up a little. This then also clears the not visible area of
> the buffer (which my naive implementation does not). To become
> independent of hd44780 code at this point I had to do something.
> I opted to for this naive implementation as a replacement for the old
> optimized clear loop. The fallback was already there.
> And then I did a separate step to remove it because I found it a bit
> suboptimal. All current users have the clear command...

I'm not contesting your naive implementation, it just looks to me that
patch 26 adds it while moving code that patch 27 removes, and patch 28
removes it. So I don't understand why not to remove it entirely in the
first place. It's possible I missed something related to other users of
that code but that wasn't clear from the patch nor the descriptions.

> > I seem to remember that the reason for the clear_fast() implementation
> > is that the default clear_display() is quite slow for small displays,
> > compared to what can be achieved by just filling the display with spaces
> > (in the order of tens of milliseconds vs hundreds of microseconds). But
> > I could be mistaken given how old this is, so please take my comment
> > with a grain of salt.
>
> ... well, and what the hd44780 controller does when it executes the
> clear command is also writing a space character to all character
> locations (also to the not visible ones). Probably very much the same
> than the original fallback implementation.

I've just checked on some old datasheets, and indeed the Display clear
command takes up to 1.6 ms, which looks very reasonable. But the charlcd
code currently sleeps 15 ms, which is 10 times more than needed. I've
just found its roots inside the panel-0.8.1 version that changed the delay
from 1ms to 15ms on 2001/11/10, and added the lcd_clear_fast() function
as a workaround. Thus probably 1ms was too short for the 1.6 ms spec,
but 15ms was needlessly high. So I think we can get rid of all of this
indeed!

> For me issuing the clear command is faster by at least one order!
> I have one of these cheap china displays with 4x20 characters. The flow
> is like this: i2c -> gpio -> hd44780 in 4 bit mode. And gpio is issuing
> a i2c request for EVERY SINGLE gpio. This is slow as hell. But it works.
> :-)

Good point for sur over i2c it would stink a little bit! Same for those
using 9600 bps serial interfaces.

> As I said I also was a bit confused by the different clean
> implementations, but the only real user of the clear_fast is the panel
> driver. The hd44780 one I use did not provide a clear_fast.
>
> So I would opt for the way I proposed in the patch series with the clear
> command instead of the loop. And let the panel driver use its optimized
> clear.

Or we could align the panel driver to get rid of it as well.

Thanks for the explanation,
Willy

2020-09-22 09:46:22

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

On Tue, Sep 22, 2020 at 07:27:01AM +0200, Willy Tarreau wrote:
> On Mon, Sep 21, 2020 at 04:46:43PM +0200, [email protected] wrote:
> > From: Lars Poeschel <[email protected]>
> >
> > Skip printing characters at the end of a display line. This fits to the
> > behaviour we already had, that the cursor is nailed to last position of
> > a line.
>
> Just very old memories, but wasn't this used with the ability to shift
> the display ? IIRC the HD44780 has a 2x64 chars buffer and you can make
> the buffered characters appear when you shift the display. That's akin
> to seeing the display as an adjustable window over the buffer. I don't
> remember having used that feature, so if it didn't previously work, please
> disregard my comment, I just want to be sure we don't break a feature
> others might be relying on.

Yes, indeed. But this is a point, where this was inconsistent. The
feature you described worked only for displays with 2 lines or less. On
displays with 4 lines there simply is no buffer that is not visible and
in this case it sticks to the end of the line already.
To make this work for all displays this would require a whole lot of
work. We would need a fallback implementation that emulates this in
software and then redraws the whole display from that software
(linux-side) buffer when shifting.

Currently my patchset cuts this feature for displays with one or two
lines. I don't see clean an easy way to leave that in. Any ideas ?

Regards,
Lars

2020-09-22 10:05:21

by Willy Tarreau

[permalink] [raw]
Subject: Re: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

On Tue, Sep 22, 2020 at 11:44:22AM +0200, Lars Poeschel wrote:
> On Tue, Sep 22, 2020 at 07:27:01AM +0200, Willy Tarreau wrote:
> > On Mon, Sep 21, 2020 at 04:46:43PM +0200, [email protected] wrote:
> > > From: Lars Poeschel <[email protected]>
> > >
> > > Skip printing characters at the end of a display line. This fits to the
> > > behaviour we already had, that the cursor is nailed to last position of
> > > a line.
> >
> > Just very old memories, but wasn't this used with the ability to shift
> > the display ? IIRC the HD44780 has a 2x64 chars buffer and you can make
> > the buffered characters appear when you shift the display. That's akin
> > to seeing the display as an adjustable window over the buffer. I don't
> > remember having used that feature, so if it didn't previously work, please
> > disregard my comment, I just want to be sure we don't break a feature
> > others might be relying on.
>
> Yes, indeed. But this is a point, where this was inconsistent. The
> feature you described worked only for displays with 2 lines or less. On
> displays with 4 lines there simply is no buffer that is not visible and
> in this case it sticks to the end of the line already.

OK.

> To make this work for all displays this would require a whole lot of
> work. We would need a fallback implementation that emulates this in
> software and then redraws the whole display from that software
> (linux-side) buffer when shifting.

I don't think that's worth it at all, really!

> Currently my patchset cuts this feature for displays with one or two
> lines. I don't see clean an easy way to leave that in. Any ideas ?

The points you mention are good enough indicators that it's extremely
unlikely anyone has ever used that feature with these drivers. So I
think it's best to keep your changes and wait to see if anyone pops
up with an obscure use case, in which case their explanation might
help figure another approach.

Willy

2020-09-22 10:22:24

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

Hi Lars, Willy,

On Tue, Sep 22, 2020 at 12:04 PM Willy Tarreau <[email protected]> wrote:
>
> The points you mention are good enough indicators that it's extremely
> unlikely anyone has ever used that feature with these drivers. So I
> think it's best to keep your changes and wait to see if anyone pops
> up with an obscure use case, in which case their explanation might
> help figure another approach.

Agreed -- Lars, please add an explanation for the removed feature in
the appropriate commit message and also mention it in the cover letter
so that such user-facing change (and any other) is documented.

Cheers,
Miguel

2020-09-22 10:27:21

by Miguel Ojeda

[permalink] [raw]
Subject: Re: [PATCH v2 00/33] Make charlcd device independent

Hi Lars,

On Mon, Sep 21, 2020 at 4:47 PM <[email protected]> wrote:
>
> This tries to make charlcd device independent.

Thanks a lot for the series!

> [1] https://marc.info/?l=linux-kernel&m=157121432124507

Nit: please use lore.kernel.org and the Link: tag instead.

Cheers,
Miguel

2020-09-22 11:52:35

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 28/32] auxdisplay: hd44780: Remove clear_fast

On Tue, Sep 22, 2020 at 11:21:21AM +0200, Willy Tarreau wrote:
> On Tue, Sep 22, 2020 at 10:49:12AM +0200, Lars Poeschel wrote:
> > > I might have got confused, but it looks to me like patches 27 and 28
> > > basically undo patch 26: in 26 you moved code to hd44780 and wrote a
> > > fallback, just to later delete that code.
> >
> > To be honest: I also got confused by this whole clear code and why are
> > there 3 different clear variants. ;-)
> > The reason why I did it this way is to show what happened to the
> > original code. The original code has a fallback that kind of does the
> > same like my naive implementation but it uses the fact, that on hd44780
> > displays the underlying buffer can be accessed in a linear manner to
> > speed things up a little. This then also clears the not visible area of
> > the buffer (which my naive implementation does not). To become
> > independent of hd44780 code at this point I had to do something.
> > I opted to for this naive implementation as a replacement for the old
> > optimized clear loop. The fallback was already there.
> > And then I did a separate step to remove it because I found it a bit
> > suboptimal. All current users have the clear command...
>
> I'm not contesting your naive implementation, it just looks to me that
> patch 26 adds it while moving code that patch 27 removes, and patch 28
> removes it. So I don't understand why not to remove it entirely in the
> first place. It's possible I missed something related to other users of
> that code but that wasn't clear from the patch nor the descriptions.

The reason is to tell the story, where the original code went. I wanted
kind of avoid a discussion why I just deleted this code. So now it's the
other way round. :-)
I do not have any deeper mental connection to this. I will squash this
together in the next version of this patch series unless other opinions
arise.

> > > I seem to remember that the reason for the clear_fast() implementation
> > > is that the default clear_display() is quite slow for small displays,
> > > compared to what can be achieved by just filling the display with spaces
> > > (in the order of tens of milliseconds vs hundreds of microseconds). But
> > > I could be mistaken given how old this is, so please take my comment
> > > with a grain of salt.
> >
> > ... well, and what the hd44780 controller does when it executes the
> > clear command is also writing a space character to all character
> > locations (also to the not visible ones). Probably very much the same
> > than the original fallback implementation.
>
> I've just checked on some old datasheets, and indeed the Display clear
> command takes up to 1.6 ms, which looks very reasonable. But the charlcd
> code currently sleeps 15 ms, which is 10 times more than needed. I've
> just found its roots inside the panel-0.8.1 version that changed the delay
> from 1ms to 15ms on 2001/11/10, and added the lcd_clear_fast() function
> as a workaround. Thus probably 1ms was too short for the 1.6 ms spec,
> but 15ms was needlessly high. So I think we can get rid of all of this
> indeed!

Ok, understood. I will also address this in the next series and make a
separate patch reducing the delay.

> > For me issuing the clear command is faster by at least one order!
> > I have one of these cheap china displays with 4x20 characters. The flow
> > is like this: i2c -> gpio -> hd44780 in 4 bit mode. And gpio is issuing
> > a i2c request for EVERY SINGLE gpio. This is slow as hell. But it works.
> > :-)
>
> Good point for sur over i2c it would stink a little bit! Same for those
> using 9600 bps serial interfaces.
>
> > As I said I also was a bit confused by the different clean
> > implementations, but the only real user of the clear_fast is the panel
> > driver. The hd44780 one I use did not provide a clear_fast.
> >
> > So I would opt for the way I proposed in the patch series with the clear
> > command instead of the loop. And let the panel driver use its optimized
> > clear.
>
> Or we could align the panel driver to get rid of it as well.

Ok, I will remove the clear_fast from panel as well and let it just use
the hd44780_common_clear_display then. This looks much cleaner. We have
just a single clear_display variant then!

Regards,
Lars

2020-09-22 11:53:15

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 31/32] auxdisplay: charlcd: Do not print chars at end of line

On Tue, Sep 22, 2020 at 12:20:36PM +0200, Miguel Ojeda wrote:
> Hi Lars, Willy,
>
> On Tue, Sep 22, 2020 at 12:04 PM Willy Tarreau <[email protected]> wrote:
> >
> > The points you mention are good enough indicators that it's extremely
> > unlikely anyone has ever used that feature with these drivers. So I
> > think it's best to keep your changes and wait to see if anyone pops
> > up with an obscure use case, in which case their explanation might
> > help figure another approach.
>
> Agreed -- Lars, please add an explanation for the removed feature in
> the appropriate commit message and also mention it in the cover letter
> so that such user-facing change (and any other) is documented.

Ok, I will do this.

Regards,
Lars

2020-09-22 16:01:50

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 32/32] auxdisplay: lcd2s DT binding doc

On Mon, 21 Sep 2020 16:46:44 +0200, [email protected] wrote:
> From: Lars Poeschel <[email protected]>
>
> Add a binding doc for the modtronix lcd2s auxdisplay driver. It also
> adds modtronix to the list of known vendor-prefixes.
>
> Signed-off-by: Lars Poeschel <[email protected]>
> --
> Changes in v2:
> - Adopted yaml based file format
> ---
> .../bindings/auxdisplay/modtronix,lcd2s.yaml | 55 +++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> 2 files changed, 57 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
>


My bot found errors running 'make dt_binding_check' on your patch:

Traceback (most recent call last):
File "/usr/local/bin/dt-extract-example", line 45, in <module>
binding = yaml.load(open(args.yamlfile, encoding='utf-8').read())
File "/usr/local/lib/python3.8/dist-packages/ruamel/yaml/main.py", line 343, in load
return constructor.get_single_data()
File "/usr/local/lib/python3.8/dist-packages/ruamel/yaml/constructor.py", line 111, in get_single_data
node = self.composer.get_single_node()
File "_ruamel_yaml.pyx", line 706, in _ruamel_yaml.CParser.get_single_node
File "_ruamel_yaml.pyx", line 724, in _ruamel_yaml.CParser._compose_document
File "_ruamel_yaml.pyx", line 775, in _ruamel_yaml.CParser._compose_node
File "_ruamel_yaml.pyx", line 891, in _ruamel_yaml.CParser._compose_mapping_node
File "_ruamel_yaml.pyx", line 904, in _ruamel_yaml.CParser._parse_next_event
ruamel.yaml.parser.ParserError: while parsing a block mapping
in "<unicode string>", line 4, column 1
did not find expected key
in "<unicode string>", line 55, column 1
make[1]: *** [Documentation/devicetree/bindings/Makefile:18: Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.example.dts] Error 1
make[1]: *** Deleting file 'Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.example.dts'
make[1]: *** Waiting for unfinished jobs....
./Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml: while parsing a block mapping
in "<unicode string>", line 4, column 1
did not find expected key
in "<unicode string>", line 55, column 1
/builds/robherring/linux-dt-review/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml: ignoring, error parsing file
warning: no schema found in file: ./Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
make: *** [Makefile:1366: dt_binding_check] Error 2


See https://patchwork.ozlabs.org/patch/1368465

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure dt-schema is up to date:

pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade

Please check and re-submit.

2020-10-02 12:44:15

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH v2 33/33] auxdisplay: add a driver for lcd2s character display

Hi!

> From: Lars Poeschel <[email protected]>
>
> This driver allows to use a lcd2s 20x4 character display from modtronix
> engineering as an auxdisplay charlcd device.

Is there userland interface documenteted somewhere? I tried to grep
through Documentation and did not find anything useful :-(.


pavel@duo:/data/l/k$ grep -ri charlcd Documentation/
pavel@duo:/data/l/k$ grep -ri auxdis Documentation/
Documentation/admin-guide/auxdisplay/ks0108.rst:For example, check Documentation/admin-guide/auxdisplay/cfag12864b.rst
Documentation/admin-guide/auxdisplay/cfag12864b.rst:Check samples/auxdisplay/cfag12864b-example.c
Documentation/admin-guide/index.rst: auxdisplay/index
Documentation/devicetree/bindings/auxdisplay/hit,hd44780.yaml:$id: http://devicetree.org/schemas/auxdisplay/hit,hd44780.yaml#
Documentation/devicetree/bindings/auxdisplay/hit,hd44780.yaml: auxdisplay {
Documentation/translations/zh_CN/admin-guide/index.rst: auxdisplay/index
pavel@duo:/data/l/k$

Best regards,
Pavel

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html


Attachments:
(No filename) (1.15 kB)
signature.asc (201.00 B)
Download all attachments

2020-10-02 13:17:11

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 33/33] auxdisplay: add a driver for lcd2s character display

On Fri, Oct 02, 2020 at 02:42:49PM +0200, Pavel Machek wrote:
> Hi!
>
> > From: Lars Poeschel <[email protected]>
> >
> > This driver allows to use a lcd2s 20x4 character display from modtronix
> > engineering as an auxdisplay charlcd device.
>
> Is there userland interface documenteted somewhere? I tried to grep
> through Documentation and did not find anything useful :-(.

I am also not aware of any userland documentation. But this driver does
not have any userland facing parts. ;-)

Regards,
Lars

2020-10-02 13:47:51

by Lars Poeschel

[permalink] [raw]
Subject: Re: [PATCH v2 32/32] auxdisplay: lcd2s DT binding doc

On Tue, Sep 22, 2020 at 09:58:36AM -0600, Rob Herring wrote:
> On Mon, 21 Sep 2020 16:46:44 +0200, [email protected] wrote:
> > From: Lars Poeschel <[email protected]>
> >
> > Add a binding doc for the modtronix lcd2s auxdisplay driver. It also
> > adds modtronix to the list of known vendor-prefixes.
> >
> > Signed-off-by: Lars Poeschel <[email protected]>
> > --
> > Changes in v2:
> > - Adopted yaml based file format
> > ---
> > .../bindings/auxdisplay/modtronix,lcd2s.yaml | 55 +++++++++++++++++++
> > .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> > 2 files changed, 57 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
> >
>
>
> My bot found errors running 'make dt_binding_check' on your patch:
>
> Traceback (most recent call last):
> File "/usr/local/bin/dt-extract-example", line 45, in <module>
> binding = yaml.load(open(args.yamlfile, encoding='utf-8').read())
> File "/usr/local/lib/python3.8/dist-packages/ruamel/yaml/main.py", line 343, in load
> return constructor.get_single_data()
> File "/usr/local/lib/python3.8/dist-packages/ruamel/yaml/constructor.py", line 111, in get_single_data
> node = self.composer.get_single_node()
> File "_ruamel_yaml.pyx", line 706, in _ruamel_yaml.CParser.get_single_node
> File "_ruamel_yaml.pyx", line 724, in _ruamel_yaml.CParser._compose_document
> File "_ruamel_yaml.pyx", line 775, in _ruamel_yaml.CParser._compose_node
> File "_ruamel_yaml.pyx", line 891, in _ruamel_yaml.CParser._compose_mapping_node
> File "_ruamel_yaml.pyx", line 904, in _ruamel_yaml.CParser._parse_next_event
> ruamel.yaml.parser.ParserError: while parsing a block mapping
> in "<unicode string>", line 4, column 1
> did not find expected key
> in "<unicode string>", line 55, column 1
> make[1]: *** [Documentation/devicetree/bindings/Makefile:18: Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.example.dts] Error 1
> make[1]: *** Deleting file 'Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.example.dts'
> make[1]: *** Waiting for unfinished jobs....
> ./Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml: while parsing a block mapping
> in "<unicode string>", line 4, column 1
> did not find expected key
> in "<unicode string>", line 55, column 1
> /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml: ignoring, error parsing file
> warning: no schema found in file: ./Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml
> make: *** [Makefile:1366: dt_binding_check] Error 2
>
>
> See https://patchwork.ozlabs.org/patch/1368465
>
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure dt-schema is up to date:
>
> pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade
>
> Please check and re-submit.

To be honest: I have indeed problems running make dt_binding_check:

larsi@lem-wkst-02:~/projekte/linux-stable$ make dt_binding_check
ERROR: dtschema minimum version is v2020.5
make[1]: *** [Documentation/devicetree/bindings/Makefile:10: check_dtschema_version] Fehler 1
make: *** [Makefile:1366: dt_binding_check] Fehler 2

Well ok, I seem to have a problem with some version. I took a look at
the mentioned part of the Makefile and found, that a tool
dt-doc-validate is called with --version argument to find out if my
version suffices. So I tried that:

larsi@lem-wkst-02:~/projekte/linux-stable$ dt-doc-validate --version
2020.8.2.dev2+gd63b653

What is wrong ? Can you give me a hint how I can properly run these
checks ?

Thank you,
Lars