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_FUNCTION_SET 0x20 /* Set function */ 21 #define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */ 22 #define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */ 23 #define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */ 24 25 #define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */ 26 27 /* sleeps that many milliseconds with a reschedule */ 28 static void long_sleep(int ms) 29 { 30 schedule_timeout_interruptible(msecs_to_jiffies(ms)); 31 } 32 33 int hd44780_common_print(struct charlcd *lcd, int c) 34 { 35 struct hd44780_common *hdc = lcd->drvdata; 36 37 if (lcd->addr.x < hdc->bwidth) { 38 hdc->write_data(hdc, c); 39 return 0; 40 } 41 42 return 1; 43 } 44 EXPORT_SYMBOL_GPL(hd44780_common_print); 45 46 int hd44780_common_gotoxy(struct charlcd *lcd) 47 { 48 struct hd44780_common *hdc = lcd->drvdata; 49 unsigned int addr; 50 51 /* 52 * we force the cursor to stay at the end of the 53 * line if it wants to go farther 54 */ 55 addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1) 56 : hdc->bwidth - 1; 57 if (lcd->addr.y & 1) 58 addr += hdc->hwidth; 59 if (lcd->addr.y & 2) 60 addr += hdc->bwidth; 61 hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr); 62 return 0; 63 } 64 EXPORT_SYMBOL_GPL(hd44780_common_gotoxy); 65 66 int hd44780_common_home(struct charlcd *lcd) 67 { 68 lcd->addr.x = 0; 69 lcd->addr.y = 0; 70 return hd44780_common_gotoxy(lcd); 71 } 72 EXPORT_SYMBOL_GPL(hd44780_common_home); 73 74 /* clears the display and resets X/Y */ 75 int hd44780_common_clear_display(struct charlcd *lcd) 76 { 77 struct hd44780_common *hdc = lcd->drvdata; 78 79 hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR); 80 /* we must wait a few milliseconds (15) */ 81 long_sleep(15); 82 return 0; 83 } 84 EXPORT_SYMBOL_GPL(hd44780_common_clear_display); 85 86 int hd44780_common_init_display(struct charlcd *lcd) 87 { 88 struct hd44780_common *hdc = lcd->drvdata; 89 90 void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd); 91 u8 init; 92 93 if (hdc->ifwidth != 4 && hdc->ifwidth != 8) 94 return -EINVAL; 95 96 hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | 97 LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B; 98 99 long_sleep(20); /* wait 20 ms after power-up for the paranoid */ 100 101 /* 102 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure 103 * the LCD is in 8-bit mode afterwards 104 */ 105 init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS; 106 if (hdc->ifwidth == 4) { 107 init >>= 4; 108 write_cmd_raw = hdc->write_cmd_raw4; 109 } else { 110 write_cmd_raw = hdc->write_cmd; 111 } 112 write_cmd_raw(hdc, init); 113 long_sleep(10); 114 write_cmd_raw(hdc, init); 115 long_sleep(10); 116 write_cmd_raw(hdc, init); 117 long_sleep(10); 118 119 if (hdc->ifwidth == 4) { 120 /* Switch to 4-bit mode, 1 line, small fonts */ 121 hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4); 122 long_sleep(10); 123 } 124 125 /* set font height and lines number */ 126 hdc->write_cmd(hdc, 127 LCD_CMD_FUNCTION_SET | 128 ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | 129 ((hdc->hd44780_common_flags & LCD_FLAG_F) ? 130 LCD_CMD_FONT_5X10_DOTS : 0) | 131 ((hdc->hd44780_common_flags & LCD_FLAG_N) ? 132 LCD_CMD_TWO_LINES : 0)); 133 long_sleep(10); 134 135 /* display off, cursor off, blink off */ 136 hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL); 137 long_sleep(10); 138 139 hdc->write_cmd(hdc, 140 LCD_CMD_DISPLAY_CTRL | /* set display mode */ 141 ((hdc->hd44780_common_flags & LCD_FLAG_D) ? 142 LCD_CMD_DISPLAY_ON : 0) | 143 ((hdc->hd44780_common_flags & LCD_FLAG_C) ? 144 LCD_CMD_CURSOR_ON : 0) | 145 ((hdc->hd44780_common_flags & LCD_FLAG_B) ? 146 LCD_CMD_BLINK_ON : 0)); 147 148 charlcd_backlight(lcd, 149 (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0); 150 151 long_sleep(10); 152 153 /* entry mode set : increment, cursor shifting */ 154 hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); 155 156 hd44780_common_clear_display(lcd); 157 return 0; 158 } 159 EXPORT_SYMBOL_GPL(hd44780_common_init_display); 160 161 struct hd44780_common *hd44780_common_alloc(void) 162 { 163 struct hd44780_common *hd; 164 165 hd = kzalloc(sizeof(*hd), GFP_KERNEL); 166 if (!hd) 167 return NULL; 168 169 hd->ifwidth = 8; 170 hd->bwidth = DEFAULT_LCD_BWIDTH; 171 hd->hwidth = DEFAULT_LCD_HWIDTH; 172 return hd; 173 } 174 EXPORT_SYMBOL_GPL(hd44780_common_alloc); 175 176 MODULE_LICENSE("GPL"); 177