2020-11-03 12:58:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
# include <linux/module.h>
2020-11-03 12:58:14 +03:00
# include <linux/sched.h>
2020-11-03 12:58:05 +03:00
# include <linux/slab.h>
2020-11-03 12:58:11 +03:00
# include "charlcd.h"
2020-11-03 12:58:05 +03:00
# include "hd44780_common.h"
2020-11-03 12:58:12 +03:00
/* LCD commands */
2020-11-03 12:58:14 +03:00
# define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
2020-11-03 12:58:17 +03:00
# 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 */
2020-11-03 12:58:18 +03:00
# 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 */
2020-11-03 12:58:17 +03:00
# 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 */
2020-11-03 12:58:20 +03:00
# define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
2020-11-03 12:58:12 +03:00
# define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
2020-11-03 12:58:14 +03:00
/* sleeps that many milliseconds with a reschedule */
static void long_sleep ( int ms )
{
schedule_timeout_interruptible ( msecs_to_jiffies ( ms ) ) ;
}
2020-11-03 12:58:11 +03:00
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 ) ;
2020-11-03 12:58:25 +03:00
int hd44780_common_gotoxy ( struct charlcd * lcd , unsigned int x , unsigned int y )
2020-11-03 12:58:12 +03:00
{
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
*/
2020-11-03 12:58:25 +03:00
addr = x < hdc - > bwidth ? x & ( hdc - > hwidth - 1 ) : hdc - > bwidth - 1 ;
if ( y & 1 )
2020-11-03 12:58:12 +03:00
addr + = hdc - > hwidth ;
2020-11-03 12:58:25 +03:00
if ( y & 2 )
2020-11-03 12:58:12 +03:00
addr + = hdc - > bwidth ;
hdc - > write_cmd ( hdc , LCD_CMD_SET_DDRAM_ADDR | addr ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( hd44780_common_gotoxy ) ;
2020-11-03 12:58:13 +03:00
int hd44780_common_home ( struct charlcd * lcd )
{
2020-11-03 12:58:25 +03:00
return hd44780_common_gotoxy ( lcd , 0 , 0 ) ;
2020-11-03 12:58:13 +03:00
}
EXPORT_SYMBOL_GPL ( hd44780_common_home ) ;
2020-11-03 12:58:14 +03:00
/* 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 ) ;
2020-11-03 12:58:22 +03:00
/* datasheet says to wait 1,64 milliseconds */
long_sleep ( 2 ) ;
2020-11-03 12:58:14 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( hd44780_common_clear_display ) ;
2020-11-03 12:58:17 +03:00
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 ) ;
2020-11-03 12:58:18 +03:00
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 ) ;
2020-11-03 12:58:20 +03:00
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 */
2020-11-03 12:58:25 +03:00
lcd - > ops - > gotoxy ( lcd , lcd - > addr . x , lcd - > addr . y ) ;
2020-11-03 12:58:20 +03:00
return 1 ;
}
EXPORT_SYMBOL_GPL ( hd44780_common_redefine_char ) ;
2020-11-03 12:58:05 +03:00
struct hd44780_common * hd44780_common_alloc ( void )
{
struct hd44780_common * hd ;
hd = kzalloc ( sizeof ( * hd ) , GFP_KERNEL ) ;
if ( ! hd )
return NULL ;
2020-11-03 12:58:07 +03:00
hd - > ifwidth = 8 ;
2020-11-03 12:58:06 +03:00
hd - > bwidth = DEFAULT_LCD_BWIDTH ;
hd - > hwidth = DEFAULT_LCD_HWIDTH ;
2020-11-03 12:58:05 +03:00
return hd ;
}
EXPORT_SYMBOL_GPL ( hd44780_common_alloc ) ;
MODULE_LICENSE ( " GPL " ) ;