^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Character LCD driver for Linux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2016-2017 Glider bvba
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/atomic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/reboot.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/workqueue.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <generated/utsrelease.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include "charlcd.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define DEFAULT_LCD_BWIDTH 40
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define DEFAULT_LCD_HWIDTH 64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) /* Keep the backlight on this many seconds for each flash */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define LCD_BL_TEMPO_PERIOD 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define LCD_FLAG_B 0x0004 /* Blink on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define LCD_FLAG_C 0x0008 /* Cursor on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define LCD_FLAG_D 0x0010 /* Display on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define LCD_FLAG_F 0x0020 /* Large font mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define LCD_FLAG_N 0x0040 /* 2-rows mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #define LCD_FLAG_L 0x0080 /* Backlight enabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /* LCD commands */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) struct charlcd_priv {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) struct charlcd lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) struct delayed_work bl_work;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) bool bl_tempo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) bool must_clear;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) /* contains the LCD config state */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) unsigned long int flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /* Contains the LCD X and Y offset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) unsigned long int x;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) unsigned long int y;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) } addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) /* Current escape sequence and it's length or -1 if outside */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) char buf[LCD_ESCAPE_LEN + 1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) int len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) } esc_seq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) unsigned long long drvdata[];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) #define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) /* Device single-open policy control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static atomic_t charlcd_available = ATOMIC_INIT(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) /* sleeps that many milliseconds with a reschedule */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static void long_sleep(int ms)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) schedule_timeout_interruptible(msecs_to_jiffies(ms));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) /* turn the backlight on or off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static void charlcd_backlight(struct charlcd *lcd, int on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (!lcd->ops->backlight)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) mutex_lock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if (!priv->bl_tempo)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) lcd->ops->backlight(lcd, on);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) mutex_unlock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static void charlcd_bl_off(struct work_struct *work)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) struct delayed_work *dwork = to_delayed_work(work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) struct charlcd_priv *priv =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) container_of(dwork, struct charlcd_priv, bl_work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) mutex_lock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (priv->bl_tempo) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) priv->bl_tempo = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (!(priv->flags & LCD_FLAG_L))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) priv->lcd.ops->backlight(&priv->lcd, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) mutex_unlock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* turn the backlight on for a little while */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) void charlcd_poke(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (!lcd->ops->backlight)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) cancel_delayed_work_sync(&priv->bl_work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) mutex_lock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) lcd->ops->backlight(lcd, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) priv->bl_tempo = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) mutex_unlock(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) EXPORT_SYMBOL_GPL(charlcd_poke);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) static void charlcd_gotoxy(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) unsigned int addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) * we force the cursor to stay at the end of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) * line if it wants to go farther
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) : lcd->bwidth - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) if (priv->addr.y & 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) addr += lcd->hwidth;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (priv->addr.y & 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) addr += lcd->bwidth;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) static void charlcd_home(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) priv->addr.x = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) priv->addr.y = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) static void charlcd_print(struct charlcd *lcd, char c)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) if (priv->addr.x < lcd->bwidth) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if (lcd->char_conv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) c = lcd->char_conv[(unsigned char)c];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) lcd->ops->write_data(lcd, c);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) priv->addr.x++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) /* prevents the cursor from wrapping onto the next line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) if (priv->addr.x == lcd->bwidth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) static void charlcd_clear_fast(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) int pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) charlcd_home(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (lcd->ops->clear_fast)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) lcd->ops->clear_fast(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) lcd->ops->write_data(lcd, ' ');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) charlcd_home(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) /* clears the display and resets X/Y */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) static void charlcd_clear_display(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) priv->addr.x = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) priv->addr.y = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) /* we must wait a few milliseconds (15) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) long_sleep(15);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) static int charlcd_init_display(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) void (*write_cmd_raw)(struct charlcd *lcd, int cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) u8 init;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (lcd->ifwidth != 4 && lcd->ifwidth != 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) LCD_FLAG_C | LCD_FLAG_B;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) long_sleep(20); /* wait 20 ms after power-up for the paranoid */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) * the LCD is in 8-bit mode afterwards
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) if (lcd->ifwidth == 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) init >>= 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) write_cmd_raw = lcd->ops->write_cmd_raw4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) write_cmd_raw = lcd->ops->write_cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) write_cmd_raw(lcd, init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) write_cmd_raw(lcd, init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) write_cmd_raw(lcd, init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if (lcd->ifwidth == 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) /* Switch to 4-bit mode, 1 line, small fonts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) /* set font height and lines number */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) LCD_CMD_FUNCTION_SET |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) /* display off, cursor off, blink off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) LCD_CMD_DISPLAY_CTRL | /* set display mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) long_sleep(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) /* entry mode set : increment, cursor shifting */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) charlcd_clear_display(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) * Parses a movement command of the form "(.*);", where the group can be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) * any number of subcommands of the form "(x|y)[0-9]+".
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * Returns whether the command is valid. The position arguments are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) * only written if the parsing was successful.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) * For instance:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) * - ";" returns (<original x>, <original y>).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) * - "x1;" returns (1, <original y>).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) * - "y2x1;" returns (1, 2).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) * - "x12y34x56;" returns (56, 34).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) * - "" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) * - "x" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) * - "x;" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) * - "x1" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) * - "xy12;" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) * - "x12yy12;" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) * - "xx" fails.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) unsigned long new_x = *x;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) unsigned long new_y = *y;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) char *p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) if (!*s)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) if (*s == ';')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) if (*s == 'x') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) new_x = simple_strtoul(s + 1, &p, 10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) if (p == s + 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) s = p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) } else if (*s == 'y') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) new_y = simple_strtoul(s + 1, &p, 10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) if (p == s + 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) s = p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) *x = new_x;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) *y = new_y;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) * These are the file operation function for user access to /dev/lcd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) * This function can also be called from inside the kernel, by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) * setting file and ppos to NULL.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) static inline int handle_lcd_special_code(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) /* LCD special codes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) int processed = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) char *esc = priv->esc_seq.buf + 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) int oldflags = priv->flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) /* check for display mode flags */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) switch (*esc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) case 'D': /* Display ON */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) priv->flags |= LCD_FLAG_D;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) case 'd': /* Display OFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) priv->flags &= ~LCD_FLAG_D;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) case 'C': /* Cursor ON */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) priv->flags |= LCD_FLAG_C;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) case 'c': /* Cursor OFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) priv->flags &= ~LCD_FLAG_C;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) case 'B': /* Blink ON */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) priv->flags |= LCD_FLAG_B;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) case 'b': /* Blink OFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) priv->flags &= ~LCD_FLAG_B;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) case '+': /* Back light ON */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) priv->flags |= LCD_FLAG_L;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) case '-': /* Back light OFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) priv->flags &= ~LCD_FLAG_L;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) case '*': /* Flash back light */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) charlcd_poke(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) case 'f': /* Small Font */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) priv->flags &= ~LCD_FLAG_F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) case 'F': /* Large Font */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) priv->flags |= LCD_FLAG_F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) case 'n': /* One Line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) priv->flags &= ~LCD_FLAG_N;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) case 'N': /* Two Lines */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) priv->flags |= LCD_FLAG_N;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) case 'l': /* Shift Cursor Left */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) if (priv->addr.x > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) /* back one char if not at end of line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) if (priv->addr.x < lcd->bwidth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) priv->addr.x--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) case 'r': /* shift cursor right */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) if (priv->addr.x < lcd->width) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) /* allow the cursor to pass the end of the line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if (priv->addr.x < (lcd->bwidth - 1))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) LCD_CMD_SHIFT | LCD_CMD_SHIFT_RIGHT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) priv->addr.x++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) case 'L': /* shift display left */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) case 'R': /* shift display right */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) LCD_CMD_SHIFT_RIGHT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) case 'k': { /* kill end of line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) int x;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) for (x = priv->addr.x; x < lcd->bwidth; x++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) lcd->ops->write_data(lcd, ' ');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) /* restore cursor position */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) case 'I': /* reinitialize display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) charlcd_init_display(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) case 'G': {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) /* Generator : LGcxxxxx...xx; must have <c> between '0'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) * and '7', representing the numerical ASCII code of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) * redefined character, and <xx...xx> a sequence of 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) * hex digits representing 8 bytes for each character.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) * Most LCDs will only use 5 lower bits of the 7 first
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) * bytes.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) unsigned char cgbytes[8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) unsigned char cgaddr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) int cgoffset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) int shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) char value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) int addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) if (!strchr(esc, ';'))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) esc++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) cgaddr = *(esc++) - '0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) if (cgaddr > 7) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) cgoffset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) shift = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) value = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) while (*esc && cgoffset < 8) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) int half;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) shift ^= 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) half = hex_to_bin(*esc++);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) if (half < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) value |= half << shift;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) if (shift == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) cgbytes[cgoffset++] = value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) value = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) for (addr = 0; addr < cgoffset; addr++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) lcd->ops->write_data(lcd, cgbytes[addr]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) /* ensures that we stop writing to CGRAM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) case 'x': /* gotoxy : LxXXX[yYYY]; */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) case 'y': /* gotoxy : LyYYY[xXXX]; */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) /* If the command is valid, move to the new address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) if (parse_xy(esc, &priv->addr.x, &priv->addr.y))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) /* Regardless of its validity, mark as processed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) /* TODO: This indent party here got ugly, clean it! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) /* Check whether one flag was changed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) if (oldflags == priv->flags)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) return processed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) /* check whether one of B,C,D flags were changed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) if ((oldflags ^ priv->flags) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) /* set display mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) LCD_CMD_DISPLAY_CTRL |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) /* check whether one of F,N flags was changed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) lcd->ops->write_cmd(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) LCD_CMD_FUNCTION_SET |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) /* check whether L flag was changed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) return processed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) static void charlcd_write_char(struct charlcd *lcd, char c)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) /* first, we'll test if we're in escape mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) if ((c != '\n') && priv->esc_seq.len >= 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) /* yes, let's add this char to the buffer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) priv->esc_seq.buf[priv->esc_seq.len++] = c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) priv->esc_seq.buf[priv->esc_seq.len] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) /* aborts any previous escape sequence */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) priv->esc_seq.len = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) switch (c) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) case LCD_ESCAPE_CHAR:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) /* start of an escape sequence */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) priv->esc_seq.len = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) priv->esc_seq.buf[priv->esc_seq.len] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) case '\b':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) /* go back one char and clear it */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) if (priv->addr.x > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) * check if we're not at the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) * end of the line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) if (priv->addr.x < lcd->bwidth)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) /* back one char */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) priv->addr.x--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) /* replace with a space */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) lcd->ops->write_data(lcd, ' ');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) /* back one char again */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) case '\f':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) /* quickly clear the display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) charlcd_clear_fast(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) case '\n':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) * flush the remainder of the current line and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) * go to the beginning of the next line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) for (; priv->addr.x < lcd->bwidth; priv->addr.x++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) lcd->ops->write_data(lcd, ' ');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) priv->addr.x = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) priv->addr.y = (priv->addr.y + 1) % lcd->height;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) case '\r':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) /* go to the beginning of the same line */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) priv->addr.x = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) charlcd_gotoxy(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) case '\t':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) /* print a space instead of the tab */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) charlcd_print(lcd, ' ');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) /* simply print this char */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) charlcd_print(lcd, c);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) * now we'll see if we're in an escape mode and if the current
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) * escape sequence can be understood.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) if (priv->esc_seq.len >= 2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) int processed = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) if (!strcmp(priv->esc_seq.buf, "[2J")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) /* clear the display */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) charlcd_clear_fast(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) } else if (!strcmp(priv->esc_seq.buf, "[H")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) /* cursor to home */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) charlcd_home(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) processed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) /* codes starting with ^[[L */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) else if ((priv->esc_seq.len >= 3) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) (priv->esc_seq.buf[0] == '[') &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) (priv->esc_seq.buf[1] == 'L')) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) processed = handle_lcd_special_code(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) /* LCD special escape codes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) * flush the escape sequence if it's been processed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) * or if it is getting too long.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) priv->esc_seq.len = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) } /* escape codes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) static struct charlcd *the_charlcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) static ssize_t charlcd_write(struct file *file, const char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) const char __user *tmp = buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) for (; count-- > 0; (*ppos)++, tmp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) * let's be a little nice with other processes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) * that need some CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) schedule();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) if (get_user(c, tmp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) charlcd_write_char(the_charlcd, c);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) return tmp - buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) static int charlcd_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) struct charlcd_priv *priv = charlcd_to_priv(the_charlcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) ret = -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) if (!atomic_dec_and_test(&charlcd_available))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) goto fail; /* open only once at a time */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) ret = -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) if (file->f_mode & FMODE_READ) /* device is write-only */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) if (priv->must_clear) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) charlcd_clear_display(&priv->lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) priv->must_clear = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) return nonseekable_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) atomic_inc(&charlcd_available);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) static int charlcd_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) atomic_inc(&charlcd_available);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) static const struct file_operations charlcd_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) .write = charlcd_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) .open = charlcd_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) .release = charlcd_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) static struct miscdevice charlcd_dev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) .minor = LCD_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) .name = "lcd",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) .fops = &charlcd_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) static void charlcd_puts(struct charlcd *lcd, const char *s)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) const char *tmp = s;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) int count = strlen(s);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) for (; count-- > 0; tmp++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) * let's be a little nice with other processes
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) * that need some CPU
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) schedule();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) charlcd_write_char(lcd, *tmp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) #ifdef CONFIG_PANEL_BOOT_MESSAGE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) #define LCD_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) #define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) #ifdef CONFIG_CHARLCD_BL_ON
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) #define LCD_INIT_BL "\x1b[L+"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) #elif defined(CONFIG_CHARLCD_BL_FLASH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) #define LCD_INIT_BL "\x1b[L*"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) #define LCD_INIT_BL "\x1b[L-"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) /* initialize the LCD driver */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) static int charlcd_init(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) if (lcd->ops->backlight) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) mutex_init(&priv->bl_tempo_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) * before this line, we must NOT send anything to the display.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) * Since charlcd_init_display() needs to write data, we have to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) * enable mark the LCD initialized just before.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) ret = charlcd_init_display(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) /* display a short message */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) /* clear the display on the next device opening */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) priv->must_clear = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) charlcd_home(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) struct charlcd *charlcd_alloc(unsigned int drvdata_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) struct charlcd_priv *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) struct charlcd *lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) priv = kzalloc(sizeof(*priv) + drvdata_size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) priv->esc_seq.len = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) lcd = &priv->lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) lcd->ifwidth = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) lcd->bwidth = DEFAULT_LCD_BWIDTH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) lcd->hwidth = DEFAULT_LCD_HWIDTH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) lcd->drvdata = priv->drvdata;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) return lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) EXPORT_SYMBOL_GPL(charlcd_alloc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) void charlcd_free(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) kfree(charlcd_to_priv(lcd));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) EXPORT_SYMBOL_GPL(charlcd_free);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) static int panel_notify_sys(struct notifier_block *this, unsigned long code,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) void *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) struct charlcd *lcd = the_charlcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) switch (code) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) case SYS_DOWN:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) charlcd_puts(lcd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) case SYS_HALT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820) charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) case SYS_POWER_OFF:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) return NOTIFY_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) static struct notifier_block panel_notifier = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832) panel_notify_sys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) int charlcd_register(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) ret = charlcd_init(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) ret = misc_register(&charlcd_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) the_charlcd = lcd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) register_reboot_notifier(&panel_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) EXPORT_SYMBOL_GPL(charlcd_register);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) int charlcd_unregister(struct charlcd *lcd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) struct charlcd_priv *priv = charlcd_to_priv(lcd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) unregister_reboot_notifier(&panel_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) misc_deregister(&charlcd_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862) the_charlcd = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) if (lcd->ops->backlight) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) cancel_delayed_work_sync(&priv->bl_work);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865) priv->lcd.ops->backlight(&priv->lcd, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) EXPORT_SYMBOL_GPL(charlcd_unregister);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872) MODULE_LICENSE("GPL");