1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Console driver for LCD2S 4x20 character displays connected through i2c. 4 * The display also has a SPI interface, but the driver does not support 5 * this yet. 6 * 7 * This is a driver allowing you to use a LCD2S 4x20 from Modtronix 8 * engineering as auxdisplay character device. 9 * 10 * (C) 2019 by Lemonage Software GmbH 11 * Author: Lars Pöschel <poeschel@lemonage.de> 12 * All rights reserved. 13 */ 14 #include <linux/hex.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/property.h> 18 #include <linux/slab.h> 19 #include <linux/i2c.h> 20 #include <linux/delay.h> 21 22 #include "charlcd.h" 23 24 #define LCD2S_CMD_CUR_MOVES_FWD 0x09 25 #define LCD2S_CMD_CUR_BLINK_OFF 0x10 26 #define LCD2S_CMD_CUR_UL_OFF 0x11 27 #define LCD2S_CMD_DISPLAY_OFF 0x12 28 #define LCD2S_CMD_CUR_BLINK_ON 0x18 29 #define LCD2S_CMD_CUR_UL_ON 0x19 30 #define LCD2S_CMD_DISPLAY_ON 0x1a 31 #define LCD2S_CMD_BACKLIGHT_OFF 0x20 32 #define LCD2S_CMD_BACKLIGHT_ON 0x28 33 #define LCD2S_CMD_WRITE 0x80 34 #define LCD2S_CMD_MOV_CUR_RIGHT 0x83 35 #define LCD2S_CMD_MOV_CUR_LEFT 0x84 36 #define LCD2S_CMD_SHIFT_RIGHT 0x85 37 #define LCD2S_CMD_SHIFT_LEFT 0x86 38 #define LCD2S_CMD_SHIFT_UP 0x87 39 #define LCD2S_CMD_SHIFT_DOWN 0x88 40 #define LCD2S_CMD_CUR_ADDR 0x89 41 #define LCD2S_CMD_CUR_POS 0x8a 42 #define LCD2S_CMD_CUR_RESET 0x8b 43 #define LCD2S_CMD_CLEAR 0x8c 44 #define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92 45 #define LCD2S_CMD_READ_STATUS 0xd0 46 47 #define LCD2S_CHARACTER_SIZE 8 48 49 #define LCD2S_STATUS_BUF_MASK 0x7f 50 51 struct lcd2s_data { 52 struct i2c_client *i2c; 53 struct charlcd *charlcd; 54 }; 55 56 static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count) 57 { 58 s32 status; 59 60 status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS); 61 if (status < 0) 62 return status; 63 64 while ((status & LCD2S_STATUS_BUF_MASK) < count) { 65 mdelay(1); 66 status = i2c_smbus_read_byte_data(client, 67 LCD2S_CMD_READ_STATUS); 68 if (status < 0) 69 return status; 70 } 71 return 0; 72 } 73 74 static int lcd2s_i2c_master_send(const struct i2c_client *client, 75 const char *buf, int count) 76 { 77 s32 status; 78 79 status = lcd2s_wait_buf_free(client, count); 80 if (status < 0) 81 return status; 82 83 return i2c_master_send(client, buf, count); 84 } 85 86 static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value) 87 { 88 s32 status; 89 90 status = lcd2s_wait_buf_free(client, 1); 91 if (status < 0) 92 return status; 93 94 return i2c_smbus_write_byte(client, value); 95 } 96 97 static int lcd2s_print(struct charlcd *lcd, int c) 98 { 99 struct lcd2s_data *lcd2s = lcd->drvdata; 100 u8 buf[2] = { LCD2S_CMD_WRITE, c }; 101 int ret; 102 103 ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); 104 if (ret < 0) 105 return ret; 106 if (ret != sizeof(buf)) 107 return -EIO; 108 return 0; 109 } 110 111 static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y) 112 { 113 struct lcd2s_data *lcd2s = lcd->drvdata; 114 u8 buf[3] = { LCD2S_CMD_CUR_POS, y + 1, x + 1 }; 115 int ret; 116 117 ret = lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); 118 if (ret < 0) 119 return ret; 120 if (ret != sizeof(buf)) 121 return -EIO; 122 return 0; 123 } 124 125 static int lcd2s_home(struct charlcd *lcd) 126 { 127 struct lcd2s_data *lcd2s = lcd->drvdata; 128 129 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET); 130 return 0; 131 } 132 133 static int lcd2s_init_display(struct charlcd *lcd) 134 { 135 struct lcd2s_data *lcd2s = lcd->drvdata; 136 137 /* turn everything off, but display on */ 138 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON); 139 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF); 140 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD); 141 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF); 142 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF); 143 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR); 144 145 return 0; 146 } 147 148 static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir) 149 { 150 struct lcd2s_data *lcd2s = lcd->drvdata; 151 152 if (dir == CHARLCD_SHIFT_LEFT) 153 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT); 154 else 155 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT); 156 157 return 0; 158 } 159 160 static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir) 161 { 162 struct lcd2s_data *lcd2s = lcd->drvdata; 163 164 if (dir == CHARLCD_SHIFT_LEFT) 165 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT); 166 else 167 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT); 168 169 return 0; 170 } 171 172 static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on) 173 { 174 struct lcd2s_data *lcd2s = lcd->drvdata; 175 176 if (on) 177 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON); 178 else 179 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF); 180 } 181 182 static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on) 183 { 184 struct lcd2s_data *lcd2s = lcd->drvdata; 185 186 if (on) 187 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON); 188 else 189 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF); 190 191 return 0; 192 } 193 194 static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on) 195 { 196 struct lcd2s_data *lcd2s = lcd->drvdata; 197 198 if (on) 199 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON); 200 else 201 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF); 202 203 return 0; 204 } 205 206 static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on) 207 { 208 struct lcd2s_data *lcd2s = lcd->drvdata; 209 210 if (on) 211 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON); 212 else 213 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF); 214 215 return 0; 216 } 217 218 static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size) 219 { 220 return 0; 221 } 222 223 static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines) 224 { 225 return 0; 226 } 227 228 /* 229 * Generator: LGcxxxxx...xx; must have <c> between '0' and '7', 230 * representing the numerical ASCII code of the redefined character, 231 * and <xx...xx> a sequence of 16 hex digits representing 8 bytes 232 * for each character. Most LCDs will only use 5 lower bits of 233 * the 7 first bytes. 234 */ 235 static int lcd2s_redefine_char(struct charlcd *lcd, char *esc) 236 { 237 struct lcd2s_data *lcd2s = lcd->drvdata; 238 u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR }; 239 u8 value; 240 int shift, i; 241 242 if (!strchr(esc, ';')) 243 return 0; 244 245 esc++; 246 247 buf[1] = *(esc++) - '0'; 248 if (buf[1] > 7) 249 return 1; 250 251 i = 2; 252 shift = 0; 253 value = 0; 254 while (*esc && i < LCD2S_CHARACTER_SIZE + 2) { 255 int half; 256 257 shift ^= 4; 258 half = hex_to_bin(*esc++); 259 if (half < 0) 260 continue; 261 262 value |= half << shift; 263 if (shift == 0) { 264 buf[i++] = value; 265 value = 0; 266 } 267 } 268 269 lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf)); 270 return 1; 271 } 272 273 static int lcd2s_clear_display(struct charlcd *lcd) 274 { 275 struct lcd2s_data *lcd2s = lcd->drvdata; 276 277 /* This implicitly sets cursor to first row and column */ 278 lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR); 279 return 0; 280 } 281 282 static const struct charlcd_ops lcd2s_ops = { 283 .print = lcd2s_print, 284 .backlight = lcd2s_backlight, 285 .gotoxy = lcd2s_gotoxy, 286 .home = lcd2s_home, 287 .clear_display = lcd2s_clear_display, 288 .init_display = lcd2s_init_display, 289 .shift_cursor = lcd2s_shift_cursor, 290 .shift_display = lcd2s_shift_display, 291 .display = lcd2s_display, 292 .cursor = lcd2s_cursor, 293 .blink = lcd2s_blink, 294 .fontsize = lcd2s_fontsize, 295 .lines = lcd2s_lines, 296 .redefine_char = lcd2s_redefine_char, 297 }; 298 299 static int lcd2s_i2c_probe(struct i2c_client *i2c) 300 { 301 struct charlcd *lcd; 302 struct lcd2s_data *lcd2s; 303 int err; 304 305 if (!i2c_check_functionality(i2c->adapter, 306 I2C_FUNC_SMBUS_WRITE_BYTE_DATA | 307 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) 308 return -EIO; 309 310 /* Test, if the display is responding */ 311 err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF); 312 if (err < 0) 313 return err; 314 315 lcd = charlcd_alloc(sizeof(*lcd2s)); 316 if (!lcd) 317 return -ENOMEM; 318 319 lcd->ops = &lcd2s_ops; 320 321 lcd2s = lcd->drvdata; 322 lcd2s->i2c = i2c; 323 lcd2s->charlcd = lcd; 324 325 /* Required properties */ 326 err = device_property_read_u32(&i2c->dev, "display-height-chars", 327 &lcd->height); 328 if (err) 329 goto fail1; 330 331 err = device_property_read_u32(&i2c->dev, "display-width-chars", 332 &lcd->width); 333 if (err) 334 goto fail1; 335 336 err = charlcd_register(lcd2s->charlcd); 337 if (err) 338 goto fail1; 339 340 i2c_set_clientdata(i2c, lcd2s); 341 return 0; 342 343 fail1: 344 charlcd_free(lcd2s->charlcd); 345 return err; 346 } 347 348 static void lcd2s_i2c_remove(struct i2c_client *i2c) 349 { 350 struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c); 351 352 charlcd_unregister(lcd2s->charlcd); 353 charlcd_free(lcd2s->charlcd); 354 } 355 356 static const struct i2c_device_id lcd2s_i2c_id[] = { 357 { "lcd2s" }, 358 { } 359 }; 360 MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id); 361 362 static const struct of_device_id lcd2s_of_table[] = { 363 { .compatible = "modtronix,lcd2s" }, 364 { } 365 }; 366 MODULE_DEVICE_TABLE(of, lcd2s_of_table); 367 368 static struct i2c_driver lcd2s_i2c_driver = { 369 .driver = { 370 .name = "lcd2s", 371 .of_match_table = lcd2s_of_table, 372 }, 373 .probe = lcd2s_i2c_probe, 374 .remove = lcd2s_i2c_remove, 375 .id_table = lcd2s_i2c_id, 376 }; 377 module_i2c_driver(lcd2s_i2c_driver); 378 379 MODULE_DESCRIPTION("LCD2S character display driver"); 380 MODULE_AUTHOR("Lars Poeschel"); 381 MODULE_LICENSE("GPL"); 382