A bigger set of changes than usual for auxdisplay:
- Significant refactor work to make charlcd independent of device, i.e. hd44780 (Lars Poeschel) - New driver: lcd2s (Lars Poeschel) - Fixes on top of the rework while being tested in -next (Lars Poeschel, Dan Carpenter and kernel test robot) -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAl/VkIYACgkQGXyLc2ht IW264w/7BcxSja4zpH4ij70Gn40K62xIb0ZXoSrLeh71cmMcWkMdzHgMbeGKYcLC xX3pUazq7g2gEbSEnlBZcMpO8SnT0ZtLTgR8pMP3tTP9HzL7lQMK6z8khm1uYCam mSvQy1UzX/tzIVggagKwiN+l4pZyfuLu9z+klI5rqlhD9LTC1qZ6O8kpMx5s6ykJ WV2zcsbocSZ4kI5tomO3/jj8NsramjWkaQ24ikCpkOKYhs+nO7YdP1KMD2oFqYJQ 1f9Z58lQSsTDYVoIr5PUdUqNUwjo+gjof4pdcKcSReaAfJnMSIoX8ZSCSFD0mXR4 0rKn9hrQGQmECHMGlDWKPCeyXBmkyG8EqnicZje+hLDjI+H/+yLz2CFWcnMXST+X DPiF4C7q84AzGLOmtcDKfjn0/VjoSN3tXQx7MJA2qtvAmvr7H40M9kq/L/4yZ6lJ go8NQ6tLX4ITK3sslBgyjPOhSoRWYe8UvRdYTkFK7ajeqUvWkdp7Rp3yFI8KceBy 4bKXuquWXQNxDDQb8U/zAf7w44oUK8mk/dKZNZyZanfTiuhCPEHQ6oEAZdifLiUq Bn+svl8tMvZh9V+I/d5Slx+7DANmc8f3JNbiYFpXeAQ01708oPKgj1/24pS2rxSG EH9TpoWra2rd3XJ7PyXvr14Dz/Nn+BeY53lFdZBDpIGmsH3lkeY= =Hc52 -----END PGP SIGNATURE----- Merge tag 'auxdisplay-for-linus-v5.11' of git://github.com/ojeda/linux Pull auxdisplay updates from Miguel Ojeda: "A bigger set of changes than usual for auxdisplay. There have been quite a few changes in auxdisplay thanks to a refactor by Lars Poeschel to share code in order to introduce a new driver. Summary: - Significant refactor work to make charlcd independent of device, i.e. hd44780 (Lars Poeschel) - New driver: lcd2s (Lars Poeschel) - Fixes on top of the rework while being tested in -next (Lars Poeschel, Dan Carpenter and kernel test robot)" * tag 'auxdisplay-for-linus-v5.11' of git://github.com/ojeda/linux: (30 commits) auxdisplay: panel: Remove redundant charlcd_ops structures auxdisplay: panel: Fix missing print function pointer auxdisplay: fix platform_no_drv_owner.cocci warnings auxdisplay: fix use after free in lcd2s_i2c_remove() auxdisplay: hd44780_common: Fix build error auxdisplay: add a driver for lcd2s character display auxdisplay: lcd2s DT binding doc auxdisplay: charlcd: Do not print chars at end of line auxdisplay: Change gotoxy calling interface auxdisplay: charlcd: replace last device specific stuff auxdisplay: hd44780: Remove clear_fast auxdisplay: hd44780_common: Reduce clear_display timeout auxdisplay: Call charlcd_backlight in place auxdisplay: Move char redefine code to hd44780_common auxdisplay: cleanup unnecessary hd44780 code in charlcd auxdisplay: implement various hd44780_common_ functions auxdisplay: Move init_display to hd44780_common auxdisplay: Make use of enum for backlight on / off auxdisplay: make charlcd_backlight visible to hd44780_common auxdisplay: Move clear_display to hd44780_common ...
This commit is contained in:
Коммит
bcc68bd816
|
@ -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 <poeschel@lemonage.de>
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
|
@ -683,6 +683,8 @@ patternProperties:
|
|||
description: MiraMEMS Sensing Technology Co., Ltd.
|
||||
"^mitsubishi,.*":
|
||||
description: Mitsubishi Electric Corporation
|
||||
"^modtronix,.*":
|
||||
description: Modtronix Engineering
|
||||
"^mosaixtech,.*":
|
||||
description: Mosaix Technologies, Inc.
|
||||
"^motorola,.*":
|
||||
|
|
|
@ -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
|
||||
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
|
||||
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).
|
||||
|
@ -154,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
|
||||
|
@ -167,7 +196,7 @@ 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
|
||||
parallel port. This driver also features 4 and 6-key keypads. The LCD
|
||||
|
|
|
@ -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
|
||||
|
@ -11,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
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -22,43 +21,9 @@
|
|||
|
||||
#include "charlcd.h"
|
||||
|
||||
#define DEFAULT_LCD_BWIDTH 40
|
||||
#define DEFAULT_LCD_HWIDTH 64
|
||||
|
||||
/* Keep the backlight on this many seconds for each flash */
|
||||
#define LCD_BL_TEMPO_PERIOD 4
|
||||
|
||||
#define LCD_FLAG_B 0x0004 /* Blink on */
|
||||
#define LCD_FLAG_C 0x0008 /* Cursor on */
|
||||
#define LCD_FLAG_D 0x0010 /* Display on */
|
||||
#define LCD_FLAG_F 0x0020 /* Large font mode */
|
||||
#define LCD_FLAG_N 0x0040 /* 2-rows mode */
|
||||
#define LCD_FLAG_L 0x0080 /* Backlight enabled */
|
||||
|
||||
/* LCD commands */
|
||||
#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
|
||||
|
||||
#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
|
||||
#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
|
||||
|
||||
#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
|
||||
#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
|
||||
#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
|
||||
#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
|
||||
|
||||
#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
|
||||
#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
|
||||
#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
|
||||
|
||||
#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
|
||||
#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
|
||||
#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
|
||||
#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
|
||||
|
||||
#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
|
||||
|
||||
#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
|
||||
|
||||
#define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
|
||||
#define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */
|
||||
|
||||
|
@ -74,12 +39,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];
|
||||
|
@ -94,14 +53,8 @@ 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 */
|
||||
static void charlcd_backlight(struct charlcd *lcd, int on)
|
||||
void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
|
||||
|
@ -113,6 +66,7 @@ static void charlcd_backlight(struct charlcd *lcd, int 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)
|
||||
{
|
||||
|
@ -124,7 +78,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);
|
||||
}
|
||||
|
@ -141,148 +95,41 @@ 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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(charlcd_poke);
|
||||
|
||||
static void charlcd_gotoxy(struct charlcd *lcd)
|
||||
{
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
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;
|
||||
if (priv->addr.y & 1)
|
||||
addr += lcd->hwidth;
|
||||
if (priv->addr.y & 2)
|
||||
addr += lcd->bwidth;
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
|
||||
}
|
||||
|
||||
static void charlcd_home(struct charlcd *lcd)
|
||||
{
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
|
||||
priv->addr.x = 0;
|
||||
priv->addr.y = 0;
|
||||
charlcd_gotoxy(lcd);
|
||||
lcd->addr.x = 0;
|
||||
lcd->addr.y = 0;
|
||||
lcd->ops->home(lcd);
|
||||
}
|
||||
|
||||
static void charlcd_print(struct charlcd *lcd, char c)
|
||||
{
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
if (lcd->addr.x >= lcd->width)
|
||||
return;
|
||||
|
||||
if (priv->addr.x < lcd->bwidth) {
|
||||
if (lcd->char_conv)
|
||||
c = lcd->char_conv[(unsigned char)c];
|
||||
lcd->ops->write_data(lcd, c);
|
||||
priv->addr.x++;
|
||||
if (lcd->char_conv)
|
||||
c = lcd->char_conv[(unsigned char)c];
|
||||
|
||||
/* prevents the cursor from wrapping onto the next line */
|
||||
if (priv->addr.x == lcd->bwidth)
|
||||
charlcd_gotoxy(lcd);
|
||||
}
|
||||
if (!lcd->ops->print(lcd, c))
|
||||
lcd->addr.x++;
|
||||
|
||||
/* prevents the cursor from wrapping onto the next line */
|
||||
if (lcd->addr.x == lcd->width)
|
||||
lcd->ops->gotoxy(lcd, lcd->addr.x - 1, lcd->addr.y);
|
||||
}
|
||||
|
||||
static void charlcd_clear_fast(struct charlcd *lcd)
|
||||
{
|
||||
int pos;
|
||||
|
||||
charlcd_home(lcd);
|
||||
|
||||
if (lcd->ops->clear_fast)
|
||||
lcd->ops->clear_fast(lcd);
|
||||
else
|
||||
for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
|
||||
lcd->ops->write_data(lcd, ' ');
|
||||
|
||||
charlcd_home(lcd);
|
||||
}
|
||||
|
||||
/* clears the display and resets X/Y */
|
||||
static void charlcd_clear_display(struct charlcd *lcd)
|
||||
{
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
|
||||
priv->addr.x = 0;
|
||||
priv->addr.y = 0;
|
||||
/* we must wait a few milliseconds (15) */
|
||||
long_sleep(15);
|
||||
}
|
||||
|
||||
static int charlcd_init_display(struct charlcd *lcd)
|
||||
{
|
||||
void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
|
||||
struct charlcd_priv *priv = charlcd_to_priv(lcd);
|
||||
u8 init;
|
||||
|
||||
if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
|
||||
return -EINVAL;
|
||||
|
||||
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 (lcd->ifwidth == 4) {
|
||||
init >>= 4;
|
||||
write_cmd_raw = lcd->ops->write_cmd_raw4;
|
||||
} else {
|
||||
write_cmd_raw = lcd->ops->write_cmd;
|
||||
}
|
||||
write_cmd_raw(lcd, init);
|
||||
long_sleep(10);
|
||||
write_cmd_raw(lcd, init);
|
||||
long_sleep(10);
|
||||
write_cmd_raw(lcd, init);
|
||||
long_sleep(10);
|
||||
|
||||
if (lcd->ifwidth == 4) {
|
||||
/* Switch to 4-bit mode, 1 line, small fonts */
|
||||
lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
|
||||
long_sleep(10);
|
||||
}
|
||||
|
||||
/* set font height and lines number */
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_FUNCTION_SET |
|
||||
((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
|
||||
((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
|
||||
((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
|
||||
long_sleep(10);
|
||||
|
||||
/* display off, cursor off, blink off */
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
|
||||
long_sleep(10);
|
||||
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_DISPLAY_CTRL | /* set display mode */
|
||||
((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
|
||||
((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
|
||||
((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
|
||||
|
||||
charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
|
||||
|
||||
long_sleep(10);
|
||||
|
||||
/* entry mode set : increment, cursor shifting */
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
|
||||
|
||||
charlcd_clear_display(lcd);
|
||||
return 0;
|
||||
lcd->ops->clear_display(lcd);
|
||||
lcd->addr.x = 0;
|
||||
lcd->addr.y = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -360,34 +207,58 @@ 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 */
|
||||
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 */
|
||||
|
@ -396,158 +267,98 @@ 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 (priv->addr.x > 0) {
|
||||
/* back one char if not at end of line */
|
||||
if (priv->addr.x < lcd->bwidth)
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
|
||||
priv->addr.x--;
|
||||
if (lcd->addr.x > 0) {
|
||||
if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
|
||||
lcd->addr.x--;
|
||||
}
|
||||
|
||||
processed = 1;
|
||||
break;
|
||||
case 'r': /* shift cursor right */
|
||||
if (priv->addr.x < lcd->width) {
|
||||
/* allow the cursor to pass the end of the line */
|
||||
if (priv->addr.x < (lcd->bwidth - 1))
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
|
||||
priv->addr.x++;
|
||||
if (lcd->addr.x < lcd->width) {
|
||||
if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT))
|
||||
lcd->addr.x++;
|
||||
}
|
||||
|
||||
processed = 1;
|
||||
break;
|
||||
case 'L': /* shift display left */
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
|
||||
lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT);
|
||||
processed = 1;
|
||||
break;
|
||||
case 'R': /* shift display right */
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
|
||||
LCD_CMD_SHIFT_RIGHT);
|
||||
lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT);
|
||||
processed = 1;
|
||||
break;
|
||||
case 'k': { /* kill end of line */
|
||||
int x;
|
||||
int x, xs, ys;
|
||||
|
||||
for (x = priv->addr.x; x < lcd->bwidth; x++)
|
||||
lcd->ops->write_data(lcd, ' ');
|
||||
xs = lcd->addr.x;
|
||||
ys = lcd->addr.y;
|
||||
for (x = lcd->addr.x; x < lcd->width; x++)
|
||||
lcd->ops->print(lcd, ' ');
|
||||
|
||||
/* restore cursor position */
|
||||
charlcd_gotoxy(lcd);
|
||||
lcd->addr.x = xs;
|
||||
lcd->addr.y = ys;
|
||||
lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
|
||||
processed = 1;
|
||||
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': {
|
||||
/* Generator : LGcxxxxx...xx; must have <c> between '0'
|
||||
* and '7', representing the numerical ASCII code of the
|
||||
* redefined character, and <xx...xx> a sequence of 16
|
||||
* hex digits representing 8 bytes for each character.
|
||||
* Most LCDs will only use 5 lower bits of the 7 first
|
||||
* bytes.
|
||||
*/
|
||||
|
||||
unsigned char cgbytes[8];
|
||||
unsigned char cgaddr;
|
||||
int cgoffset;
|
||||
int shift;
|
||||
char value;
|
||||
int addr;
|
||||
|
||||
if (!strchr(esc, ';'))
|
||||
break;
|
||||
|
||||
esc++;
|
||||
|
||||
cgaddr = *(esc++) - '0';
|
||||
if (cgaddr > 7) {
|
||||
case 'G':
|
||||
if (lcd->ops->redefine_char)
|
||||
processed = lcd->ops->redefine_char(lcd, esc);
|
||||
else
|
||||
processed = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
cgoffset = 0;
|
||||
shift = 0;
|
||||
value = 0;
|
||||
while (*esc && cgoffset < 8) {
|
||||
int half;
|
||||
|
||||
shift ^= 4;
|
||||
|
||||
half = hex_to_bin(*esc++);
|
||||
if (half < 0)
|
||||
continue;
|
||||
|
||||
value |= half << shift;
|
||||
if (shift == 0) {
|
||||
cgbytes[cgoffset++] = value;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
|
||||
for (addr = 0; addr < cgoffset; addr++)
|
||||
lcd->ops->write_data(lcd, cgbytes[addr]);
|
||||
|
||||
/* ensures that we stop writing to CGRAM */
|
||||
charlcd_gotoxy(lcd);
|
||||
processed = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'x': /* gotoxy : LxXXX[yYYY]; */
|
||||
case 'y': /* gotoxy : LyYYY[xXXX]; */
|
||||
if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
|
||||
break;
|
||||
|
||||
/* If the command is valid, move to the new address */
|
||||
if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
|
||||
charlcd_gotoxy(lcd);
|
||||
if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
|
||||
lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
|
||||
|
||||
/* Regardless of its validity, mark as processed */
|
||||
processed = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* TODO: This indent party here got ugly, clean it! */
|
||||
/* Check whether one flag was changed */
|
||||
if (oldflags == priv->flags)
|
||||
return processed;
|
||||
|
||||
/* check whether one of B,C,D flags were changed */
|
||||
if ((oldflags ^ priv->flags) &
|
||||
(LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
|
||||
/* set display mode */
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_DISPLAY_CTRL |
|
||||
((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
|
||||
((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
|
||||
((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
|
||||
/* check whether one of F,N flags was changed */
|
||||
else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
|
||||
lcd->ops->write_cmd(lcd,
|
||||
LCD_CMD_FUNCTION_SET |
|
||||
((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
|
||||
((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
|
||||
((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
|
||||
/* check whether L flag was changed */
|
||||
else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
|
||||
charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
|
@ -572,40 +383,39 @@ 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) {
|
||||
/*
|
||||
* check if we're not at the
|
||||
* end of the line
|
||||
*/
|
||||
if (priv->addr.x < lcd->bwidth)
|
||||
/* back one char */
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
|
||||
priv->addr.x--;
|
||||
if (lcd->addr.x > 0) {
|
||||
/* back one char */
|
||||
if (!lcd->ops->shift_cursor(lcd,
|
||||
CHARLCD_SHIFT_LEFT))
|
||||
lcd->addr.x--;
|
||||
}
|
||||
/* replace with a space */
|
||||
lcd->ops->write_data(lcd, ' ');
|
||||
charlcd_print(lcd, ' ');
|
||||
/* back one char again */
|
||||
lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
|
||||
if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
|
||||
lcd->addr.x--;
|
||||
|
||||
break;
|
||||
case '\f':
|
||||
/* quickly clear the display */
|
||||
charlcd_clear_fast(lcd);
|
||||
charlcd_clear_display(lcd);
|
||||
break;
|
||||
case '\n':
|
||||
/*
|
||||
* flush the remainder of the current line and
|
||||
* go to the beginning of the next line
|
||||
*/
|
||||
for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
|
||||
lcd->ops->write_data(lcd, ' ');
|
||||
priv->addr.x = 0;
|
||||
priv->addr.y = (priv->addr.y + 1) % lcd->height;
|
||||
charlcd_gotoxy(lcd);
|
||||
for (; lcd->addr.x < lcd->width; lcd->addr.x++)
|
||||
lcd->ops->print(lcd, ' ');
|
||||
|
||||
lcd->addr.x = 0;
|
||||
lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
|
||||
lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
|
||||
break;
|
||||
case '\r':
|
||||
/* go to the beginning of the same line */
|
||||
priv->addr.x = 0;
|
||||
charlcd_gotoxy(lcd);
|
||||
lcd->addr.x = 0;
|
||||
lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
|
||||
break;
|
||||
case '\t':
|
||||
/* print a space instead of the tab */
|
||||
|
@ -627,7 +437,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 */
|
||||
|
@ -690,8 +500,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);
|
||||
|
||||
|
@ -756,6 +568,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);
|
||||
|
@ -766,7 +580,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;
|
||||
|
||||
|
@ -779,22 +593,18 @@ 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;
|
||||
|
||||
priv->esc_seq.len = -1;
|
||||
|
||||
lcd = &priv->lcd;
|
||||
lcd->ifwidth = 8;
|
||||
lcd->bwidth = DEFAULT_LCD_BWIDTH;
|
||||
lcd->hwidth = DEFAULT_LCD_HWIDTH;
|
||||
lcd->drvdata = priv->drvdata;
|
||||
|
||||
return lcd;
|
||||
}
|
||||
|
@ -862,7 +672,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;
|
||||
|
|
|
@ -9,31 +9,91 @@
|
|||
#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,
|
||||
};
|
||||
|
||||
enum charlcd_shift_dir {
|
||||
CHARLCD_SHIFT_LEFT,
|
||||
CHARLCD_SHIFT_RIGHT,
|
||||
};
|
||||
|
||||
enum charlcd_fontsize {
|
||||
CHARLCD_FONTSIZE_SMALL,
|
||||
CHARLCD_FONTSIZE_LARGE,
|
||||
};
|
||||
|
||||
enum charlcd_lines {
|
||||
CHARLCD_LINES_1,
|
||||
CHARLCD_LINES_2,
|
||||
};
|
||||
|
||||
struct charlcd {
|
||||
const struct charlcd_ops *ops;
|
||||
const unsigned char *char_conv; /* Optional */
|
||||
|
||||
int ifwidth; /* 4-bit or 8-bit (default) */
|
||||
int height;
|
||||
int width;
|
||||
int bwidth; /* Default set by charlcd_alloc() */
|
||||
int hwidth; /* Default set by charlcd_alloc() */
|
||||
|
||||
void *drvdata; /* Set by charlcd_alloc() */
|
||||
/* Contains the LCD X and Y offset */
|
||||
struct {
|
||||
unsigned long x;
|
||||
unsigned long y;
|
||||
} addr;
|
||||
|
||||
void *drvdata;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct charlcd_ops - Functions used by charlcd. Drivers have to implement
|
||||
* these.
|
||||
* @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
|
||||
* 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.
|
||||
* @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.
|
||||
* @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.
|
||||
* @redefine_char: Redefine the actual pixel matrix of character.
|
||||
*/
|
||||
struct charlcd_ops {
|
||||
/* Required */
|
||||
void (*write_cmd)(struct charlcd *lcd, int cmd);
|
||||
void (*write_data)(struct charlcd *lcd, int data);
|
||||
|
||||
/* Optional */
|
||||
void (*write_cmd_raw4)(struct charlcd *lcd, int cmd); /* 4-bit only */
|
||||
void (*clear_fast)(struct charlcd *lcd);
|
||||
void (*backlight)(struct charlcd *lcd, int on);
|
||||
void (*backlight)(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int (*print)(struct charlcd *lcd, int c);
|
||||
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);
|
||||
int (*shift_cursor)(struct charlcd *lcd, enum charlcd_shift_dir dir);
|
||||
int (*shift_display)(struct charlcd *lcd, enum charlcd_shift_dir dir);
|
||||
int (*display)(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int (*cursor)(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int (*blink)(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int (*fontsize)(struct charlcd *lcd, enum charlcd_fontsize size);
|
||||
int (*lines)(struct charlcd *lcd, enum charlcd_lines lines);
|
||||
int (*redefine_char)(struct charlcd *lcd, char *esc);
|
||||
};
|
||||
|
||||
struct charlcd *charlcd_alloc(unsigned int drvdata_size);
|
||||
void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
struct charlcd *charlcd_alloc(void);
|
||||
void charlcd_free(struct charlcd *lcd);
|
||||
|
||||
int charlcd_register(struct charlcd *lcd);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/slab.h>
|
||||
|
||||
#include "charlcd.h"
|
||||
#include "hd44780_common.h"
|
||||
|
||||
enum hd44780_pin {
|
||||
/* Order does matter due to writing to GPIO array subsets! */
|
||||
|
@ -37,9 +38,10 @@ 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;
|
||||
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);
|
||||
|
@ -101,9 +103,9 @@ static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
|
|||
}
|
||||
|
||||
/* Send a command to the LCD panel in 8 bit GPIO mode */
|
||||
static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
|
||||
static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
struct hd44780 *hd = hdc->hd44780;
|
||||
|
||||
hd44780_write_gpio8(hd, cmd, 0);
|
||||
|
||||
|
@ -112,9 +114,9 @@ static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
|
|||
}
|
||||
|
||||
/* Send data to the LCD panel in 8 bit GPIO mode */
|
||||
static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
|
||||
static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)
|
||||
{
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
struct hd44780 *hd = hdc->hd44780;
|
||||
|
||||
hd44780_write_gpio8(hd, data, 1);
|
||||
|
||||
|
@ -123,15 +125,26 @@ 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,
|
||||
.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,
|
||||
};
|
||||
|
||||
/* Send a command to the LCD panel in 4 bit GPIO mode */
|
||||
static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
|
||||
static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
struct hd44780 *hd = hdc->hd44780;
|
||||
|
||||
hd44780_write_gpio4(hd, cmd, 0);
|
||||
|
||||
|
@ -140,10 +153,10 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
|
|||
}
|
||||
|
||||
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
|
||||
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
|
||||
static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
struct hd44780 *hd = hdc->hd44780;
|
||||
unsigned int n;
|
||||
|
||||
/* Command nibble + RS, RW */
|
||||
|
@ -157,9 +170,9 @@ static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
|
|||
}
|
||||
|
||||
/* Send data to the LCD panel in 4 bit GPIO mode */
|
||||
static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
|
||||
static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)
|
||||
{
|
||||
struct hd44780 *hd = lcd->drvdata;
|
||||
struct hd44780 *hd = hdc->hd44780;
|
||||
|
||||
hd44780_write_gpio4(hd, data, 1);
|
||||
|
||||
|
@ -168,10 +181,20 @@ static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
|
|||
}
|
||||
|
||||
static const struct charlcd_ops hd44780_ops_gpio4 = {
|
||||
.write_cmd = hd44780_write_cmd_gpio4,
|
||||
.write_cmd_raw4 = hd44780_write_cmd_raw_gpio4,
|
||||
.write_data = hd44780_write_data_gpio4,
|
||||
.backlight = hd44780_backlight,
|
||||
.print = hd44780_common_print,
|
||||
.gotoxy = hd44780_common_gotoxy,
|
||||
.home = hd44780_common_home,
|
||||
.clear_display = hd44780_common_clear_display,
|
||||
.init_display = hd44780_common_init_display,
|
||||
.shift_cursor = hd44780_common_shift_cursor,
|
||||
.shift_display = hd44780_common_shift_display,
|
||||
.display = hd44780_common_display,
|
||||
.cursor = hd44780_common_cursor,
|
||||
.blink = hd44780_common_blink,
|
||||
.fontsize = hd44780_common_fontsize,
|
||||
.lines = hd44780_common_lines,
|
||||
.redefine_char = hd44780_common_redefine_char,
|
||||
};
|
||||
|
||||
static int hd44780_probe(struct platform_device *pdev)
|
||||
|
@ -179,8 +202,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 +222,39 @@ static int hd44780_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
lcd = charlcd_alloc(sizeof(struct hd44780));
|
||||
if (!lcd)
|
||||
hdc = hd44780_common_alloc();
|
||||
if (!hdc)
|
||||
return -ENOMEM;
|
||||
|
||||
hd = lcd->drvdata;
|
||||
lcd = charlcd_alloc();
|
||||
if (!lcd)
|
||||
goto fail1;
|
||||
|
||||
hd = kzalloc(sizeof(struct hd44780), GFP_KERNEL);
|
||||
if (!hd)
|
||||
goto fail2;
|
||||
|
||||
hdc->hd44780 = hd;
|
||||
lcd->drvdata = hdc;
|
||||
for (i = 0; i < ifwidth; i++) {
|
||||
hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hd->pins[base + i])) {
|
||||
ret = PTR_ERR(hd->pins[base + i]);
|
||||
goto fail;
|
||||
goto fail3;
|
||||
}
|
||||
}
|
||||
|
||||
hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hd->pins[PIN_CTRL_E])) {
|
||||
ret = PTR_ERR(hd->pins[PIN_CTRL_E]);
|
||||
goto fail;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(hd->pins[PIN_CTRL_RS])) {
|
||||
ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);
|
||||
goto fail;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Optional pins */
|
||||
|
@ -230,47 +262,60 @@ static int hd44780_probe(struct platform_device *pdev)
|
|||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
|
||||
ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
|
||||
goto fail;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hd->pins[PIN_CTRL_BL])) {
|
||||
ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);
|
||||
goto fail;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Required properties */
|
||||
ret = device_property_read_u32(dev, "display-height-chars",
|
||||
&lcd->height);
|
||||
if (ret)
|
||||
goto fail;
|
||||
goto fail3;
|
||||
ret = device_property_read_u32(dev, "display-width-chars", &lcd->width);
|
||||
if (ret)
|
||||
goto fail;
|
||||
goto fail3;
|
||||
|
||||
/*
|
||||
* On displays with more than two rows, the internal buffer width is
|
||||
* usually equal to the display width
|
||||
*/
|
||||
if (lcd->height > 2)
|
||||
lcd->bwidth = lcd->width;
|
||||
hdc->bwidth = lcd->width;
|
||||
|
||||
/* Optional properties */
|
||||
device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
|
||||
device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);
|
||||
|
||||
lcd->ifwidth = ifwidth;
|
||||
lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
|
||||
hdc->ifwidth = ifwidth;
|
||||
if (ifwidth == 8) {
|
||||
lcd->ops = &hd44780_ops_gpio8;
|
||||
hdc->write_data = hd44780_write_data_gpio8;
|
||||
hdc->write_cmd = hd44780_write_cmd_gpio8;
|
||||
} else {
|
||||
lcd->ops = &hd44780_ops_gpio4;
|
||||
hdc->write_data = hd44780_write_data_gpio4;
|
||||
hdc->write_cmd = hd44780_write_cmd_gpio4;
|
||||
hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4;
|
||||
}
|
||||
|
||||
ret = charlcd_register(lcd);
|
||||
if (ret)
|
||||
goto fail;
|
||||
goto fail3;
|
||||
|
||||
platform_set_drvdata(pdev, lcd);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
charlcd_free(lcd);
|
||||
fail3:
|
||||
kfree(hd);
|
||||
fail2:
|
||||
kfree(lcd);
|
||||
fail1:
|
||||
kfree(hdc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -278,9 +323,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "charlcd.h"
|
||||
#include "hd44780_common.h"
|
||||
|
||||
/* LCD commands */
|
||||
#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
|
||||
|
||||
#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
|
||||
#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
|
||||
|
||||
#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
|
||||
#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
|
||||
#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
|
||||
#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
|
||||
|
||||
#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
|
||||
#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
|
||||
#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
|
||||
|
||||
#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
|
||||
#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
|
||||
#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
|
||||
#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
|
||||
|
||||
#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
|
||||
|
||||
#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
|
||||
|
||||
/* sleeps that many milliseconds with a reschedule */
|
||||
static void long_sleep(int ms)
|
||||
{
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(ms));
|
||||
}
|
||||
|
||||
int hd44780_common_print(struct charlcd *lcd, int c)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (lcd->addr.x < hdc->bwidth) {
|
||||
hdc->write_data(hdc, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_print);
|
||||
|
||||
int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
|
||||
{
|
||||
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 = x < hdc->bwidth ? x & (hdc->hwidth - 1) : hdc->bwidth - 1;
|
||||
if (y & 1)
|
||||
addr += hdc->hwidth;
|
||||
if (y & 2)
|
||||
addr += hdc->bwidth;
|
||||
hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);
|
||||
|
||||
int hd44780_common_home(struct charlcd *lcd)
|
||||
{
|
||||
return hd44780_common_gotoxy(lcd, 0, 0);
|
||||
}
|
||||
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);
|
||||
/* datasheet says to wait 1,64 milliseconds */
|
||||
long_sleep(2);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
|
||||
|
||||
int hd44780_common_init_display(struct charlcd *lcd)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
|
||||
u8 init;
|
||||
|
||||
if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
|
||||
return -EINVAL;
|
||||
|
||||
hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) |
|
||||
LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
|
||||
|
||||
long_sleep(20); /* wait 20 ms after power-up for the paranoid */
|
||||
|
||||
/*
|
||||
* 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
|
||||
* the LCD is in 8-bit mode afterwards
|
||||
*/
|
||||
init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
|
||||
if (hdc->ifwidth == 4) {
|
||||
init >>= 4;
|
||||
write_cmd_raw = hdc->write_cmd_raw4;
|
||||
} else {
|
||||
write_cmd_raw = hdc->write_cmd;
|
||||
}
|
||||
write_cmd_raw(hdc, init);
|
||||
long_sleep(10);
|
||||
write_cmd_raw(hdc, init);
|
||||
long_sleep(10);
|
||||
write_cmd_raw(hdc, init);
|
||||
long_sleep(10);
|
||||
|
||||
if (hdc->ifwidth == 4) {
|
||||
/* Switch to 4-bit mode, 1 line, small fonts */
|
||||
hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
|
||||
long_sleep(10);
|
||||
}
|
||||
|
||||
/* set font height and lines number */
|
||||
hdc->write_cmd(hdc,
|
||||
LCD_CMD_FUNCTION_SET |
|
||||
((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_F) ?
|
||||
LCD_CMD_FONT_5X10_DOTS : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_N) ?
|
||||
LCD_CMD_TWO_LINES : 0));
|
||||
long_sleep(10);
|
||||
|
||||
/* display off, cursor off, blink off */
|
||||
hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
|
||||
long_sleep(10);
|
||||
|
||||
hdc->write_cmd(hdc,
|
||||
LCD_CMD_DISPLAY_CTRL | /* set display mode */
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_D) ?
|
||||
LCD_CMD_DISPLAY_ON : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_C) ?
|
||||
LCD_CMD_CURSOR_ON : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_B) ?
|
||||
LCD_CMD_BLINK_ON : 0));
|
||||
|
||||
charlcd_backlight(lcd,
|
||||
(hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0);
|
||||
|
||||
long_sleep(10);
|
||||
|
||||
/* entry mode set : increment, cursor shifting */
|
||||
hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
|
||||
|
||||
hd44780_common_clear_display(lcd);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_init_display);
|
||||
|
||||
int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (dir == CHARLCD_SHIFT_LEFT) {
|
||||
/* back one char if not at end of line */
|
||||
if (lcd->addr.x < hdc->bwidth)
|
||||
hdc->write_cmd(hdc, LCD_CMD_SHIFT);
|
||||
} else if (dir == CHARLCD_SHIFT_RIGHT) {
|
||||
/* allow the cursor to pass the end of the line */
|
||||
if (lcd->addr.x < (hdc->bwidth - 1))
|
||||
hdc->write_cmd(hdc,
|
||||
LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor);
|
||||
|
||||
int hd44780_common_shift_display(struct charlcd *lcd,
|
||||
enum charlcd_shift_dir dir)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (dir == CHARLCD_SHIFT_LEFT)
|
||||
hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
|
||||
else if (dir == CHARLCD_SHIFT_RIGHT)
|
||||
hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
|
||||
LCD_CMD_SHIFT_RIGHT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_shift_display);
|
||||
|
||||
static void hd44780_common_set_mode(struct hd44780_common *hdc)
|
||||
{
|
||||
hdc->write_cmd(hdc,
|
||||
LCD_CMD_DISPLAY_CTRL |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_D) ?
|
||||
LCD_CMD_DISPLAY_ON : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_C) ?
|
||||
LCD_CMD_CURSOR_ON : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_B) ?
|
||||
LCD_CMD_BLINK_ON : 0));
|
||||
}
|
||||
|
||||
int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (on == CHARLCD_ON)
|
||||
hdc->hd44780_common_flags |= LCD_FLAG_D;
|
||||
else
|
||||
hdc->hd44780_common_flags &= ~LCD_FLAG_D;
|
||||
|
||||
hd44780_common_set_mode(hdc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_display);
|
||||
|
||||
int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (on == CHARLCD_ON)
|
||||
hdc->hd44780_common_flags |= LCD_FLAG_C;
|
||||
else
|
||||
hdc->hd44780_common_flags &= ~LCD_FLAG_C;
|
||||
|
||||
hd44780_common_set_mode(hdc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_cursor);
|
||||
|
||||
int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (on == CHARLCD_ON)
|
||||
hdc->hd44780_common_flags |= LCD_FLAG_B;
|
||||
else
|
||||
hdc->hd44780_common_flags &= ~LCD_FLAG_B;
|
||||
|
||||
hd44780_common_set_mode(hdc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_blink);
|
||||
|
||||
static void hd44780_common_set_function(struct hd44780_common *hdc)
|
||||
{
|
||||
hdc->write_cmd(hdc,
|
||||
LCD_CMD_FUNCTION_SET |
|
||||
((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_F) ?
|
||||
LCD_CMD_FONT_5X10_DOTS : 0) |
|
||||
((hdc->hd44780_common_flags & LCD_FLAG_N) ?
|
||||
LCD_CMD_TWO_LINES : 0));
|
||||
}
|
||||
|
||||
int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (size == CHARLCD_FONTSIZE_LARGE)
|
||||
hdc->hd44780_common_flags |= LCD_FLAG_F;
|
||||
else
|
||||
hdc->hd44780_common_flags &= ~LCD_FLAG_F;
|
||||
|
||||
hd44780_common_set_function(hdc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_fontsize);
|
||||
|
||||
int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines)
|
||||
{
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
|
||||
if (lines == CHARLCD_LINES_2)
|
||||
hdc->hd44780_common_flags |= LCD_FLAG_N;
|
||||
else
|
||||
hdc->hd44780_common_flags &= ~LCD_FLAG_N;
|
||||
|
||||
hd44780_common_set_function(hdc);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_lines);
|
||||
|
||||
int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
|
||||
{
|
||||
/* Generator : LGcxxxxx...xx; must have <c> between '0'
|
||||
* and '7', representing the numerical ASCII code of the
|
||||
* redefined character, and <xx...xx> a sequence of 16
|
||||
* hex digits representing 8 bytes for each character.
|
||||
* Most LCDs will only use 5 lower bits of the 7 first
|
||||
* bytes.
|
||||
*/
|
||||
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
unsigned char cgbytes[8];
|
||||
unsigned char cgaddr;
|
||||
int cgoffset;
|
||||
int shift;
|
||||
char value;
|
||||
int addr;
|
||||
|
||||
if (!strchr(esc, ';'))
|
||||
return 0;
|
||||
|
||||
esc++;
|
||||
|
||||
cgaddr = *(esc++) - '0';
|
||||
if (cgaddr > 7)
|
||||
return 1;
|
||||
|
||||
cgoffset = 0;
|
||||
shift = 0;
|
||||
value = 0;
|
||||
while (*esc && cgoffset < 8) {
|
||||
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, lcd->addr.x, lcd->addr.y);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
|
||||
|
||||
struct hd44780_common *hd44780_common_alloc(void)
|
||||
{
|
||||
struct hd44780_common *hd;
|
||||
|
||||
hd = kzalloc(sizeof(*hd), GFP_KERNEL);
|
||||
if (!hd)
|
||||
return NULL;
|
||||
|
||||
hd->ifwidth = 8;
|
||||
hd->bwidth = DEFAULT_LCD_BWIDTH;
|
||||
hd->hwidth = DEFAULT_LCD_HWIDTH;
|
||||
return hd;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hd44780_common_alloc);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#define DEFAULT_LCD_BWIDTH 40
|
||||
#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() */
|
||||
unsigned long hd44780_common_flags;
|
||||
void (*write_data)(struct hd44780_common *hdc, int data);
|
||||
void (*write_cmd)(struct hd44780_common *hdc, int cmd);
|
||||
/* write_cmd_raw4 is for 4-bit connected displays only */
|
||||
void (*write_cmd_raw4)(struct hd44780_common *hdc, int cmd);
|
||||
void *hd44780;
|
||||
};
|
||||
|
||||
int hd44780_common_print(struct charlcd *lcd, int c);
|
||||
int hd44780_common_gotoxy(struct charlcd *lcd, 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);
|
||||
int hd44780_common_shift_cursor(struct charlcd *lcd,
|
||||
enum charlcd_shift_dir dir);
|
||||
int hd44780_common_shift_display(struct charlcd *lcd,
|
||||
enum charlcd_shift_dir dir);
|
||||
int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on);
|
||||
int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size);
|
||||
int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines);
|
||||
int hd44780_common_redefine_char(struct charlcd *lcd, char *esc);
|
||||
struct hd44780_common *hd44780_common_alloc(void);
|
|
@ -0,0 +1,402 @@
|
|||
// 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 <poeschel@lemonage.de>
|
||||
* All rights reserved.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "charlcd.h"
|
||||
|
||||
#define LCD2S_CMD_CUR_MOVES_FWD 0x09
|
||||
#define LCD2S_CMD_CUR_BLINK_OFF 0x10
|
||||
#define LCD2S_CMD_CUR_UL_OFF 0x11
|
||||
#define LCD2S_CMD_DISPLAY_OFF 0x12
|
||||
#define LCD2S_CMD_CUR_BLINK_ON 0x18
|
||||
#define LCD2S_CMD_CUR_UL_ON 0x19
|
||||
#define LCD2S_CMD_DISPLAY_ON 0x1a
|
||||
#define LCD2S_CMD_BACKLIGHT_OFF 0x20
|
||||
#define LCD2S_CMD_BACKLIGHT_ON 0x28
|
||||
#define LCD2S_CMD_WRITE 0x80
|
||||
#define LCD2S_CMD_MOV_CUR_RIGHT 0x83
|
||||
#define LCD2S_CMD_MOV_CUR_LEFT 0x84
|
||||
#define LCD2S_CMD_SHIFT_RIGHT 0x85
|
||||
#define LCD2S_CMD_SHIFT_LEFT 0x86
|
||||
#define LCD2S_CMD_SHIFT_UP 0x87
|
||||
#define LCD2S_CMD_SHIFT_DOWN 0x88
|
||||
#define LCD2S_CMD_CUR_ADDR 0x89
|
||||
#define LCD2S_CMD_CUR_POS 0x8a
|
||||
#define LCD2S_CMD_CUR_RESET 0x8b
|
||||
#define LCD2S_CMD_CLEAR 0x8c
|
||||
#define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92
|
||||
#define LCD2S_CMD_READ_STATUS 0xd0
|
||||
|
||||
#define LCD2S_CHARACTER_SIZE 8
|
||||
|
||||
#define LCD2S_STATUS_BUF_MASK 0x7f
|
||||
|
||||
struct lcd2s_data {
|
||||
struct i2c_client *i2c;
|
||||
struct charlcd *charlcd;
|
||||
};
|
||||
|
||||
static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
while ((status & LCD2S_STATUS_BUF_MASK) < count) {
|
||||
mdelay(1);
|
||||
status = i2c_smbus_read_byte_data(client,
|
||||
LCD2S_CMD_READ_STATUS);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_i2c_master_send(const struct i2c_client *client,
|
||||
const char *buf, int count)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = lcd2s_wait_buf_free(client, count);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return i2c_master_send(client, buf, count);
|
||||
}
|
||||
|
||||
static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = lcd2s_wait_buf_free(client, 1);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return i2c_smbus_write_byte(client, value);
|
||||
}
|
||||
|
||||
static int lcd2s_print(struct charlcd *lcd, int c)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
u8 buf[2] = { LCD2S_CMD_WRITE, c };
|
||||
|
||||
lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
u8 buf[] = { LCD2S_CMD_CUR_POS, y + 1, x + 1};
|
||||
|
||||
lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_home(struct charlcd *lcd)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_init_display(struct charlcd *lcd)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
/* turn everything off, but display on */
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD);
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (dir == CHARLCD_SHIFT_LEFT)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (dir == CHARLCD_SHIFT_LEFT)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (on)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
|
||||
}
|
||||
|
||||
static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (on)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (on)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on)
|
||||
{
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
|
||||
if (on)
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON);
|
||||
else
|
||||
lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcd2s_redefine_char(struct charlcd *lcd, char *esc)
|
||||
{
|
||||
/* Generator : LGcxxxxx...xx; must have <c> between '0'
|
||||
* and '7', representing the numerical ASCII code of the
|
||||
* redefined character, and <xx...xx> a sequence of 16
|
||||
* hex digits representing 8 bytes for each character.
|
||||
* Most LCDs will only use 5 lower bits of the 7 first
|
||||
* bytes.
|
||||
*/
|
||||
|
||||
struct lcd2s_data *lcd2s = lcd->drvdata;
|
||||
u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR };
|
||||
u8 value;
|
||||
int shift, i;
|
||||
|
||||
if (!strchr(esc, ';'))
|
||||
return 0;
|
||||
|
||||
esc++;
|
||||
|
||||
buf[1] = *(esc++) - '0';
|
||||
if (buf[1] > 7)
|
||||
return 1;
|
||||
|
||||
i = 0;
|
||||
shift = 0;
|
||||
value = 0;
|
||||
while (*esc && i < LCD2S_CHARACTER_SIZE + 2) {
|
||||
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);
|
||||
|
||||
charlcd_unregister(lcd2s->charlcd);
|
||||
kfree(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",
|
||||
#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");
|
|
@ -56,6 +56,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
|
||||
#include "charlcd.h"
|
||||
#include "hd44780_common.h"
|
||||
|
||||
#define LCD_MAXBYTES 256 /* max burst write */
|
||||
|
||||
|
@ -298,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
|
||||
|
||||
|
@ -708,7 +707,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;
|
||||
|
@ -724,7 +723,7 @@ static void lcd_backlight(struct charlcd *charlcd, int on)
|
|||
}
|
||||
|
||||
/* send a command to the LCD panel in serial mode */
|
||||
static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd)
|
||||
static void lcd_write_cmd_s(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
spin_lock_irq(&pprt_lock);
|
||||
lcd_send_serial(0x1F); /* R/W=W, RS=0 */
|
||||
|
@ -735,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 */
|
||||
|
@ -746,7 +745,7 @@ static void lcd_write_data_s(struct charlcd *charlcd, int data)
|
|||
}
|
||||
|
||||
/* send a command to the LCD panel in 8 bits parallel mode */
|
||||
static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd)
|
||||
static void lcd_write_cmd_p8(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
spin_lock_irq(&pprt_lock);
|
||||
/* present the data to the data port */
|
||||
|
@ -768,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 */
|
||||
|
@ -790,7 +789,7 @@ static void lcd_write_data_p8(struct charlcd *charlcd, int data)
|
|||
}
|
||||
|
||||
/* send a command to the TI LCD panel */
|
||||
static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd)
|
||||
static void lcd_write_cmd_tilcd(struct hd44780_common *hdc, int cmd)
|
||||
{
|
||||
spin_lock_irq(&pprt_lock);
|
||||
/* present the data to the control port */
|
||||
|
@ -800,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 */
|
||||
|
@ -809,105 +808,50 @@ static void lcd_write_data_tilcd(struct charlcd *charlcd, int data)
|
|||
spin_unlock_irq(&pprt_lock);
|
||||
}
|
||||
|
||||
/* fills the display with spaces and resets X/Y */
|
||||
static void lcd_clear_fast_s(struct charlcd *charlcd)
|
||||
{
|
||||
int pos;
|
||||
|
||||
spin_lock_irq(&pprt_lock);
|
||||
for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
|
||||
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
|
||||
lcd_send_serial(' ' & 0x0F);
|
||||
lcd_send_serial((' ' >> 4) & 0x0F);
|
||||
/* the shortest data takes at least 40 us */
|
||||
udelay(40);
|
||||
}
|
||||
spin_unlock_irq(&pprt_lock);
|
||||
}
|
||||
|
||||
/* fills the display with spaces and resets X/Y */
|
||||
static void lcd_clear_fast_p8(struct charlcd *charlcd)
|
||||
{
|
||||
int pos;
|
||||
|
||||
spin_lock_irq(&pprt_lock);
|
||||
for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
|
||||
/* present the data to the data port */
|
||||
w_dtr(pprt, ' ');
|
||||
|
||||
/* maintain the data during 20 us before the strobe */
|
||||
udelay(20);
|
||||
|
||||
set_bit(LCD_BIT_E, bits);
|
||||
set_bit(LCD_BIT_RS, bits);
|
||||
clear_bit(LCD_BIT_RW, bits);
|
||||
set_ctrl_bits();
|
||||
|
||||
/* maintain the strobe during 40 us */
|
||||
udelay(40);
|
||||
|
||||
clear_bit(LCD_BIT_E, bits);
|
||||
set_ctrl_bits();
|
||||
|
||||
/* the shortest data takes at least 45 us */
|
||||
udelay(45);
|
||||
}
|
||||
spin_unlock_irq(&pprt_lock);
|
||||
}
|
||||
|
||||
/* fills the display with spaces and resets X/Y */
|
||||
static void lcd_clear_fast_tilcd(struct charlcd *charlcd)
|
||||
{
|
||||
int pos;
|
||||
|
||||
spin_lock_irq(&pprt_lock);
|
||||
for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) {
|
||||
/* present the data to the data port */
|
||||
w_dtr(pprt, ' ');
|
||||
udelay(60);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&pprt_lock);
|
||||
}
|
||||
|
||||
static const struct charlcd_ops charlcd_serial_ops = {
|
||||
.write_cmd = lcd_write_cmd_s,
|
||||
.write_data = lcd_write_data_s,
|
||||
.clear_fast = lcd_clear_fast_s,
|
||||
.backlight = lcd_backlight,
|
||||
};
|
||||
|
||||
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,
|
||||
static const struct charlcd_ops charlcd_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,
|
||||
};
|
||||
|
||||
/* initialize the LCD driver */
|
||||
static void lcd_init(void)
|
||||
{
|
||||
struct charlcd *charlcd;
|
||||
struct hd44780_common *hdc;
|
||||
|
||||
charlcd = charlcd_alloc(0);
|
||||
if (!charlcd)
|
||||
hdc = hd44780_common_alloc();
|
||||
if (!hdc)
|
||||
return;
|
||||
|
||||
charlcd = charlcd_alloc();
|
||||
if (!charlcd) {
|
||||
kfree(hdc);
|
||||
return;
|
||||
}
|
||||
|
||||
hdc->hd44780 = &lcd;
|
||||
charlcd->drvdata = hdc;
|
||||
|
||||
/*
|
||||
* Init lcd struct with load-time values to preserve exact
|
||||
* current functionality (at least for now).
|
||||
*/
|
||||
charlcd->height = lcd_height;
|
||||
charlcd->width = lcd_width;
|
||||
charlcd->bwidth = lcd_bwidth;
|
||||
charlcd->hwidth = lcd_hwidth;
|
||||
hdc->bwidth = lcd_bwidth;
|
||||
hdc->hwidth = lcd_hwidth;
|
||||
|
||||
switch (selected_lcd_type) {
|
||||
case LCD_TYPE_OLD:
|
||||
|
@ -918,8 +862,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:
|
||||
|
@ -931,8 +875,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:
|
||||
|
@ -944,8 +888,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:
|
||||
|
@ -963,8 +907,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;
|
||||
}
|
||||
|
@ -975,9 +919,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)
|
||||
|
@ -998,15 +942,17 @@ static void lcd_init(void)
|
|||
/* this is used to catch wrong and default values */
|
||||
if (charlcd->width <= 0)
|
||||
charlcd->width = DEFAULT_LCD_WIDTH;
|
||||
if (charlcd->bwidth <= 0)
|
||||
charlcd->bwidth = DEFAULT_LCD_BWIDTH;
|
||||
if (charlcd->hwidth <= 0)
|
||||
charlcd->hwidth = DEFAULT_LCD_HWIDTH;
|
||||
if (hdc->bwidth <= 0)
|
||||
hdc->bwidth = DEFAULT_LCD_BWIDTH;
|
||||
if (hdc->hwidth <= 0)
|
||||
hdc->hwidth = DEFAULT_LCD_HWIDTH;
|
||||
if (charlcd->height <= 0)
|
||||
charlcd->height = DEFAULT_LCD_HEIGHT;
|
||||
|
||||
if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
|
||||
charlcd->ops = &charlcd_serial_ops;
|
||||
charlcd->ops = &charlcd_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;
|
||||
|
@ -1014,7 +960,9 @@ 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;
|
||||
|
||||
if (lcd.pins.e == PIN_NOT_SET)
|
||||
lcd.pins.e = DEFAULT_LCD_PIN_E;
|
||||
|
@ -1023,7 +971,9 @@ 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;
|
||||
}
|
||||
|
||||
if (lcd.pins.bl == PIN_NOT_SET)
|
||||
|
@ -1620,7 +1570,7 @@ err_lcd_unreg:
|
|||
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 +1597,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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче