1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <linux/module.h> 3 #include <linux/sched.h> 4 #include <linux/slab.h> 5 6 #include "charlcd.h" 7 #include "hd44780_common.h" 8 9 /* LCD commands */ 10 #define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */ 11 12 #define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */ 13 #define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */ 14 15 #define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */ 16 #define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */ 17 #define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */ 18 #define LCD_CMD_BLINK_ON 0x01 /* Set blink on */ 19 20 #define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */ 21 #define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */ 22 #define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */ 23 24 #define LCD_CMD_FUNCTION_SET 0x20 /* Set function */ 25 #define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */ 26 #define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */ 27 #define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */ 28 29 #define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */ 30 31 #define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */ 32 33 /* sleeps that many milliseconds with a reschedule */ 34 static void long_sleep(int ms) 35 { 36 schedule_timeout_interruptible(msecs_to_jiffies(ms)); 37 } 38 39 int hd44780_common_print(struct charlcd *lcd, int c) 40 { 41 struct hd44780_common *hdc = lcd->drvdata; 42 43 if (lcd->addr.x < hdc->bwidth) { 44 hdc->write_data(hdc, c); 45 return 0; 46 } 47 48 return 1; 49 } 50 EXPORT_SYMBOL_GPL(hd44780_common_print); 51 52 int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) 53 { 54 struct hd44780_common *hdc = lcd->drvdata; 55 unsigned int addr; 56 57 /* 58 * we force the cursor to stay at the end of the 59 * line if it wants to go farther 60 */ 61 addr = x < hdc->bwidth ? x & (hdc->hwidth - 1) : hdc->bwidth - 1; 62 if (y & 1) 63 addr += hdc->hwidth; 64 if (y & 2) 65 addr += hdc->bwidth; 66 hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr); 67 return 0; 68 } 69 EXPORT_SYMBOL_GPL(hd44780_common_gotoxy); 70 71 int hd44780_common_home(struct charlcd *lcd) 72 { 73 return hd44780_common_gotoxy(lcd, 0, 0); 74 } 75 EXPORT_SYMBOL_GPL(hd44780_common_home); 76 77 /* clears the display and resets X/Y */ 78 int hd44780_common_clear_display(struct charlcd *lcd) 79 { 80 struct hd44780_common *hdc = lcd->drvdata; 81 82 hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR); 83 /* datasheet says to wait 1,64 milliseconds */ 84 long_sleep(2); 85 86 /* 87 * The Hitachi HD44780 controller (and compatible ones) reset the DDRAM 88 * address when executing the DISPLAY_CLEAR command, thus the 89 * following call is not required. However, other controllers do not 90 * (e.g. NewHaven NHD-0220DZW-AG5), thus move the cursor to home 91 * unconditionally to support both. 92 */ 93 return hd44780_common_home(lcd); 94 } 95 EXPORT_SYMBOL_GPL(hd44780_common_clear_display); 96 97 int hd44780_common_init_display(struct charlcd *lcd) 98 { 99 struct hd44780_common *hdc = lcd->drvdata; 100 101 void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd); 102 u8 init; 103 104 if (hdc->ifwidth != 4 && hdc->ifwidth != 8) 105 return -EINVAL; 106 107 hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | 108 LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B; 109 110 long_sleep(20); /* wait 20 ms after power-up for the paranoid */ 111 112 /* 113 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure 114 * the LCD is in 8-bit mode afterwards 115 */ 116 init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS; 117 if (hdc->ifwidth == 4) { 118 init >>= 4; 119 write_cmd_raw = hdc->write_cmd_raw4; 120 } else { 121 write_cmd_raw = hdc->write_cmd; 122 } 123 write_cmd_raw(hdc, init); 124 long_sleep(10); 125 write_cmd_raw(hdc, init); 126 long_sleep(10); 127 write_cmd_raw(hdc, init); 128 long_sleep(10); 129 130 if (hdc->ifwidth == 4) { 131 /* Switch to 4-bit mode, 1 line, small fonts */ 132 hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4); 133 long_sleep(10); 134 } 135 136 /* set font height and lines number */ 137 hdc->write_cmd(hdc, 138 LCD_CMD_FUNCTION_SET | 139 ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | 140 ((hdc->hd44780_common_flags & LCD_FLAG_F) ? 141 LCD_CMD_FONT_5X10_DOTS : 0) | 142 ((hdc->hd44780_common_flags & LCD_FLAG_N) ? 143 LCD_CMD_TWO_LINES : 0)); 144 long_sleep(10); 145 146 /* display off, cursor off, blink off */ 147 hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL); 148 long_sleep(10); 149 150 hdc->write_cmd(hdc, 151 LCD_CMD_DISPLAY_CTRL | /* set display mode */ 152 ((hdc->hd44780_common_flags & LCD_FLAG_D) ? 153 LCD_CMD_DISPLAY_ON : 0) | 154 ((hdc->hd44780_common_flags & LCD_FLAG_C) ? 155 LCD_CMD_CURSOR_ON : 0) | 156 ((hdc->hd44780_common_flags & LCD_FLAG_B) ? 157 LCD_CMD_BLINK_ON : 0)); 158 159 charlcd_backlight(lcd, 160 (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0); 161 162 long_sleep(10); 163 164 /* entry mode set : increment, cursor shifting */ 165 hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); 166 167 hd44780_common_clear_display(lcd); 168 return 0; 169 } 170 EXPORT_SYMBOL_GPL(hd44780_common_init_display); 171 172 int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir) 173 { 174 struct hd44780_common *hdc = lcd->drvdata; 175 176 if (dir == CHARLCD_SHIFT_LEFT) { 177 /* back one char if not at end of line */ 178 if (lcd->addr.x < hdc->bwidth) 179 hdc->write_cmd(hdc, LCD_CMD_SHIFT); 180 } else if (dir == CHARLCD_SHIFT_RIGHT) { 181 /* allow the cursor to pass the end of the line */ 182 if (lcd->addr.x < (hdc->bwidth - 1)) 183 hdc->write_cmd(hdc, 184 LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT); 185 } 186 187 return 0; 188 } 189 EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor); 190 191 int hd44780_common_shift_display(struct charlcd *lcd, 192 enum charlcd_shift_dir dir) 193 { 194 struct hd44780_common *hdc = lcd->drvdata; 195 196 if (dir == CHARLCD_SHIFT_LEFT) 197 hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT); 198 else if (dir == CHARLCD_SHIFT_RIGHT) 199 hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT | 200 LCD_CMD_SHIFT_RIGHT); 201 202 return 0; 203 } 204 EXPORT_SYMBOL_GPL(hd44780_common_shift_display); 205 206 static void hd44780_common_set_mode(struct hd44780_common *hdc) 207 { 208 hdc->write_cmd(hdc, 209 LCD_CMD_DISPLAY_CTRL | 210 ((hdc->hd44780_common_flags & LCD_FLAG_D) ? 211 LCD_CMD_DISPLAY_ON : 0) | 212 ((hdc->hd44780_common_flags & LCD_FLAG_C) ? 213 LCD_CMD_CURSOR_ON : 0) | 214 ((hdc->hd44780_common_flags & LCD_FLAG_B) ? 215 LCD_CMD_BLINK_ON : 0)); 216 } 217 218 int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on) 219 { 220 struct hd44780_common *hdc = lcd->drvdata; 221 222 if (on == CHARLCD_ON) 223 hdc->hd44780_common_flags |= LCD_FLAG_D; 224 else 225 hdc->hd44780_common_flags &= ~LCD_FLAG_D; 226 227 hd44780_common_set_mode(hdc); 228 return 0; 229 } 230 EXPORT_SYMBOL_GPL(hd44780_common_display); 231 232 int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on) 233 { 234 struct hd44780_common *hdc = lcd->drvdata; 235 236 if (on == CHARLCD_ON) 237 hdc->hd44780_common_flags |= LCD_FLAG_C; 238 else 239 hdc->hd44780_common_flags &= ~LCD_FLAG_C; 240 241 hd44780_common_set_mode(hdc); 242 return 0; 243 } 244 EXPORT_SYMBOL_GPL(hd44780_common_cursor); 245 246 int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on) 247 { 248 struct hd44780_common *hdc = lcd->drvdata; 249 250 if (on == CHARLCD_ON) 251 hdc->hd44780_common_flags |= LCD_FLAG_B; 252 else 253 hdc->hd44780_common_flags &= ~LCD_FLAG_B; 254 255 hd44780_common_set_mode(hdc); 256 return 0; 257 } 258 EXPORT_SYMBOL_GPL(hd44780_common_blink); 259 260 static void hd44780_common_set_function(struct hd44780_common *hdc) 261 { 262 hdc->write_cmd(hdc, 263 LCD_CMD_FUNCTION_SET | 264 ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | 265 ((hdc->hd44780_common_flags & LCD_FLAG_F) ? 266 LCD_CMD_FONT_5X10_DOTS : 0) | 267 ((hdc->hd44780_common_flags & LCD_FLAG_N) ? 268 LCD_CMD_TWO_LINES : 0)); 269 } 270 271 int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size) 272 { 273 struct hd44780_common *hdc = lcd->drvdata; 274 275 if (size == CHARLCD_FONTSIZE_LARGE) 276 hdc->hd44780_common_flags |= LCD_FLAG_F; 277 else 278 hdc->hd44780_common_flags &= ~LCD_FLAG_F; 279 280 hd44780_common_set_function(hdc); 281 return 0; 282 } 283 EXPORT_SYMBOL_GPL(hd44780_common_fontsize); 284 285 int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines) 286 { 287 struct hd44780_common *hdc = lcd->drvdata; 288 289 if (lines == CHARLCD_LINES_2) 290 hdc->hd44780_common_flags |= LCD_FLAG_N; 291 else 292 hdc->hd44780_common_flags &= ~LCD_FLAG_N; 293 294 hd44780_common_set_function(hdc); 295 return 0; 296 } 297 EXPORT_SYMBOL_GPL(hd44780_common_lines); 298 299 int hd44780_common_redefine_char(struct charlcd *lcd, char *esc) 300 { 301 /* Generator : LGcxxxxx...xx; must have <c> between '0' 302 * and '7', representing the numerical ASCII code of the 303 * redefined character, and <xx...xx> a sequence of 16 304 * hex digits representing 8 bytes for each character. 305 * Most LCDs will only use 5 lower bits of the 7 first 306 * bytes. 307 */ 308 309 struct hd44780_common *hdc = lcd->drvdata; 310 unsigned char cgbytes[8]; 311 unsigned char cgaddr; 312 int cgoffset; 313 int shift; 314 char value; 315 int addr; 316 317 if (!strchr(esc, ';')) 318 return 0; 319 320 esc++; 321 322 cgaddr = *(esc++) - '0'; 323 if (cgaddr > 7) 324 return 1; 325 326 cgoffset = 0; 327 shift = 0; 328 value = 0; 329 while (*esc && cgoffset < 8) { 330 int half; 331 332 shift ^= 4; 333 half = hex_to_bin(*esc++); 334 if (half < 0) 335 continue; 336 337 value |= half << shift; 338 if (shift == 0) { 339 cgbytes[cgoffset++] = value; 340 value = 0; 341 } 342 } 343 344 hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8)); 345 for (addr = 0; addr < cgoffset; addr++) 346 hdc->write_data(hdc, cgbytes[addr]); 347 348 /* ensures that we stop writing to CGRAM */ 349 lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y); 350 return 1; 351 } 352 EXPORT_SYMBOL_GPL(hd44780_common_redefine_char); 353 354 struct hd44780_common *hd44780_common_alloc(void) 355 { 356 struct hd44780_common *hd; 357 358 hd = kzalloc(sizeof(*hd), GFP_KERNEL); 359 if (!hd) 360 return NULL; 361 362 hd->ifwidth = 8; 363 hd->bwidth = DEFAULT_LCD_BWIDTH; 364 hd->hwidth = DEFAULT_LCD_HWIDTH; 365 return hd; 366 } 367 EXPORT_SYMBOL_GPL(hd44780_common_alloc); 368 369 MODULE_LICENSE("GPL"); 370