353 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * HD44780 Character LCD driver for Linux4 *5 * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>6 * Copyright (C) 2016-2017 Glider bvba7 */8 9#include <linux/delay.h>10#include <linux/gpio/consumer.h>11#include <linux/module.h>12#include <linux/mod_devicetable.h>13#include <linux/platform_device.h>14#include <linux/property.h>15#include <linux/slab.h>16 17#include "charlcd.h"18#include "hd44780_common.h"19 20enum hd44780_pin {21 /* Order does matter due to writing to GPIO array subsets! */22 PIN_DATA0, /* Optional */23 PIN_DATA1, /* Optional */24 PIN_DATA2, /* Optional */25 PIN_DATA3, /* Optional */26 PIN_DATA4,27 PIN_DATA5,28 PIN_DATA6,29 PIN_DATA7,30 PIN_CTRL_RS,31 PIN_CTRL_RW, /* Optional */32 PIN_CTRL_E,33 PIN_CTRL_BL, /* Optional */34 PIN_NUM35};36 37struct hd44780 {38 struct gpio_desc *pins[PIN_NUM];39};40 41static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on)42{43 struct hd44780_common *hdc = lcd->drvdata;44 struct hd44780 *hd = hdc->hd44780;45 46 if (hd->pins[PIN_CTRL_BL])47 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);48}49 50static void hd44780_strobe_gpio(struct hd44780 *hd)51{52 /* Maintain the data during 20 us before the strobe */53 udelay(20);54 55 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 1);56 57 /* Maintain the strobe during 40 us */58 udelay(40);59 60 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 0);61}62 63/* write to an LCD panel register in 8 bit GPIO mode */64static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)65{66 DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */67 unsigned int n;68 69 values[0] = val;70 __assign_bit(8, values, rs);71 n = hd->pins[PIN_CTRL_RW] ? 10 : 9;72 73 /* Present the data to the port */74 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values);75 76 hd44780_strobe_gpio(hd);77}78 79/* write to an LCD panel register in 4 bit GPIO mode */80static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)81{82 DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */83 unsigned int n;84 85 /* High nibble + RS, RW */86 values[0] = val >> 4;87 __assign_bit(4, values, rs);88 n = hd->pins[PIN_CTRL_RW] ? 6 : 5;89 90 /* Present the data to the port */91 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);92 93 hd44780_strobe_gpio(hd);94 95 /* Low nibble */96 values[0] &= ~0x0fUL;97 values[0] |= val & 0x0f;98 99 /* Present the data to the port */100 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);101 102 hd44780_strobe_gpio(hd);103}104 105/* Send a command to the LCD panel in 8 bit GPIO mode */106static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd)107{108 struct hd44780 *hd = hdc->hd44780;109 110 hd44780_write_gpio8(hd, cmd, 0);111 112 /* The shortest command takes at least 120 us */113 udelay(120);114}115 116/* Send data to the LCD panel in 8 bit GPIO mode */117static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data)118{119 struct hd44780 *hd = hdc->hd44780;120 121 hd44780_write_gpio8(hd, data, 1);122 123 /* The shortest data takes at least 45 us */124 udelay(45);125}126 127static const struct charlcd_ops hd44780_ops_gpio8 = {128 .backlight = hd44780_backlight,129 .print = hd44780_common_print,130 .gotoxy = hd44780_common_gotoxy,131 .home = hd44780_common_home,132 .clear_display = hd44780_common_clear_display,133 .init_display = hd44780_common_init_display,134 .shift_cursor = hd44780_common_shift_cursor,135 .shift_display = hd44780_common_shift_display,136 .display = hd44780_common_display,137 .cursor = hd44780_common_cursor,138 .blink = hd44780_common_blink,139 .fontsize = hd44780_common_fontsize,140 .lines = hd44780_common_lines,141 .redefine_char = hd44780_common_redefine_char,142};143 144/* Send a command to the LCD panel in 4 bit GPIO mode */145static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd)146{147 struct hd44780 *hd = hdc->hd44780;148 149 hd44780_write_gpio4(hd, cmd, 0);150 151 /* The shortest command takes at least 120 us */152 udelay(120);153}154 155/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */156static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd)157{158 DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */159 struct hd44780 *hd = hdc->hd44780;160 unsigned int n;161 162 /* Command nibble + RS, RW */163 values[0] = cmd & 0x0f;164 n = hd->pins[PIN_CTRL_RW] ? 6 : 5;165 166 /* Present the data to the port */167 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);168 169 hd44780_strobe_gpio(hd);170}171 172/* Send data to the LCD panel in 4 bit GPIO mode */173static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data)174{175 struct hd44780 *hd = hdc->hd44780;176 177 hd44780_write_gpio4(hd, data, 1);178 179 /* The shortest data takes at least 45 us */180 udelay(45);181}182 183static const struct charlcd_ops hd44780_ops_gpio4 = {184 .backlight = hd44780_backlight,185 .print = hd44780_common_print,186 .gotoxy = hd44780_common_gotoxy,187 .home = hd44780_common_home,188 .clear_display = hd44780_common_clear_display,189 .init_display = hd44780_common_init_display,190 .shift_cursor = hd44780_common_shift_cursor,191 .shift_display = hd44780_common_shift_display,192 .display = hd44780_common_display,193 .cursor = hd44780_common_cursor,194 .blink = hd44780_common_blink,195 .fontsize = hd44780_common_fontsize,196 .lines = hd44780_common_lines,197 .redefine_char = hd44780_common_redefine_char,198};199 200static int hd44780_probe(struct platform_device *pdev)201{202 struct device *dev = &pdev->dev;203 unsigned int i, base;204 struct charlcd *lcd;205 struct hd44780_common *hdc;206 struct hd44780 *hd;207 int ifwidth, ret = -ENOMEM;208 209 /* Required pins */210 ifwidth = gpiod_count(dev, "data");211 if (ifwidth < 0)212 return ifwidth;213 214 switch (ifwidth) {215 case 4:216 base = PIN_DATA4;217 break;218 case 8:219 base = PIN_DATA0;220 break;221 default:222 return -EINVAL;223 }224 225 hdc = hd44780_common_alloc();226 if (!hdc)227 return -ENOMEM;228 229 lcd = charlcd_alloc();230 if (!lcd)231 goto fail1;232 233 hd = kzalloc(sizeof(*hd), GFP_KERNEL);234 if (!hd)235 goto fail2;236 237 hdc->hd44780 = hd;238 lcd->drvdata = hdc;239 for (i = 0; i < ifwidth; i++) {240 hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,241 GPIOD_OUT_LOW);242 if (IS_ERR(hd->pins[base + i])) {243 ret = PTR_ERR(hd->pins[base + i]);244 goto fail3;245 }246 }247 248 hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);249 if (IS_ERR(hd->pins[PIN_CTRL_E])) {250 ret = PTR_ERR(hd->pins[PIN_CTRL_E]);251 goto fail3;252 }253 254 hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);255 if (IS_ERR(hd->pins[PIN_CTRL_RS])) {256 ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);257 goto fail3;258 }259 260 /* Optional pins */261 hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, "rw",262 GPIOD_OUT_LOW);263 if (IS_ERR(hd->pins[PIN_CTRL_RW])) {264 ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);265 goto fail3;266 }267 268 hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",269 GPIOD_OUT_LOW);270 if (IS_ERR(hd->pins[PIN_CTRL_BL])) {271 ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);272 goto fail3;273 }274 275 /* Required properties */276 ret = device_property_read_u32(dev, "display-height-chars",277 &lcd->height);278 if (ret)279 goto fail3;280 ret = device_property_read_u32(dev, "display-width-chars", &lcd->width);281 if (ret)282 goto fail3;283 284 /*285 * On displays with more than two rows, the internal buffer width is286 * usually equal to the display width287 */288 if (lcd->height > 2)289 hdc->bwidth = lcd->width;290 291 /* Optional properties */292 device_property_read_u32(dev, "internal-buffer-width", &hdc->bwidth);293 294 hdc->ifwidth = ifwidth;295 if (ifwidth == 8) {296 lcd->ops = &hd44780_ops_gpio8;297 hdc->write_data = hd44780_write_data_gpio8;298 hdc->write_cmd = hd44780_write_cmd_gpio8;299 } else {300 lcd->ops = &hd44780_ops_gpio4;301 hdc->write_data = hd44780_write_data_gpio4;302 hdc->write_cmd = hd44780_write_cmd_gpio4;303 hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4;304 }305 306 ret = charlcd_register(lcd);307 if (ret)308 goto fail3;309 310 platform_set_drvdata(pdev, lcd);311 return 0;312 313fail3:314 kfree(hd);315fail2:316 kfree(lcd);317fail1:318 kfree(hdc);319 return ret;320}321 322static void hd44780_remove(struct platform_device *pdev)323{324 struct charlcd *lcd = platform_get_drvdata(pdev);325 struct hd44780_common *hdc = lcd->drvdata;326 327 charlcd_unregister(lcd);328 kfree(hdc->hd44780);329 kfree(lcd->drvdata);330 331 kfree(lcd);332}333 334static const struct of_device_id hd44780_of_match[] = {335 { .compatible = "hit,hd44780" },336 { /* sentinel */ }337};338MODULE_DEVICE_TABLE(of, hd44780_of_match);339 340static struct platform_driver hd44780_driver = {341 .probe = hd44780_probe,342 .remove_new = hd44780_remove,343 .driver = {344 .name = "hd44780",345 .of_match_table = hd44780_of_match,346 },347};348 349module_platform_driver(hd44780_driver);350MODULE_DESCRIPTION("HD44780 Character LCD driver");351MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");352MODULE_LICENSE("GPL");353