From 66ce7d5c1e124b497f45aead50df1dc3c2873382 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:04 +0100 Subject: [PATCH 01/30] auxdisplay: Use an enum for charlcd backlight on/off ops 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 on / turn off operations as well. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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; From 718e05ed92ecac0d9d3954bcc8064527c3ce7565 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:05 +0100 Subject: [PATCH 02/30] auxdisplay: Introduce hd44780_common.[ch] 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/Kconfig | 20 +++++++++++++ drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/hd44780.c | 45 +++++++++++++++++++---------- drivers/auxdisplay/hd44780_common.c | 19 ++++++++++++ drivers/auxdisplay/hd44780_common.h | 7 +++++ drivers/auxdisplay/panel.c | 18 ++++++++++-- 6 files changed, 92 insertions(+), 18 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..aaf2d66357a1 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -16,10 +16,29 @@ menuconfig AUXDISPLAY if AUXDISPLAY +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. + 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 #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; } - lcd = charlcd_alloc(sizeof(struct hd44780)); - if (!lcd) + hdc = hd44780_common_alloc(); + if (!hdc) return -ENOMEM; - hd = lcd->drvdata; + lcd = charlcd_alloc(sizeof(struct hd44780)); + 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,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..2fdea29d6a8f --- /dev/null +++ b/drivers/auxdisplay/hd44780_common.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + +#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..974868f7529c --- /dev/null +++ b/drivers/auxdisplay/hd44780_common.h @@ -0,0 +1,7 @@ +/* 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 #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; } From 2545c1c948a6a765f1a0e820c7598138b36f67ef Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:06 +0100 Subject: [PATCH 03/30] auxdisplay: Move hwidth and bwidth to struct hd44780_common 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 40 ++++++++++++------------- drivers/auxdisplay/charlcd.h | 6 ++-- drivers/auxdisplay/hd44780.c | 24 +++++++++------ drivers/auxdisplay/hd44780_common.c | 2 ++ drivers/auxdisplay/hd44780_common.h | 5 ++++ drivers/auxdisplay/panel.c | 45 +++++++++++++++-------------- 6 files changed, 67 insertions(+), 55 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 #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 2fdea29d6a8f..3a05fd30cae0 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -12,6 +12,8 @@ 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); diff --git a/drivers/auxdisplay/hd44780_common.h b/drivers/auxdisplay/hd44780_common.h index 974868f7529c..9647d24c4490 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; From 3fc04dd7eb77b54228a17753ec01128417433e46 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:07 +0100 Subject: [PATCH 04/30] auxdisplay: Move ifwidth to struct hd44780_common 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 3a05fd30cae0..4968461f0c4a 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 9647d24c4490..54cbe5a489d7 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; From 71ff701bbefec9e3c342f3a01d2d89b7ae026c71 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:08 +0100 Subject: [PATCH 05/30] auxdisplay: Move write_data pointer to hd44780_common 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 54cbe5a489d7..bcc13421bf45 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) From 2c6a82f2342fadfcbd5dd92656e662c326f7a40a Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:09 +0100 Subject: [PATCH 06/30] auxdisplay: Move write_cmd pointers to hd44780 drivers 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 bcc13421bf45..73dd4c7c6de3 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) From 11588b59cf620305e78523f57918b986b5e32214 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:10 +0100 Subject: [PATCH 07/30] auxdisplay: Move addr out of charlcd_priv 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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; }; From b26deabb1d915fe87d395081bbd3058b938dee89 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:11 +0100 Subject: [PATCH 08/30] auxdisplay: hd44780_common_print 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 calls via its ops function pointers, into the print function offered by drivers. Reported-by: kernel test robot Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 28 +++++++++++++++------------- drivers/auxdisplay/charlcd.h | 12 ++++++++++++ drivers/auxdisplay/hd44780.c | 2 ++ drivers/auxdisplay/hd44780_common.c | 14 ++++++++++++++ drivers/auxdisplay/hd44780_common.h | 1 + 5 files changed, 44 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..94922e3c1c4c 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -30,9 +30,21 @@ 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. + * Optional. + * @backlight: Turn backlight on or off. Optional. + * @print: 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 4968461f0c4a..1dfcb2cdbc70 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -2,8 +2,22 @@ #include #include +#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; +} +EXPORT_SYMBOL_GPL(hd44780_common_print); + 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 73dd4c7c6de3..ac404a44889b 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -14,4 +14,5 @@ struct hd44780_common { void *hd44780; }; +int hd44780_common_print(struct charlcd *lcd, int c); struct hd44780_common *hd44780_common_alloc(void); From d3a2fb810f273b7a2c393d4de28ae91a3f76985d Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:12 +0100 Subject: [PATCH 09/30] auxdisplay: provide hd44780_common_gotoxy Provide a hd44780_common_gotoxy function and a pointer in the ops for charlcd to use to move the cursor. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 94922e3c1c4c..f36cc80ce385 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -37,14 +37,16 @@ struct charlcd { * Optional. * @backlight: Turn backlight on or off. Optional. * @print: 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 1dfcb2cdbc70..f86eb2f27cee 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; @@ -18,6 +21,26 @@ int hd44780_common_print(struct charlcd *lcd, int c) } EXPORT_SYMBOL_GPL(hd44780_common_print); +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 ac404a44889b..d8cbea4a8658 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -15,4 +15,5 @@ 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 */ From 88645a86e3420cddfe5bb9cd8d7c15aff8f54b46 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:13 +0100 Subject: [PATCH 10/30] auxdisplay: add home to charlcd_ops 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 f36cc80ce385..236fd0e9e8ab 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -41,12 +41,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 f86eb2f27cee..ea62beada9d8 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -41,6 +41,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 d8cbea4a8658..c91070ed931b 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -16,4 +16,5 @@ 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 */ From 45421ffefbb5f195de02ead952755329ef8576d8 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:14 +0100 Subject: [PATCH 11/30] auxdisplay: Move clear_display to hd44780_common 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 236fd0e9e8ab..a6c32c4d1aac 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -43,6 +43,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); @@ -50,6 +53,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 ea62beada9d8..2f7d55668eb4 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 +#include #include #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; @@ -49,6 +58,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 c91070ed931b..ef11935a3764 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -17,4 +17,5 @@ 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 */ From 2bf82b5a80d9c4ca3e64d273771d5629d46eb1d0 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:15 +0100 Subject: [PATCH 12/30] auxdisplay: make charlcd_backlight visible to hd44780_common hd44780_common wants to use the charlcd_backlight function, so make it visible. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 a6c32c4d1aac..a3210305cae7 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -56,6 +56,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); From bd26b181d14af003a1c9903831f5735f431530f6 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:16 +0100 Subject: [PATCH 13/30] auxdisplay: Make use of enum for backlight on / off To turn the backlight on or off use our new enum CHARLCD_ON / CHARLCD_OFF. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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; From 01ec46dfa633a52ccfe38d4a194460d6adb2ba00 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:17 +0100 Subject: [PATCH 14/30] auxdisplay: Move init_display to hd44780_common The init_display function is moved over to hd44780_common. charlcd uses it via its ops function pointer and drivers initialize the ops with the common hd44780_common_init_display function. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 #include -#include #include #include #include @@ -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 a3210305cae7..dd40fd814a57 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, @@ -46,6 +53,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); @@ -54,6 +62,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 2f7d55668eb4..7d4aea36cc55 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 */ @@ -70,6 +83,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 ef11935a3764..a8e322891ce2 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,4 +19,5 @@ 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 */ From d2f2187e8f27cd884150acdce7ec96135260413b Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:18 +0100 Subject: [PATCH 15/30] auxdisplay: implement various hd44780_common_ functions This implements various hd44780_common_ functions for hd44780 compatible display drivers to use. charlcd then calls these functions through its ops function pointer. The functions namely are: - hd44780_common_shift_cursor - hd44780_common_display_shift - hd44780_common_display - hd44780_common_cursor - hd44780_common_blink - hd44780_common_fontsize - hd44780_common_lines Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 73 ++++++++++------ drivers/auxdisplay/charlcd.h | 28 ++++++ drivers/auxdisplay/hd44780.c | 14 +++ drivers/auxdisplay/hd44780_common.c | 131 ++++++++++++++++++++++++++++ drivers/auxdisplay/hd44780_common.h | 9 ++ drivers/auxdisplay/panel.c | 21 +++++ 6 files changed, 249 insertions(+), 27 deletions(-) diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 94f6b0afab13..281a3259c144 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 */ @@ -236,26 +232,44 @@ 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 */ 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 */ 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 */ @@ -272,47 +286,54 @@ 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 */ 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 */ 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 */ - 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 */ @@ -456,19 +477,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 dd40fd814a57..5a89bdeb659a 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -21,6 +21,21 @@ enum charlcd_onoff { 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 */ @@ -54,6 +69,12 @@ 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. + * @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. + * @lines: One or two lines. */ struct charlcd_ops { void (*clear_fast)(struct charlcd *lcd); @@ -63,6 +84,13 @@ 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); + 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); }; void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on); diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c index 5916da09f738..7b7b28d72198 100644 --- a/drivers/auxdisplay/hd44780.c +++ b/drivers/auxdisplay/hd44780.c @@ -131,6 +131,13 @@ 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, + .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, }; /* Send a command to the LCD panel in 4 bit GPIO mode */ @@ -179,6 +186,13 @@ 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, + .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, }; static int hd44780_probe(struct platform_device *pdev) diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c index 7d4aea36cc55..9d538fdcd260 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -17,6 +17,10 @@ #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 */ @@ -158,6 +162,133 @@ 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); + +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); + 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 a8e322891ce2..e5a69fdc3b61 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -20,4 +20,13 @@ 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); struct hd44780_common *hd44780_common_alloc(void); diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index 583bd22d3abd..3d33d7cc979c 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -879,6 +879,13 @@ 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, + .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, }; static const struct charlcd_ops charlcd_parallel_ops = { @@ -888,6 +895,13 @@ 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, + .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, }; static const struct charlcd_ops charlcd_tilcd_ops = { @@ -897,6 +911,13 @@ 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, + .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, }; /* initialize the LCD driver */ From 8a86270ef0ea5ebc37c9a1223589c1d45c7e07de Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:19 +0100 Subject: [PATCH 16/30] auxdisplay: cleanup unnecessary hd44780 code in charlcd This cleans up now unnecessary hd44780 specific code from charlcd. We obsoleted this with the last patch. So another chunk of hd44780 specific code can be dropped from charlcd. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 281a3259c144..29737c3e18f6 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -25,21 +25,8 @@ /* Keep the backlight on this many seconds for each flash */ #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 */ -#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 */ @@ -431,24 +418,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)) - 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; From 339acb082987a6b0aa7c67a5a77b29f6c81962f6 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:20 +0100 Subject: [PATCH 17/30] auxdisplay: Move char redefine code to hd44780_common 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 60 +++-------------------------- drivers/auxdisplay/charlcd.h | 2 + drivers/auxdisplay/hd44780.c | 2 + drivers/auxdisplay/hd44780_common.c | 57 +++++++++++++++++++++++++++ drivers/auxdisplay/hd44780_common.h | 1 + drivers/auxdisplay/panel.c | 3 ++ 6 files changed, 70 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 between '0' - * and '7', representing the numerical ASCII code of the - * redefined character, and 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 5a89bdeb659a..3339e8c2554e 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -75,6 +75,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); @@ -91,6 +92,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 9d538fdcd260..c528fb8e8808 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 */ @@ -289,6 +291,61 @@ 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 between '0' + * and '7', representing the numerical ASCII code of the + * redefined character, and 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) { + 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); + 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 e5a69fdc3b61..bc817d1610d4 100644 --- a/drivers/auxdisplay/hd44780_common.h +++ b/drivers/auxdisplay/hd44780_common.h @@ -29,4 +29,5 @@ 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 */ From a2060f288e15a69b2191b7e9df2e3d470e27e433 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:21 +0100 Subject: [PATCH 18/30] auxdisplay: Call charlcd_backlight in place 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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; } From 7b231bb5d0bee86afc40db9ddfd50ba39ef73769 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:22 +0100 Subject: [PATCH 19/30] auxdisplay: hd44780_common: Reduce clear_display timeout Digging in the hd44780 datasheet revealed that the timeout needed after clearing the whole display is only 1,64ms not 15ms. So we can reduce that timeout. Link: https://lore.kernel.org/lkml/20200922092121.GG16421@1wt.eu/ Link: https://www.crystalfontz.com/controllers/Hitachi/HD44780/433/ Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/hd44780_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/auxdisplay/hd44780_common.c b/drivers/auxdisplay/hd44780_common.c index c528fb8e8808..bd93a4d3367e 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -83,8 +83,8 @@ 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); + /* datasheet says to wait 1,64 milliseconds */ + long_sleep(2); return 0; } EXPORT_SYMBOL_GPL(hd44780_common_clear_display); From 377cf2cbfdb962579eedf2c29845c463c6973f7e Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:23 +0100 Subject: [PATCH 20/30] auxdisplay: hd44780: Remove clear_fast We remove the hd44780_clear_fast (display) clear implementation. With the new timeout the normal clear_display is reasonably fast. So there is no need for a clear_fast anymore. Link: https://lore.kernel.org/lkml/20200922092121.GG16421@1wt.eu/ Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 21 ++++------- drivers/auxdisplay/charlcd.h | 7 ++-- drivers/auxdisplay/panel.c | 67 ------------------------------------ 3 files changed, 8 insertions(+), 87 deletions(-) diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 0f0568a4bd35..077c01e87127 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -125,20 +125,11 @@ static void charlcd_print(struct charlcd *lcd, char c) lcd->ops->gotoxy(lcd); } -static void charlcd_clear_fast(struct charlcd *lcd) +static void charlcd_clear_display(struct charlcd *lcd) { - struct hd44780_common *hdc = lcd->drvdata; - int pos; - - charlcd_home(lcd); - - 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, ' '); - - charlcd_home(lcd); + lcd->ops->clear_display(lcd); + lcd->addr.x = 0; + lcd->addr.y = 0; } /* @@ -409,7 +400,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c) break; case '\f': /* quickly clear the display */ - charlcd_clear_fast(lcd); + charlcd_clear_display(lcd); break; case '\n': /* @@ -448,7 +439,7 @@ static void charlcd_write_char(struct charlcd *lcd, char c) if (!strcmp(priv->esc_seq.buf, "[2J")) { /* clear the display */ - charlcd_clear_fast(lcd); + charlcd_clear_display(lcd); processed = 1; } else if (!strcmp(priv->esc_seq.buf, "[H")) { /* cursor to home */ diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h index 3339e8c2554e..e7e9b12b1e99 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -55,8 +55,6 @@ struct charlcd { /** * 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. - * Optional. * @backlight: Turn backlight on or off. Optional. * @print: Print one character to the display at current cursor position. * The buffered cursor position is advanced by charlcd. The cursor should not @@ -65,8 +63,8 @@ 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 + * @clear_display: Clear the whole display and set the cursor to 0, 0. The + * values in addr.x and addr.y are set to 0, 0 by charlcd after to calling this * function. * @init_display: Initialize the display. * @shift_cursor: Shift cursor left or right one position. @@ -78,7 +76,6 @@ struct charlcd { * @redefine_char: Redefine the actual pixel matrix of character. */ 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); diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index b0d2ae5b9be8..e07fadac281d 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -808,72 +808,7 @@ static void lcd_write_data_tilcd(struct hd44780_common *hdc, 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) -{ - struct hd44780_common *hdc = charlcd->drvdata; - int pos; - - spin_lock_irq(&pprt_lock); - 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); - /* 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) -{ - struct hd44780_common *hdc = charlcd->drvdata; - int pos; - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < charlcd->height * hdc->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) -{ - struct hd44780_common *hdc = charlcd->drvdata; - int pos; - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < charlcd->height * hdc->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 = { - .clear_fast = lcd_clear_fast_s, .backlight = lcd_backlight, .gotoxy = hd44780_common_gotoxy, .home = hd44780_common_home, @@ -890,7 +825,6 @@ static const struct charlcd_ops charlcd_serial_ops = { }; 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, @@ -907,7 +841,6 @@ static const struct charlcd_ops charlcd_parallel_ops = { }; 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, From 6e49eea35886c7f7173796d6a646c8c2c0f6149d Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:24 +0100 Subject: [PATCH 21/30] auxdisplay: charlcd: replace last device specific stuff 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 077c01e87127..59f21401d6a9 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -20,7 +20,6 @@ #include #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); } @@ -195,7 +192,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 */ @@ -323,7 +319,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 */ @@ -366,7 +362,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) { @@ -407,7 +402,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; From 40c2b72e4b11f0a80dff19b539fccf36472dc417 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:25 +0100 Subject: [PATCH 22/30] auxdisplay: Change gotoxy calling interface 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. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- 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 59f21401d6a9..ef10b5ca0e16 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_display(struct charlcd *lcd) @@ -325,7 +325,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; } @@ -349,7 +349,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; @@ -407,12 +407,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 e7e9b12b1e99..eed80063a6d2 100644 --- a/drivers/auxdisplay/charlcd.h +++ b/drivers/auxdisplay/charlcd.h @@ -78,7 +78,7 @@ struct charlcd { struct charlcd_ops { 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 bd93a4d3367e..3934c2eebf33 100644 --- a/drivers/auxdisplay/hd44780_common.c +++ b/drivers/auxdisplay/hd44780_common.c @@ -49,7 +49,7 @@ int hd44780_common_print(struct charlcd *lcd, int c) } EXPORT_SYMBOL_GPL(hd44780_common_print); -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; @@ -58,11 +58,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; @@ -71,9 +70,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); @@ -341,7 +338,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 bc817d1610d4..a16aa8c29c99 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); From af9470b26dc6c3a8f9ef460c90e34a66ce4f5f1d Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:26 +0100 Subject: [PATCH 23/30] auxdisplay: charlcd: Do not print chars at end of line Skip printing characters at the end of a display line. This fits to the behaviour we already had, that the cursor is nailed to the last position of a line. This might slightly change behaviour. On hd44780 displays with one or two lines the previous implementation did still write characters to the buffer of the display even if they are currently not visible. The shift_display command could be used to set the "viewing window" to a new position in the buffer and then you could see the characters previously written. This described behaviour does not work for hd44780 displays with more than two display lines. There simply is not enough buffer. So the behaviour was a bit inconsistent across different displays. The new behaviour is to stop writing characters at the end of a visible line, even if there would be room in the buffer. This allows us to have an easy implementation, that should behave equal on all supported displays. This is not hd44780 hardware dependent anymore. Reviewed-by: Willy Tarreau Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/charlcd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index ef10b5ca0e16..f43430e9dcee 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]; From 1741b014bd5556219fc9312a9401cad7aa364385 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:27 +0100 Subject: [PATCH 24/30] 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. Reviewed-by: Willy Tarreau Reviewed-by: Rob Herring Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- .../bindings/auxdisplay/modtronix,lcd2s.yaml | 58 +++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 60 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..a1d55a2634a5 --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/modtronix,lcd2s.yaml @@ -0,0 +1,58 @@ +# 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 + +maintainers: + - Lars Poeschel + +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 2735be1a8470..65f40ab5a87f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -681,6 +681,8 @@ patternProperties: description: MiraMEMS Sensing Technology Co., Ltd. "^mitsubishi,.*": description: Mitsubishi Electric Corporation + "^modtronix,.*": + description: Modtronix Engineering "^mosaixtech,.*": description: Mosaix Technologies, Inc. "^motorola,.*": From 8c9108d014c5bd0f0da2e3544eb45dc56a6da92b Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Tue, 3 Nov 2020 10:58:28 +0100 Subject: [PATCH 25/30] 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 Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/Kconfig | 10 + drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/lcd2s.c | 403 ++++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 drivers/auxdisplay/lcd2s.c diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index aaf2d66357a1..a69623124a26 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -173,6 +173,16 @@ 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 + 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..cfa5f86deeef --- /dev/null +++ b/drivers/auxdisplay/lcd2s.c @@ -0,0 +1,403 @@ +// 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 + * All rights reserved. + */ +#include +#include +#include +#include +#include + +#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 between '0' + * and '7', representing the numerical ASCII code of the + * redefined character, and 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) { + int half; + + shift ^= 4; + half = hex_to_bin(*esc++); + if (half < 0) + continue; + + value |= half << shift; + if (shift == 0) { + buf[i++] = value; + value = 0; + } + } + + 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"); From ffdf726db7fb02a96396dcb8deb7436b4a47dd14 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 9 Nov 2020 10:31:54 +0100 Subject: [PATCH 26/30] auxdisplay: hd44780_common: Fix build error When building the hd44780_common driver without a driver that actually uses it like panel or hd44780 you get a build error, because hd44780_common uses charlcd, but did not select it. Its users did select it. This is fixed now. hd4478_common now selects charlcd in Kconfig and panel and hd44780 do not. They only select hd44780_common. Reported-by: Randy Dunlap Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index a69623124a26..a2b59b84bb88 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -27,6 +27,7 @@ config CHARLCD config HD44780_COMMON tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST + select CHARLCD 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 @@ -37,7 +38,6 @@ config HD44780_COMMON 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. @@ -196,7 +196,6 @@ config ARM_CHARLCD 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 From 2cfd72f81cf69c2659c6d2121308b55416229beb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 6 Nov 2020 22:24:15 +0300 Subject: [PATCH 27/30] auxdisplay: fix use after free in lcd2s_i2c_remove() The kfree() needs to be moved down a line to prevent a use after free. Fixes: 8c9108d014c5 ("auxdisplay: add a driver for lcd2s character display") Signed-off-by: Dan Carpenter Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/lcd2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c index cfa5f86deeef..3eb7f04db6cb 100644 --- a/drivers/auxdisplay/lcd2s.c +++ b/drivers/auxdisplay/lcd2s.c @@ -348,8 +348,8 @@ static int lcd2s_i2c_remove(struct i2c_client *i2c) { struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c); - kfree(lcd2s->charlcd); charlcd_unregister(lcd2s->charlcd); + kfree(lcd2s->charlcd); return 0; } From decbaf182d8f3a0c83be7c60d27026b5abc04c42 Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Wed, 11 Nov 2020 17:25:59 +0800 Subject: [PATCH 28/30] auxdisplay: fix platform_no_drv_owner.cocci warnings drivers/auxdisplay/lcd2s.c:373:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Fixes: 8c9108d014c5 ("auxdisplay: add a driver for lcd2s character display") CC: Lars Poeschel Reported-by: kernel test robot Signed-off-by: kernel test robot Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/lcd2s.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c index 3eb7f04db6cb..38ba08628ccb 100644 --- a/drivers/auxdisplay/lcd2s.c +++ b/drivers/auxdisplay/lcd2s.c @@ -370,7 +370,6 @@ MODULE_DEVICE_TABLE(of, lcd2s_of_table); 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 From 32d917e754bdc322e22439f6ce400a1a74bbdc6e Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 16 Nov 2020 14:21:55 +0100 Subject: [PATCH 29/30] auxdisplay: panel: Fix missing print function pointer charlcd drivers need to provide some print function to charlcd. For hd44780 based panel driver this function was missing. We provide the generic hd44780_common_print function which should be suitable. Fixes: b26deabb1d915fe87d395081bbd3058b938dee89 ("auxdisplay: hd44780_common_print") Reported-by: kernel test robot Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/panel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index e07fadac281d..36848e464d1f 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -810,6 +810,7 @@ static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data) static const struct charlcd_ops charlcd_serial_ops = { .backlight = lcd_backlight, + .print = hd44780_common_print, .gotoxy = hd44780_common_gotoxy, .home = hd44780_common_home, .clear_display = hd44780_common_clear_display, @@ -826,6 +827,7 @@ static const struct charlcd_ops charlcd_serial_ops = { static const struct charlcd_ops charlcd_parallel_ops = { .backlight = lcd_backlight, + .print = hd44780_common_print, .gotoxy = hd44780_common_gotoxy, .home = hd44780_common_home, .clear_display = hd44780_common_clear_display, @@ -842,6 +844,7 @@ static const struct charlcd_ops charlcd_parallel_ops = { static const struct charlcd_ops charlcd_tilcd_ops = { .backlight = lcd_backlight, + .print = hd44780_common_print, .gotoxy = hd44780_common_gotoxy, .home = hd44780_common_home, .clear_display = hd44780_common_clear_display, From 351dcacc6d774258be9fec6f51c14f8ff38243f6 Mon Sep 17 00:00:00 2001 From: Lars Poeschel Date: Mon, 16 Nov 2020 14:41:21 +0100 Subject: [PATCH 30/30] auxdisplay: panel: Remove redundant charlcd_ops structures The three struct charlcd_ops contain the same data, so we only need one of this structures. The other two are removed. Signed-off-by: Lars Poeschel Signed-off-by: Miguel Ojeda --- drivers/auxdisplay/panel.c | 42 ++++---------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index 36848e464d1f..ff5755ee5694 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -808,41 +808,7 @@ static void lcd_write_data_tilcd(struct hd44780_common *hdc, int data) spin_unlock_irq(&pprt_lock); } -static const struct charlcd_ops charlcd_serial_ops = { - .backlight = lcd_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 const struct charlcd_ops charlcd_parallel_ops = { - .backlight = lcd_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 const struct charlcd_ops charlcd_tilcd_ops = { +static const struct charlcd_ops charlcd_ops = { .backlight = lcd_backlight, .print = hd44780_common_print, .gotoxy = hd44780_common_gotoxy, @@ -984,7 +950,7 @@ static void lcd_init(void) charlcd->height = DEFAULT_LCD_HEIGHT; if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */ - charlcd->ops = &charlcd_serial_ops; + charlcd->ops = &charlcd_ops; hdc->write_data = lcd_write_data_s; hdc->write_cmd = lcd_write_cmd_s; @@ -994,7 +960,7 @@ static void lcd_init(void) lcd.pins.da = DEFAULT_LCD_PIN_SDA; } else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */ - charlcd->ops = &charlcd_parallel_ops; + charlcd->ops = &charlcd_ops; hdc->write_data = lcd_write_data_p8; hdc->write_cmd = lcd_write_cmd_p8; @@ -1005,7 +971,7 @@ static void lcd_init(void) if (lcd.pins.rw == PIN_NOT_SET) lcd.pins.rw = DEFAULT_LCD_PIN_RW; } else { - charlcd->ops = &charlcd_tilcd_ops; + charlcd->ops = &charlcd_ops; hdc->write_data = lcd_write_data_tilcd; hdc->write_cmd = lcd_write_cmd_tilcd; }