Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 #include <linux/module.h>
0003 #include <linux/sched.h>
0004 #include <linux/slab.h>
0005 
0006 #include "charlcd.h"
0007 #include "hd44780_common.h"
0008 
0009 /* LCD commands */
0010 #define LCD_CMD_DISPLAY_CLEAR   0x01    /* Clear entire display */
0011 
0012 #define LCD_CMD_ENTRY_MODE  0x04    /* Set entry mode */
0013 #define LCD_CMD_CURSOR_INC  0x02    /* Increment cursor */
0014 
0015 #define LCD_CMD_DISPLAY_CTRL    0x08    /* Display control */
0016 #define LCD_CMD_DISPLAY_ON  0x04    /* Set display on */
0017 #define LCD_CMD_CURSOR_ON   0x02    /* Set cursor on */
0018 #define LCD_CMD_BLINK_ON    0x01    /* Set blink on */
0019 
0020 #define LCD_CMD_SHIFT       0x10    /* Shift cursor/display */
0021 #define LCD_CMD_DISPLAY_SHIFT   0x08    /* Shift display instead of cursor */
0022 #define LCD_CMD_SHIFT_RIGHT 0x04    /* Shift display/cursor to the right */
0023 
0024 #define LCD_CMD_FUNCTION_SET    0x20    /* Set function */
0025 #define LCD_CMD_DATA_LEN_8BITS  0x10    /* Set data length to 8 bits */
0026 #define LCD_CMD_TWO_LINES   0x08    /* Set to two display lines */
0027 #define LCD_CMD_FONT_5X10_DOTS  0x04    /* Set char font to 5x10 dots */
0028 
0029 #define LCD_CMD_SET_CGRAM_ADDR  0x40    /* Set char generator RAM address */
0030 
0031 #define LCD_CMD_SET_DDRAM_ADDR  0x80    /* Set display data RAM address */
0032 
0033 /* sleeps that many milliseconds with a reschedule */
0034 static void long_sleep(int ms)
0035 {
0036     schedule_timeout_interruptible(msecs_to_jiffies(ms));
0037 }
0038 
0039 int hd44780_common_print(struct charlcd *lcd, int c)
0040 {
0041     struct hd44780_common *hdc = lcd->drvdata;
0042 
0043     if (lcd->addr.x < hdc->bwidth) {
0044         hdc->write_data(hdc, c);
0045         return 0;
0046     }
0047 
0048     return 1;
0049 }
0050 EXPORT_SYMBOL_GPL(hd44780_common_print);
0051 
0052 int hd44780_common_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
0053 {
0054     struct hd44780_common *hdc = lcd->drvdata;
0055     unsigned int addr;
0056 
0057     /*
0058      * we force the cursor to stay at the end of the
0059      * line if it wants to go farther
0060      */
0061     addr = x < hdc->bwidth ? x & (hdc->hwidth - 1) : hdc->bwidth - 1;
0062     if (y & 1)
0063         addr += hdc->hwidth;
0064     if (y & 2)
0065         addr += hdc->bwidth;
0066     hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
0067     return 0;
0068 }
0069 EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);
0070 
0071 int hd44780_common_home(struct charlcd *lcd)
0072 {
0073     return hd44780_common_gotoxy(lcd, 0, 0);
0074 }
0075 EXPORT_SYMBOL_GPL(hd44780_common_home);
0076 
0077 /* clears the display and resets X/Y */
0078 int hd44780_common_clear_display(struct charlcd *lcd)
0079 {
0080     struct hd44780_common *hdc = lcd->drvdata;
0081 
0082     hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
0083     /* datasheet says to wait 1,64 milliseconds */
0084     long_sleep(2);
0085     return 0;
0086 }
0087 EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
0088 
0089 int hd44780_common_init_display(struct charlcd *lcd)
0090 {
0091     struct hd44780_common *hdc = lcd->drvdata;
0092 
0093     void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
0094     u8 init;
0095 
0096     if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
0097         return -EINVAL;
0098 
0099     hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) |
0100         LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
0101 
0102     long_sleep(20);     /* wait 20 ms after power-up for the paranoid */
0103 
0104     /*
0105      * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
0106      * the LCD is in 8-bit mode afterwards
0107      */
0108     init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
0109     if (hdc->ifwidth == 4) {
0110         init >>= 4;
0111         write_cmd_raw = hdc->write_cmd_raw4;
0112     } else {
0113         write_cmd_raw = hdc->write_cmd;
0114     }
0115     write_cmd_raw(hdc, init);
0116     long_sleep(10);
0117     write_cmd_raw(hdc, init);
0118     long_sleep(10);
0119     write_cmd_raw(hdc, init);
0120     long_sleep(10);
0121 
0122     if (hdc->ifwidth == 4) {
0123         /* Switch to 4-bit mode, 1 line, small fonts */
0124         hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
0125         long_sleep(10);
0126     }
0127 
0128     /* set font height and lines number */
0129     hdc->write_cmd(hdc,
0130         LCD_CMD_FUNCTION_SET |
0131         ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
0132         ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
0133             LCD_CMD_FONT_5X10_DOTS : 0) |
0134         ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
0135             LCD_CMD_TWO_LINES : 0));
0136     long_sleep(10);
0137 
0138     /* display off, cursor off, blink off */
0139     hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
0140     long_sleep(10);
0141 
0142     hdc->write_cmd(hdc,
0143         LCD_CMD_DISPLAY_CTRL |  /* set display mode */
0144         ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
0145             LCD_CMD_DISPLAY_ON : 0) |
0146         ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
0147             LCD_CMD_CURSOR_ON : 0) |
0148         ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
0149             LCD_CMD_BLINK_ON : 0));
0150 
0151     charlcd_backlight(lcd,
0152             (hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0);
0153 
0154     long_sleep(10);
0155 
0156     /* entry mode set : increment, cursor shifting */
0157     hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
0158 
0159     hd44780_common_clear_display(lcd);
0160     return 0;
0161 }
0162 EXPORT_SYMBOL_GPL(hd44780_common_init_display);
0163 
0164 int hd44780_common_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
0165 {
0166     struct hd44780_common *hdc = lcd->drvdata;
0167 
0168     if (dir == CHARLCD_SHIFT_LEFT) {
0169         /* back one char if not at end of line */
0170         if (lcd->addr.x < hdc->bwidth)
0171             hdc->write_cmd(hdc, LCD_CMD_SHIFT);
0172     } else if (dir == CHARLCD_SHIFT_RIGHT) {
0173         /* allow the cursor to pass the end of the line */
0174         if (lcd->addr.x < (hdc->bwidth - 1))
0175             hdc->write_cmd(hdc,
0176                     LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
0177     }
0178 
0179     return 0;
0180 }
0181 EXPORT_SYMBOL_GPL(hd44780_common_shift_cursor);
0182 
0183 int hd44780_common_shift_display(struct charlcd *lcd,
0184         enum charlcd_shift_dir dir)
0185 {
0186     struct hd44780_common *hdc = lcd->drvdata;
0187 
0188     if (dir == CHARLCD_SHIFT_LEFT)
0189         hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
0190     else if (dir == CHARLCD_SHIFT_RIGHT)
0191         hdc->write_cmd(hdc, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
0192             LCD_CMD_SHIFT_RIGHT);
0193 
0194     return 0;
0195 }
0196 EXPORT_SYMBOL_GPL(hd44780_common_shift_display);
0197 
0198 static void hd44780_common_set_mode(struct hd44780_common *hdc)
0199 {
0200     hdc->write_cmd(hdc,
0201         LCD_CMD_DISPLAY_CTRL |
0202         ((hdc->hd44780_common_flags & LCD_FLAG_D) ?
0203             LCD_CMD_DISPLAY_ON : 0) |
0204         ((hdc->hd44780_common_flags & LCD_FLAG_C) ?
0205             LCD_CMD_CURSOR_ON : 0) |
0206         ((hdc->hd44780_common_flags & LCD_FLAG_B) ?
0207             LCD_CMD_BLINK_ON : 0));
0208 }
0209 
0210 int hd44780_common_display(struct charlcd *lcd, enum charlcd_onoff on)
0211 {
0212     struct hd44780_common *hdc = lcd->drvdata;
0213 
0214     if (on == CHARLCD_ON)
0215         hdc->hd44780_common_flags |= LCD_FLAG_D;
0216     else
0217         hdc->hd44780_common_flags &= ~LCD_FLAG_D;
0218 
0219     hd44780_common_set_mode(hdc);
0220     return 0;
0221 }
0222 EXPORT_SYMBOL_GPL(hd44780_common_display);
0223 
0224 int hd44780_common_cursor(struct charlcd *lcd, enum charlcd_onoff on)
0225 {
0226     struct hd44780_common *hdc = lcd->drvdata;
0227 
0228     if (on == CHARLCD_ON)
0229         hdc->hd44780_common_flags |= LCD_FLAG_C;
0230     else
0231         hdc->hd44780_common_flags &= ~LCD_FLAG_C;
0232 
0233     hd44780_common_set_mode(hdc);
0234     return 0;
0235 }
0236 EXPORT_SYMBOL_GPL(hd44780_common_cursor);
0237 
0238 int hd44780_common_blink(struct charlcd *lcd, enum charlcd_onoff on)
0239 {
0240     struct hd44780_common *hdc = lcd->drvdata;
0241 
0242     if (on == CHARLCD_ON)
0243         hdc->hd44780_common_flags |= LCD_FLAG_B;
0244     else
0245         hdc->hd44780_common_flags &= ~LCD_FLAG_B;
0246 
0247     hd44780_common_set_mode(hdc);
0248     return 0;
0249 }
0250 EXPORT_SYMBOL_GPL(hd44780_common_blink);
0251 
0252 static void hd44780_common_set_function(struct hd44780_common *hdc)
0253 {
0254     hdc->write_cmd(hdc,
0255         LCD_CMD_FUNCTION_SET |
0256         ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
0257         ((hdc->hd44780_common_flags & LCD_FLAG_F) ?
0258             LCD_CMD_FONT_5X10_DOTS : 0) |
0259         ((hdc->hd44780_common_flags & LCD_FLAG_N) ?
0260             LCD_CMD_TWO_LINES : 0));
0261 }
0262 
0263 int hd44780_common_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
0264 {
0265     struct hd44780_common *hdc = lcd->drvdata;
0266 
0267     if (size == CHARLCD_FONTSIZE_LARGE)
0268         hdc->hd44780_common_flags |= LCD_FLAG_F;
0269     else
0270         hdc->hd44780_common_flags &= ~LCD_FLAG_F;
0271 
0272     hd44780_common_set_function(hdc);
0273     return 0;
0274 }
0275 EXPORT_SYMBOL_GPL(hd44780_common_fontsize);
0276 
0277 int hd44780_common_lines(struct charlcd *lcd, enum charlcd_lines lines)
0278 {
0279     struct hd44780_common *hdc = lcd->drvdata;
0280 
0281     if (lines == CHARLCD_LINES_2)
0282         hdc->hd44780_common_flags |= LCD_FLAG_N;
0283     else
0284         hdc->hd44780_common_flags &= ~LCD_FLAG_N;
0285 
0286     hd44780_common_set_function(hdc);
0287     return 0;
0288 }
0289 EXPORT_SYMBOL_GPL(hd44780_common_lines);
0290 
0291 int hd44780_common_redefine_char(struct charlcd *lcd, char *esc)
0292 {
0293     /* Generator : LGcxxxxx...xx; must have <c> between '0'
0294      * and '7', representing the numerical ASCII code of the
0295      * redefined character, and <xx...xx> a sequence of 16
0296      * hex digits representing 8 bytes for each character.
0297      * Most LCDs will only use 5 lower bits of the 7 first
0298      * bytes.
0299      */
0300 
0301     struct hd44780_common *hdc = lcd->drvdata;
0302     unsigned char cgbytes[8];
0303     unsigned char cgaddr;
0304     int cgoffset;
0305     int shift;
0306     char value;
0307     int addr;
0308 
0309     if (!strchr(esc, ';'))
0310         return 0;
0311 
0312     esc++;
0313 
0314     cgaddr = *(esc++) - '0';
0315     if (cgaddr > 7)
0316         return 1;
0317 
0318     cgoffset = 0;
0319     shift = 0;
0320     value = 0;
0321     while (*esc && cgoffset < 8) {
0322         int half;
0323 
0324         shift ^= 4;
0325         half = hex_to_bin(*esc++);
0326         if (half < 0)
0327             continue;
0328 
0329         value |= half << shift;
0330         if (shift == 0) {
0331             cgbytes[cgoffset++] = value;
0332             value = 0;
0333         }
0334     }
0335 
0336     hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
0337     for (addr = 0; addr < cgoffset; addr++)
0338         hdc->write_data(hdc, cgbytes[addr]);
0339 
0340     /* ensures that we stop writing to CGRAM */
0341     lcd->ops->gotoxy(lcd, lcd->addr.x, lcd->addr.y);
0342     return 1;
0343 }
0344 EXPORT_SYMBOL_GPL(hd44780_common_redefine_char);
0345 
0346 struct hd44780_common *hd44780_common_alloc(void)
0347 {
0348     struct hd44780_common *hd;
0349 
0350     hd = kzalloc(sizeof(*hd), GFP_KERNEL);
0351     if (!hd)
0352         return NULL;
0353 
0354     hd->ifwidth = 8;
0355     hd->bwidth = DEFAULT_LCD_BWIDTH;
0356     hd->hwidth = DEFAULT_LCD_HWIDTH;
0357     return hd;
0358 }
0359 EXPORT_SYMBOL_GPL(hd44780_common_alloc);
0360 
0361 MODULE_LICENSE("GPL");