Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  Console driver for LCD2S 4x20 character displays connected through i2c.
0004  *  The display also has a SPI interface, but the driver does not support
0005  *  this yet.
0006  *
0007  *  This is a driver allowing you to use a LCD2S 4x20 from Modtronix
0008  *  engineering as auxdisplay character device.
0009  *
0010  *  (C) 2019 by Lemonage Software GmbH
0011  *  Author: Lars Pöschel <poeschel@lemonage.de>
0012  *  All rights reserved.
0013  */
0014 #include <linux/kernel.h>
0015 #include <linux/mod_devicetable.h>
0016 #include <linux/module.h>
0017 #include <linux/property.h>
0018 #include <linux/slab.h>
0019 #include <linux/i2c.h>
0020 #include <linux/delay.h>
0021 
0022 #include "charlcd.h"
0023 
0024 #define LCD2S_CMD_CUR_MOVES_FWD     0x09
0025 #define LCD2S_CMD_CUR_BLINK_OFF     0x10
0026 #define LCD2S_CMD_CUR_UL_OFF        0x11
0027 #define LCD2S_CMD_DISPLAY_OFF       0x12
0028 #define LCD2S_CMD_CUR_BLINK_ON      0x18
0029 #define LCD2S_CMD_CUR_UL_ON     0x19
0030 #define LCD2S_CMD_DISPLAY_ON        0x1a
0031 #define LCD2S_CMD_BACKLIGHT_OFF     0x20
0032 #define LCD2S_CMD_BACKLIGHT_ON      0x28
0033 #define LCD2S_CMD_WRITE         0x80
0034 #define LCD2S_CMD_MOV_CUR_RIGHT     0x83
0035 #define LCD2S_CMD_MOV_CUR_LEFT      0x84
0036 #define LCD2S_CMD_SHIFT_RIGHT       0x85
0037 #define LCD2S_CMD_SHIFT_LEFT        0x86
0038 #define LCD2S_CMD_SHIFT_UP      0x87
0039 #define LCD2S_CMD_SHIFT_DOWN        0x88
0040 #define LCD2S_CMD_CUR_ADDR      0x89
0041 #define LCD2S_CMD_CUR_POS       0x8a
0042 #define LCD2S_CMD_CUR_RESET     0x8b
0043 #define LCD2S_CMD_CLEAR         0x8c
0044 #define LCD2S_CMD_DEF_CUSTOM_CHAR   0x92
0045 #define LCD2S_CMD_READ_STATUS       0xd0
0046 
0047 #define LCD2S_CHARACTER_SIZE        8
0048 
0049 #define LCD2S_STATUS_BUF_MASK       0x7f
0050 
0051 struct lcd2s_data {
0052     struct i2c_client *i2c;
0053     struct charlcd *charlcd;
0054 };
0055 
0056 static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count)
0057 {
0058     s32 status;
0059 
0060     status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS);
0061     if (status < 0)
0062         return status;
0063 
0064     while ((status & LCD2S_STATUS_BUF_MASK) < count) {
0065         mdelay(1);
0066         status = i2c_smbus_read_byte_data(client,
0067                           LCD2S_CMD_READ_STATUS);
0068         if (status < 0)
0069             return status;
0070     }
0071     return 0;
0072 }
0073 
0074 static int lcd2s_i2c_master_send(const struct i2c_client *client,
0075                  const char *buf, int count)
0076 {
0077     s32 status;
0078 
0079     status = lcd2s_wait_buf_free(client, count);
0080     if (status < 0)
0081         return status;
0082 
0083     return i2c_master_send(client, buf, count);
0084 }
0085 
0086 static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
0087 {
0088     s32 status;
0089 
0090     status = lcd2s_wait_buf_free(client, 1);
0091     if (status < 0)
0092         return status;
0093 
0094     return i2c_smbus_write_byte(client, value);
0095 }
0096 
0097 static int lcd2s_print(struct charlcd *lcd, int c)
0098 {
0099     struct lcd2s_data *lcd2s = lcd->drvdata;
0100     u8 buf[2] = { LCD2S_CMD_WRITE, c };
0101 
0102     lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
0103     return 0;
0104 }
0105 
0106 static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
0107 {
0108     struct lcd2s_data *lcd2s = lcd->drvdata;
0109     u8 buf[3] = { LCD2S_CMD_CUR_POS, y + 1, x + 1 };
0110 
0111     lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
0112 
0113     return 0;
0114 }
0115 
0116 static int lcd2s_home(struct charlcd *lcd)
0117 {
0118     struct lcd2s_data *lcd2s = lcd->drvdata;
0119 
0120     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET);
0121     return 0;
0122 }
0123 
0124 static int lcd2s_init_display(struct charlcd *lcd)
0125 {
0126     struct lcd2s_data *lcd2s = lcd->drvdata;
0127 
0128     /* turn everything off, but display on */
0129     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
0130     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
0131     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD);
0132     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
0133     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
0134     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
0135 
0136     return 0;
0137 }
0138 
0139 static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
0140 {
0141     struct lcd2s_data *lcd2s = lcd->drvdata;
0142 
0143     if (dir == CHARLCD_SHIFT_LEFT)
0144         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT);
0145     else
0146         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT);
0147 
0148     return 0;
0149 }
0150 
0151 static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir)
0152 {
0153     struct lcd2s_data *lcd2s = lcd->drvdata;
0154 
0155     if (dir == CHARLCD_SHIFT_LEFT)
0156         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT);
0157     else
0158         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT);
0159 
0160     return 0;
0161 }
0162 
0163 static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on)
0164 {
0165     struct lcd2s_data *lcd2s = lcd->drvdata;
0166 
0167     if (on)
0168         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON);
0169     else
0170         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
0171 }
0172 
0173 static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on)
0174 {
0175     struct lcd2s_data *lcd2s = lcd->drvdata;
0176 
0177     if (on)
0178         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
0179     else
0180         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF);
0181 
0182     return 0;
0183 }
0184 
0185 static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on)
0186 {
0187     struct lcd2s_data *lcd2s = lcd->drvdata;
0188 
0189     if (on)
0190         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON);
0191     else
0192         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
0193 
0194     return 0;
0195 }
0196 
0197 static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on)
0198 {
0199     struct lcd2s_data *lcd2s = lcd->drvdata;
0200 
0201     if (on)
0202         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON);
0203     else
0204         lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
0205 
0206     return 0;
0207 }
0208 
0209 static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
0210 {
0211     return 0;
0212 }
0213 
0214 static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines)
0215 {
0216     return 0;
0217 }
0218 
0219 /*
0220  * Generator: LGcxxxxx...xx; must have <c> between '0' and '7',
0221  * representing the numerical ASCII code of the redefined character,
0222  * and <xx...xx> a sequence of 16 hex digits representing 8 bytes
0223  * for each character. Most LCDs will only use 5 lower bits of
0224  * the 7 first bytes.
0225  */
0226 static int lcd2s_redefine_char(struct charlcd *lcd, char *esc)
0227 {
0228     struct lcd2s_data *lcd2s = lcd->drvdata;
0229     u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR };
0230     u8 value;
0231     int shift, i;
0232 
0233     if (!strchr(esc, ';'))
0234         return 0;
0235 
0236     esc++;
0237 
0238     buf[1] = *(esc++) - '0';
0239     if (buf[1] > 7)
0240         return 1;
0241 
0242     i = 2;
0243     shift = 0;
0244     value = 0;
0245     while (*esc && i < LCD2S_CHARACTER_SIZE + 2) {
0246         int half;
0247 
0248         shift ^= 4;
0249         half = hex_to_bin(*esc++);
0250         if (half < 0)
0251             continue;
0252 
0253         value |= half << shift;
0254         if (shift == 0) {
0255             buf[i++] = value;
0256             value = 0;
0257         }
0258     }
0259 
0260     lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
0261     return 1;
0262 }
0263 
0264 static int lcd2s_clear_display(struct charlcd *lcd)
0265 {
0266     struct lcd2s_data *lcd2s = lcd->drvdata;
0267 
0268     /* This implicitly sets cursor to first row and column */
0269     lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
0270     return 0;
0271 }
0272 
0273 static const struct charlcd_ops lcd2s_ops = {
0274     .print      = lcd2s_print,
0275     .backlight  = lcd2s_backlight,
0276     .gotoxy     = lcd2s_gotoxy,
0277     .home       = lcd2s_home,
0278     .clear_display  = lcd2s_clear_display,
0279     .init_display   = lcd2s_init_display,
0280     .shift_cursor   = lcd2s_shift_cursor,
0281     .shift_display  = lcd2s_shift_display,
0282     .display    = lcd2s_display,
0283     .cursor     = lcd2s_cursor,
0284     .blink      = lcd2s_blink,
0285     .fontsize   = lcd2s_fontsize,
0286     .lines      = lcd2s_lines,
0287     .redefine_char  = lcd2s_redefine_char,
0288 };
0289 
0290 static int lcd2s_i2c_probe(struct i2c_client *i2c)
0291 {
0292     struct charlcd *lcd;
0293     struct lcd2s_data *lcd2s;
0294     int err;
0295 
0296     if (!i2c_check_functionality(i2c->adapter,
0297             I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
0298             I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
0299         return -EIO;
0300 
0301     lcd2s = devm_kzalloc(&i2c->dev, sizeof(*lcd2s), GFP_KERNEL);
0302     if (!lcd2s)
0303         return -ENOMEM;
0304 
0305     /* Test, if the display is responding */
0306     err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF);
0307     if (err < 0)
0308         return err;
0309 
0310     lcd = charlcd_alloc();
0311     if (!lcd)
0312         return -ENOMEM;
0313 
0314     lcd->drvdata = lcd2s;
0315     lcd2s->i2c = i2c;
0316     lcd2s->charlcd = lcd;
0317 
0318     /* Required properties */
0319     err = device_property_read_u32(&i2c->dev, "display-height-chars",
0320             &lcd->height);
0321     if (err)
0322         goto fail1;
0323 
0324     err = device_property_read_u32(&i2c->dev, "display-width-chars",
0325             &lcd->width);
0326     if (err)
0327         goto fail1;
0328 
0329     lcd->ops = &lcd2s_ops;
0330 
0331     err = charlcd_register(lcd2s->charlcd);
0332     if (err)
0333         goto fail1;
0334 
0335     i2c_set_clientdata(i2c, lcd2s);
0336     return 0;
0337 
0338 fail1:
0339     charlcd_free(lcd2s->charlcd);
0340     return err;
0341 }
0342 
0343 static int lcd2s_i2c_remove(struct i2c_client *i2c)
0344 {
0345     struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c);
0346 
0347     charlcd_unregister(lcd2s->charlcd);
0348     charlcd_free(lcd2s->charlcd);
0349     return 0;
0350 }
0351 
0352 static const struct i2c_device_id lcd2s_i2c_id[] = {
0353     { "lcd2s", 0 },
0354     { }
0355 };
0356 MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
0357 
0358 static const struct of_device_id lcd2s_of_table[] = {
0359     { .compatible = "modtronix,lcd2s" },
0360     { }
0361 };
0362 MODULE_DEVICE_TABLE(of, lcd2s_of_table);
0363 
0364 static struct i2c_driver lcd2s_i2c_driver = {
0365     .driver = {
0366         .name = "lcd2s",
0367         .of_match_table = lcd2s_of_table,
0368     },
0369     .probe_new = lcd2s_i2c_probe,
0370     .remove = lcd2s_i2c_remove,
0371     .id_table = lcd2s_i2c_id,
0372 };
0373 module_i2c_driver(lcd2s_i2c_driver);
0374 
0375 MODULE_DESCRIPTION("LCD2S character display driver");
0376 MODULE_AUTHOR("Lars Poeschel");
0377 MODULE_LICENSE("GPL");