2018-02-17 22:33:13 +03:00
// SPDX-License-Identifier: GPL-2.0+
2017-03-10 17:15:21 +03:00
/*
* HD44780 Character LCD driver for Linux
*
* Copyright ( C ) 2000 - 2008 , Willy Tarreau < w @ 1 wt . eu >
* Copyright ( C ) 2016 - 2017 Glider bvba
*/
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/module.h>
2018-06-20 08:47:28 +03:00
# include <linux/mod_devicetable.h>
2017-03-10 17:15:21 +03:00
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/slab.h>
2019-08-06 10:14:44 +03:00
# include "charlcd.h"
2020-11-03 12:58:05 +03:00
# include "hd44780_common.h"
2017-03-10 17:15:21 +03:00
enum hd44780_pin {
/* Order does matter due to writing to GPIO array subsets! */
PIN_DATA0 , /* Optional */
PIN_DATA1 , /* Optional */
PIN_DATA2 , /* Optional */
PIN_DATA3 , /* Optional */
PIN_DATA4 ,
PIN_DATA5 ,
PIN_DATA6 ,
PIN_DATA7 ,
PIN_CTRL_RS ,
PIN_CTRL_RW , /* Optional */
PIN_CTRL_E ,
PIN_CTRL_BL , /* Optional */
PIN_NUM
} ;
struct hd44780 {
struct gpio_desc * pins [ PIN_NUM ] ;
} ;
2020-11-03 12:58:04 +03:00
static void hd44780_backlight ( struct charlcd * lcd , enum charlcd_onoff on )
2017-03-10 17:15:21 +03:00
{
2020-11-03 12:58:06 +03:00
struct hd44780_common * hdc = lcd - > drvdata ;
struct hd44780 * hd = hdc - > hd44780 ;
2017-03-10 17:15:21 +03:00
if ( hd - > pins [ PIN_CTRL_BL ] )
gpiod_set_value_cansleep ( hd - > pins [ PIN_CTRL_BL ] , on ) ;
}
static void hd44780_strobe_gpio ( struct hd44780 * hd )
{
/* Maintain the data during 20 us before the strobe */
udelay ( 20 ) ;
gpiod_set_value_cansleep ( hd - > pins [ PIN_CTRL_E ] , 1 ) ;
/* Maintain the strobe during 40 us */
udelay ( 40 ) ;
gpiod_set_value_cansleep ( hd - > pins [ PIN_CTRL_E ] , 0 ) ;
}
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8 ( struct hd44780 * hd , u8 val , unsigned int rs )
{
2018-09-06 00:50:05 +03:00
DECLARE_BITMAP ( values , 10 ) ; /* for DATA[0-7], RS, RW */
unsigned int n ;
values [ 0 ] = val ;
__assign_bit ( 8 , values , rs ) ;
n = hd - > pins [ PIN_CTRL_RW ] ? 10 : 9 ;
2017-03-10 17:15:21 +03:00
/* Present the data to the port */
2018-09-06 00:50:07 +03:00
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA0 ] , NULL , values ) ;
2017-03-10 17:15:21 +03:00
hd44780_strobe_gpio ( hd ) ;
}
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4 ( struct hd44780 * hd , u8 val , unsigned int rs )
{
2018-09-06 00:50:05 +03:00
DECLARE_BITMAP ( values , 6 ) ; /* for DATA[4-7], RS, RW */
unsigned int n ;
2017-03-10 17:15:21 +03:00
/* High nibble + RS, RW */
2018-09-06 00:50:05 +03:00
values [ 0 ] = val > > 4 ;
__assign_bit ( 4 , values , rs ) ;
n = hd - > pins [ PIN_CTRL_RW ] ? 6 : 5 ;
2017-03-10 17:15:21 +03:00
/* Present the data to the port */
2018-09-06 00:50:07 +03:00
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] , NULL , values ) ;
2017-03-10 17:15:21 +03:00
hd44780_strobe_gpio ( hd ) ;
/* Low nibble */
2018-09-06 00:50:05 +03:00
values [ 0 ] & = ~ 0x0fUL ;
values [ 0 ] | = val & 0x0f ;
2017-03-10 17:15:21 +03:00
/* Present the data to the port */
2018-09-06 00:50:07 +03:00
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] , NULL , values ) ;
2017-03-10 17:15:21 +03:00
hd44780_strobe_gpio ( hd ) ;
}
/* Send a command to the LCD panel in 8 bit GPIO mode */
2020-11-03 12:58:09 +03:00
static void hd44780_write_cmd_gpio8 ( struct hd44780_common * hdc , int cmd )
2017-03-10 17:15:21 +03:00
{
2020-11-03 12:58:06 +03:00
struct hd44780 * hd = hdc - > hd44780 ;
2017-03-10 17:15:21 +03:00
hd44780_write_gpio8 ( hd , cmd , 0 ) ;
/* The shortest command takes at least 120 us */
udelay ( 120 ) ;
}
/* Send data to the LCD panel in 8 bit GPIO mode */
2020-11-03 12:58:08 +03:00
static void hd44780_write_data_gpio8 ( struct hd44780_common * hdc , int data )
2017-03-10 17:15:21 +03:00
{
2020-11-03 12:58:06 +03:00
struct hd44780 * hd = hdc - > hd44780 ;
2017-03-10 17:15:21 +03:00
hd44780_write_gpio8 ( hd , data , 1 ) ;
/* The shortest data takes at least 45 us */
udelay ( 45 ) ;
}
static const struct charlcd_ops hd44780_ops_gpio8 = {
. backlight = hd44780_backlight ,
2020-11-03 12:58:11 +03:00
. print = hd44780_common_print ,
2020-11-03 12:58:12 +03:00
. gotoxy = hd44780_common_gotoxy ,
2020-11-03 12:58:13 +03:00
. home = hd44780_common_home ,
2020-11-03 12:58:14 +03:00
. clear_display = hd44780_common_clear_display ,
2020-11-03 12:58:17 +03:00
. init_display = hd44780_common_init_display ,
2020-11-03 12:58:18 +03:00
. 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 ,
2020-11-03 12:58:20 +03:00
. redefine_char = hd44780_common_redefine_char ,
2017-03-10 17:15:21 +03:00
} ;
/* Send a command to the LCD panel in 4 bit GPIO mode */
2020-11-03 12:58:09 +03:00
static void hd44780_write_cmd_gpio4 ( struct hd44780_common * hdc , int cmd )
2017-03-10 17:15:21 +03:00
{
2020-11-03 12:58:06 +03:00
struct hd44780 * hd = hdc - > hd44780 ;
2017-03-10 17:15:21 +03:00
hd44780_write_gpio4 ( hd , cmd , 0 ) ;
/* The shortest command takes at least 120 us */
udelay ( 120 ) ;
}
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
2020-11-03 12:58:09 +03:00
static void hd44780_write_cmd_raw_gpio4 ( struct hd44780_common * hdc , int cmd )
2017-03-10 17:15:21 +03:00
{
2018-09-06 00:50:05 +03:00
DECLARE_BITMAP ( values , 6 ) ; /* for DATA[4-7], RS, RW */
2020-11-03 12:58:06 +03:00
struct hd44780 * hd = hdc - > hd44780 ;
2018-09-06 00:50:05 +03:00
unsigned int n ;
2017-03-10 17:15:21 +03:00
/* Command nibble + RS, RW */
2018-09-06 00:50:05 +03:00
values [ 0 ] = cmd & 0x0f ;
n = hd - > pins [ PIN_CTRL_RW ] ? 6 : 5 ;
2017-03-10 17:15:21 +03:00
/* Present the data to the port */
2018-09-06 00:50:07 +03:00
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] , NULL , values ) ;
2017-03-10 17:15:21 +03:00
hd44780_strobe_gpio ( hd ) ;
}
/* Send data to the LCD panel in 4 bit GPIO mode */
2020-11-03 12:58:08 +03:00
static void hd44780_write_data_gpio4 ( struct hd44780_common * hdc , int data )
2017-03-10 17:15:21 +03:00
{
2020-11-03 12:58:06 +03:00
struct hd44780 * hd = hdc - > hd44780 ;
2017-03-10 17:15:21 +03:00
hd44780_write_gpio4 ( hd , data , 1 ) ;
/* The shortest data takes at least 45 us */
udelay ( 45 ) ;
}
static const struct charlcd_ops hd44780_ops_gpio4 = {
. backlight = hd44780_backlight ,
2020-11-03 12:58:11 +03:00
. print = hd44780_common_print ,
2020-11-03 12:58:12 +03:00
. gotoxy = hd44780_common_gotoxy ,
2020-11-03 12:58:13 +03:00
. home = hd44780_common_home ,
2020-11-03 12:58:14 +03:00
. clear_display = hd44780_common_clear_display ,
2020-11-03 12:58:17 +03:00
. init_display = hd44780_common_init_display ,
2020-11-03 12:58:18 +03:00
. 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 ,
2020-11-03 12:58:20 +03:00
. redefine_char = hd44780_common_redefine_char ,
2017-03-10 17:15:21 +03:00
} ;
static int hd44780_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
unsigned int i , base ;
struct charlcd * lcd ;
2020-11-03 12:58:05 +03:00
struct hd44780_common * hdc ;
2017-03-10 17:15:21 +03:00
struct hd44780 * hd ;
2020-11-03 12:58:05 +03:00
int ifwidth , ret = - ENOMEM ;
2017-03-10 17:15:21 +03:00
/* Required pins */
ifwidth = gpiod_count ( dev , " data " ) ;
if ( ifwidth < 0 )
return ifwidth ;
switch ( ifwidth ) {
case 4 :
base = PIN_DATA4 ;
break ;
case 8 :
base = PIN_DATA0 ;
break ;
default :
return - EINVAL ;
}
2020-11-03 12:58:05 +03:00
hdc = hd44780_common_alloc ( ) ;
if ( ! hdc )
return - ENOMEM ;
2020-11-03 12:58:06 +03:00
lcd = charlcd_alloc ( ) ;
2017-03-10 17:15:21 +03:00
if ( ! lcd )
2020-11-03 12:58:05 +03:00
goto fail1 ;
2017-03-10 17:15:21 +03:00
2020-11-03 12:58:05 +03:00
hd = kzalloc ( sizeof ( struct hd44780 ) , GFP_KERNEL ) ;
if ( ! hd )
goto fail2 ;
2017-03-10 17:15:21 +03:00
2020-11-03 12:58:05 +03:00
hdc - > hd44780 = hd ;
lcd - > drvdata = hdc ;
2017-03-10 17:15:21 +03:00
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 ] ) ;
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
}
}
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 ] ) ;
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
}
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 ] ) ;
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
}
/* Optional pins */
hd - > pins [ PIN_CTRL_RW ] = devm_gpiod_get_optional ( dev , " rw " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( hd - > pins [ PIN_CTRL_RW ] ) ) {
ret = PTR_ERR ( hd - > pins [ PIN_CTRL_RW ] ) ;
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
}
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 ] ) ;
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
}
/* Required properties */
2017-03-23 16:25:37 +03:00
ret = device_property_read_u32 ( dev , " display-height-chars " ,
& lcd - > height ) ;
2017-03-10 17:15:21 +03:00
if ( ret )
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-23 16:25:37 +03:00
ret = device_property_read_u32 ( dev , " display-width-chars " , & lcd - > width ) ;
2017-03-10 17:15:21 +03:00
if ( ret )
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
/*
* On displays with more than two rows , the internal buffer width is
* usually equal to the display width
*/
if ( lcd - > height > 2 )
2020-11-03 12:58:06 +03:00
hdc - > bwidth = lcd - > width ;
2017-03-10 17:15:21 +03:00
/* Optional properties */
2020-11-03 12:58:06 +03:00
device_property_read_u32 ( dev , " internal-buffer-width " , & hdc - > bwidth ) ;
2017-03-10 17:15:21 +03:00
2020-11-03 12:58:07 +03:00
hdc - > ifwidth = ifwidth ;
2020-11-03 12:58:08 +03:00
if ( ifwidth = = 8 ) {
lcd - > ops = & hd44780_ops_gpio8 ;
hdc - > write_data = hd44780_write_data_gpio8 ;
2020-11-03 12:58:09 +03:00
hdc - > write_cmd = hd44780_write_cmd_gpio8 ;
2020-11-03 12:58:08 +03:00
} else {
lcd - > ops = & hd44780_ops_gpio4 ;
hdc - > write_data = hd44780_write_data_gpio4 ;
2020-11-03 12:58:09 +03:00
hdc - > write_cmd = hd44780_write_cmd_gpio4 ;
hdc - > write_cmd_raw4 = hd44780_write_cmd_raw_gpio4 ;
2020-11-03 12:58:08 +03:00
}
2017-03-10 17:15:21 +03:00
ret = charlcd_register ( lcd ) ;
if ( ret )
2020-11-03 12:58:05 +03:00
goto fail3 ;
2017-03-10 17:15:21 +03:00
platform_set_drvdata ( pdev , lcd ) ;
return 0 ;
2020-11-03 12:58:05 +03:00
fail3 :
kfree ( hd ) ;
fail2 :
kfree ( lcd ) ;
fail1 :
kfree ( hdc ) ;
2017-03-10 17:15:21 +03:00
return ret ;
}
static int hd44780_remove ( struct platform_device * pdev )
{
struct charlcd * lcd = platform_get_drvdata ( pdev ) ;
2020-11-03 12:58:05 +03:00
kfree ( lcd - > drvdata ) ;
2017-03-10 17:15:21 +03:00
charlcd_unregister ( lcd ) ;
2019-03-12 17:44:28 +03:00
2020-11-03 12:58:05 +03:00
kfree ( lcd ) ;
2017-03-10 17:15:21 +03:00
return 0 ;
}
static const struct of_device_id hd44780_of_match [ ] = {
{ . compatible = " hit,hd44780 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , hd44780_of_match ) ;
static struct platform_driver hd44780_driver = {
. probe = hd44780_probe ,
. remove = hd44780_remove ,
. driver = {
. name = " hd44780 " ,
. of_match_table = hd44780_of_match ,
} ,
} ;
module_platform_driver ( hd44780_driver ) ;
MODULE_DESCRIPTION ( " HD44780 Character LCD driver " ) ;
MODULE_AUTHOR ( " Geert Uytterhoeven <geert@linux-m68k.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;