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
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/slab.h>
# include <misc/charlcd.h>
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 ] ;
} ;
static void hd44780_backlight ( struct charlcd * lcd , int on )
{
struct hd44780 * hd = lcd - > drvdata ;
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 )
{
int values [ 10 ] ; /* for DATA[0-7], RS, RW */
unsigned int i , n ;
for ( i = 0 ; i < 8 ; i + + )
values [ PIN_DATA0 + i ] = ! ! ( val & BIT ( i ) ) ;
values [ PIN_CTRL_RS ] = rs ;
n = 9 ;
if ( hd - > pins [ PIN_CTRL_RW ] ) {
values [ PIN_CTRL_RW ] = 0 ;
n + + ;
}
/* Present the data to the port */
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA0 ] , values ) ;
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 )
{
int values [ 10 ] ; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
unsigned int i , n ;
/* High nibble + RS, RW */
for ( i = 4 ; i < 8 ; i + + )
values [ PIN_DATA0 + i ] = ! ! ( val & BIT ( i ) ) ;
values [ PIN_CTRL_RS ] = rs ;
n = 5 ;
if ( hd - > pins [ PIN_CTRL_RW ] ) {
values [ PIN_CTRL_RW ] = 0 ;
n + + ;
}
/* Present the data to the port */
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] ,
& values [ PIN_DATA4 ] ) ;
hd44780_strobe_gpio ( hd ) ;
/* Low nibble */
for ( i = 0 ; i < 4 ; i + + )
values [ PIN_DATA4 + i ] = ! ! ( val & BIT ( i ) ) ;
/* Present the data to the port */
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] ,
& values [ PIN_DATA4 ] ) ;
hd44780_strobe_gpio ( hd ) ;
}
/* Send a command to the LCD panel in 8 bit GPIO mode */
static void hd44780_write_cmd_gpio8 ( struct charlcd * lcd , int cmd )
{
struct hd44780 * hd = lcd - > drvdata ;
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 */
static void hd44780_write_data_gpio8 ( struct charlcd * lcd , int data )
{
struct hd44780 * hd = lcd - > drvdata ;
hd44780_write_gpio8 ( hd , data , 1 ) ;
/* The shortest data takes at least 45 us */
udelay ( 45 ) ;
}
static const struct charlcd_ops hd44780_ops_gpio8 = {
. write_cmd = hd44780_write_cmd_gpio8 ,
. write_data = hd44780_write_data_gpio8 ,
. backlight = hd44780_backlight ,
} ;
/* Send a command to the LCD panel in 4 bit GPIO mode */
static void hd44780_write_cmd_gpio4 ( struct charlcd * lcd , int cmd )
{
struct hd44780 * hd = lcd - > drvdata ;
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 */
static void hd44780_write_cmd_raw_gpio4 ( struct charlcd * lcd , int cmd )
{
int values [ 10 ] ; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
struct hd44780 * hd = lcd - > drvdata ;
unsigned int i , n ;
/* Command nibble + RS, RW */
for ( i = 0 ; i < 4 ; i + + )
values [ PIN_DATA4 + i ] = ! ! ( cmd & BIT ( i ) ) ;
values [ PIN_CTRL_RS ] = 0 ;
n = 5 ;
if ( hd - > pins [ PIN_CTRL_RW ] ) {
values [ PIN_CTRL_RW ] = 0 ;
n + + ;
}
/* Present the data to the port */
gpiod_set_array_value_cansleep ( n , & hd - > pins [ PIN_DATA4 ] ,
& values [ PIN_DATA4 ] ) ;
hd44780_strobe_gpio ( hd ) ;
}
/* Send data to the LCD panel in 4 bit GPIO mode */
static void hd44780_write_data_gpio4 ( struct charlcd * lcd , int data )
{
struct hd44780 * hd = lcd - > drvdata ;
hd44780_write_gpio4 ( hd , data , 1 ) ;
/* The shortest data takes at least 45 us */
udelay ( 45 ) ;
}
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 ,
} ;
static int hd44780_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
unsigned int i , base ;
struct charlcd * lcd ;
struct hd44780 * hd ;
int ifwidth , ret ;
/* 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 ;
}
lcd = charlcd_alloc ( sizeof ( struct hd44780 ) ) ;
if ( ! lcd )
return - ENOMEM ;
hd = lcd - > drvdata ;
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 ;
}
}
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 ;
}
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 ;
}
/* 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 ] ) ;
goto fail ;
}
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 ;
}
/* 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 )
goto fail ;
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 )
goto fail ;
/*
* 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 ;
/* Optional properties */
device_property_read_u32 ( dev , " internal-buffer-width " , & lcd - > bwidth ) ;
lcd - > ifwidth = ifwidth ;
lcd - > ops = ifwidth = = 8 ? & hd44780_ops_gpio8 : & hd44780_ops_gpio4 ;
ret = charlcd_register ( lcd ) ;
if ( ret )
goto fail ;
platform_set_drvdata ( pdev , lcd ) ;
return 0 ;
fail :
kfree ( lcd ) ;
return ret ;
}
static int hd44780_remove ( struct platform_device * pdev )
{
struct charlcd * lcd = platform_get_drvdata ( pdev ) ;
charlcd_unregister ( lcd ) ;
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 " ) ;