Auxdisplay improvements:
- 4-digit 7-segment and quad alphanumeric display support for the ht16k33 driver, allowing the user to display and scroll text messages, from Geert Uytterhoeven. - An assortment of fixes and cleanups from Geert Uytterhoeven. - Header cleanups from Mianhan Liu. - Whitespace cleanup from Huiquan Deng. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAmGGj/EACgkQGXyLc2ht IW0bNg/+PIA7158LvIcNxpPpCwAdaZZznmawvwjSH7Gzg+sPgHH7VJ50OtoTpZnm vP7k9/l9NTxkgarpiKsDQtjEx5lK8CObVODdLvN6YccrXVIl4/CQLJdx3XK6qOe6 TQ+qNVQrpWUjPoj3sHI0mlo1641X1h7T4UViLxL+5CNZ8bh0G2dTur7YYpIVSKZd Kk9457xP6g4BomhEFKiTIUz16H+522h6yvrreChDVRf/TBFxPfRMNMrGQu6aznVh Ape1p44/muna8dKtPJXOmoQ9gagve6Y/D5SZLROFpCNKv7MwgnspnLZa0j8kwRGS qi3xeVJSb6I9GZYKHOlv9BhX8dP4fxobJngeIbTMX/j0OLRQg4KH/VrL8FCnFgmW WDfPhoee2FAyqcCk+dxBrcTkoKb50oEAX9IVhGubjnhvhD7z75DjqEkh+vW+D6um S+lylNyg7Rl1RLxEsC1b5Z8TLnyeytLGMzkUOg+4FiSVYOeDo/SJkMl2kEtCi3vq HIko+3liGJP4dNqfk6nSliM7apUP1/LKD6cmW72CKfeDbxgCOrvKL7SPyP+5J1uo URaWIVbY22MMOWKYjIB47JBNiT6/QKBNrtqLu9CTcd4hUqK84+T5ME+Ca/QpuNIP YmXCUxEF21SKLVTFuQwJFluqgAmZ8DDQyhd2lk1TuIQ+kht5hpo= =aMxr -----END PGP SIGNATURE----- Merge tag 'auxdisplay-for-linus-v5.16' of git://github.com/ojeda/linux Pull auxdisplay updates from Miguel Ojeda: - 4-digit 7-segment and quad alphanumeric display support for the ht16k33 driver, allowing the user to display and scroll text messages, from Geert Uytterhoeven. - An assortment of fixes and cleanups from Geert Uytterhoeven. - Header cleanups from Mianhan Liu. - Whitespace cleanup from Huiquan Deng. * tag 'auxdisplay-for-linus-v5.16' of git://github.com/ojeda/linux: (26 commits) MAINTAINERS: Add DT Bindings for Auxiliary Display Drivers auxdisplay: cfag12864bfb: code indent should use tabs where possible auxdisplay: ht16k33: remove superfluous header files auxdisplay: ks0108: remove superfluous header files auxdisplay: cfag12864bfb: remove superfluous header files auxdisplay: ht16k33: Make use of device properties auxdisplay: ht16k33: Add LED support dt-bindings: auxdisplay: ht16k33: Document LED subnode auxdisplay: ht16k33: Add support for segment displays auxdisplay: ht16k33: Extract frame buffer probing auxdisplay: ht16k33: Extract ht16k33_brightness_set() auxdisplay: ht16k33: Move delayed work auxdisplay: ht16k33: Add helper variable dev auxdisplay: ht16k33: Convert to simple i2c probe function auxdisplay: ht16k33: Remove unneeded error check in keypad probe() auxdisplay: ht16k33: Use HT16K33_FB_SIZE in ht16k33_initialize() auxdisplay: ht16k33: Fix frame buffer device blanking auxdisplay: ht16k33: Connect backlight to fbdev auxdisplay: linedisp: Add support for changing scroll rate auxdisplay: linedisp: Use kmemdup_nul() helper ...
This commit is contained in:
commit
e582e08ec0
@ -14,14 +14,21 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: holtek,ht16k33
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- adafruit,3108 # 0.56" 4-Digit 7-Segment FeatherWing Display (Red)
|
||||
- adafruit,3130 # 0.54" Quad Alphanumeric FeatherWing Display (Red)
|
||||
- const: holtek,ht16k33
|
||||
|
||||
- const: holtek,ht16k33 # Generic 16*8 LED controller with dot-matrix display
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
refresh-rate-hz:
|
||||
maxItems: 1
|
||||
description: Display update interval in Hertz
|
||||
description: Display update interval in Hertz for dot-matrix displays
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -41,10 +48,22 @@ properties:
|
||||
default: 16
|
||||
description: Initial brightness level
|
||||
|
||||
led:
|
||||
type: object
|
||||
$ref: /schemas/leds/common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- refresh-rate-hz
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
const: holtek,ht16k33
|
||||
then:
|
||||
required:
|
||||
- refresh-rate-hz
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -52,6 +71,7 @@ examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -73,5 +93,11 @@ examples:
|
||||
<MATRIX_KEY(4, 1, KEY_F9)>,
|
||||
<MATRIX_KEY(5, 1, KEY_F3)>,
|
||||
<MATRIX_KEY(6, 1, KEY_F1)>;
|
||||
|
||||
led {
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_BACKLIGHT;
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -3170,6 +3170,7 @@ F: lib/*audit.c
|
||||
AUXILIARY DISPLAY DRIVERS
|
||||
M: Miguel Ojeda <ojeda@kernel.org>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/auxdisplay/
|
||||
F: drivers/auxdisplay/
|
||||
F: include/linux/cfag12864b.h
|
||||
|
||||
|
@ -25,6 +25,12 @@ config CHARLCD
|
||||
This is some character LCD core interface that multiple drivers can
|
||||
use.
|
||||
|
||||
config LINEDISP
|
||||
tristate "Character line display core support" if COMPILE_TEST
|
||||
help
|
||||
This is the core support for single-line character displays, to be
|
||||
selected by drivers that use it.
|
||||
|
||||
config HD44780_COMMON
|
||||
tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST
|
||||
select CHARLCD
|
||||
@ -155,6 +161,7 @@ config IMG_ASCII_LCD
|
||||
depends on HAS_IOMEM
|
||||
default y if MIPS_MALTA
|
||||
select MFD_SYSCON
|
||||
select LINEDISP
|
||||
help
|
||||
Enable this to support the simple ASCII LCD displays found on
|
||||
development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
|
||||
@ -162,13 +169,16 @@ config IMG_ASCII_LCD
|
||||
|
||||
config HT16K33
|
||||
tristate "Holtek Ht16K33 LED controller with keyscan"
|
||||
depends on FB && OF && I2C && INPUT
|
||||
depends on FB && I2C && INPUT
|
||||
select FB_SYS_FOPS
|
||||
select FB_SYS_FILLRECT
|
||||
select FB_SYS_COPYAREA
|
||||
select FB_SYS_IMAGEBLIT
|
||||
select INPUT_MATRIXKMAP
|
||||
select FB_BACKLIGHT
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
select LINEDISP
|
||||
help
|
||||
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
|
||||
LED controller driver with keyscan.
|
||||
|
@ -13,3 +13,4 @@ obj-$(CONFIG_HD44780) += hd44780.o
|
||||
obj-$(CONFIG_HT16K33) += ht16k33.o
|
||||
obj-$(CONFIG_PARPORT_PANEL) += panel.o
|
||||
obj-$(CONFIG_LCD2S) += lcd2s.o
|
||||
obj-$(CONFIG_LINEDISP) += line-display.o
|
||||
|
@ -12,13 +12,10 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/cfag12864b.h>
|
||||
|
||||
#define CFAG12864BFB_NAME "cfag12864bfb"
|
||||
@ -41,8 +38,8 @@ static const struct fb_var_screeninfo cfag12864bfb_var = {
|
||||
.yres_virtual = CFAG12864B_HEIGHT,
|
||||
.bits_per_pixel = 1,
|
||||
.red = { 0, 1, 0 },
|
||||
.green = { 0, 1, 0 },
|
||||
.blue = { 0, 1, 0 },
|
||||
.green = { 0, 1, 0 },
|
||||
.blue = { 0, 1, 0 },
|
||||
.left_margin = 0,
|
||||
.right_margin = 0,
|
||||
.upper_margin = 0,
|
||||
@ -70,7 +67,7 @@ static const struct fb_ops cfag12864bfb_ops = {
|
||||
static int cfag12864bfb_probe(struct platform_device *device)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct fb_info *info = framebuffer_alloc(0, &device->dev);
|
||||
struct fb_info *info = framebuffer_alloc(0, &device->dev);
|
||||
|
||||
if (!info)
|
||||
goto none;
|
||||
|
@ -5,27 +5,39 @@
|
||||
* Author: Robin van der Gracht <robin@protonic.nl>
|
||||
*
|
||||
* Copyright: (C) 2016 Protonic Holland.
|
||||
* Copyright (C) 2021 Glider bv
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <linux/map_to_7segment.h>
|
||||
#include <linux/map_to_14segment.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
/* Registers */
|
||||
#define REG_SYSTEM_SETUP 0x20
|
||||
#define REG_SYSTEM_SETUP_OSC_ON BIT(0)
|
||||
|
||||
#define REG_DISPLAY_SETUP 0x80
|
||||
#define REG_DISPLAY_SETUP_ON BIT(0)
|
||||
#define REG_DISPLAY_SETUP_BLINK_OFF (0 << 1)
|
||||
#define REG_DISPLAY_SETUP_BLINK_2HZ (1 << 1)
|
||||
#define REG_DISPLAY_SETUP_BLINK_1HZ (2 << 1)
|
||||
#define REG_DISPLAY_SETUP_BLINK_0HZ5 (3 << 1)
|
||||
|
||||
#define REG_ROWINT_SET 0xA0
|
||||
#define REG_ROWINT_SET_INT_EN BIT(0)
|
||||
@ -47,6 +59,12 @@
|
||||
#define BYTES_PER_ROW (HT16K33_MATRIX_LED_MAX_ROWS / 8)
|
||||
#define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
|
||||
|
||||
enum display_type {
|
||||
DISP_MATRIX = 0,
|
||||
DISP_QUAD_7SEG,
|
||||
DISP_QUAD_14SEG,
|
||||
};
|
||||
|
||||
struct ht16k33_keypad {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *dev;
|
||||
@ -65,13 +83,29 @@ struct ht16k33_fbdev {
|
||||
uint32_t refresh_rate;
|
||||
uint8_t *buffer;
|
||||
uint8_t *cache;
|
||||
struct delayed_work work;
|
||||
};
|
||||
|
||||
struct ht16k33_seg {
|
||||
struct linedisp linedisp;
|
||||
union {
|
||||
struct seg7_conversion_map seg7;
|
||||
struct seg14_conversion_map seg14;
|
||||
} map;
|
||||
unsigned int map_size;
|
||||
char curr[4];
|
||||
};
|
||||
|
||||
struct ht16k33_priv {
|
||||
struct i2c_client *client;
|
||||
struct delayed_work work;
|
||||
struct led_classdev led;
|
||||
struct ht16k33_keypad keypad;
|
||||
struct ht16k33_fbdev fbdev;
|
||||
union {
|
||||
struct ht16k33_fbdev fbdev;
|
||||
struct ht16k33_seg seg;
|
||||
};
|
||||
enum display_type type;
|
||||
uint8_t blink;
|
||||
};
|
||||
|
||||
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
|
||||
@ -101,9 +135,36 @@ static const struct fb_var_screeninfo ht16k33_fb_var = {
|
||||
.vmode = FB_VMODE_NONINTERLACED,
|
||||
};
|
||||
|
||||
static const SEG7_DEFAULT_MAP(initial_map_seg7);
|
||||
static const SEG14_DEFAULT_MAP(initial_map_seg14);
|
||||
|
||||
static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ht16k33_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
memcpy(buf, &priv->seg.map, priv->seg.map_size);
|
||||
return priv->seg.map_size;
|
||||
}
|
||||
|
||||
static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t cnt)
|
||||
{
|
||||
struct ht16k33_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (cnt != priv->seg.map_size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&priv->seg.map, buf, cnt);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store);
|
||||
static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
|
||||
|
||||
static int ht16k33_display_on(struct ht16k33_priv *priv)
|
||||
{
|
||||
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON;
|
||||
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv->blink;
|
||||
|
||||
return i2c_smbus_write_byte(priv->client, data);
|
||||
}
|
||||
@ -113,11 +174,72 @@ static int ht16k33_display_off(struct ht16k33_priv *priv)
|
||||
return i2c_smbus_write_byte(priv->client, REG_DISPLAY_SETUP);
|
||||
}
|
||||
|
||||
static int ht16k33_brightness_set(struct ht16k33_priv *priv,
|
||||
unsigned int brightness)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (brightness == 0) {
|
||||
priv->blink = REG_DISPLAY_SETUP_BLINK_OFF;
|
||||
return ht16k33_display_off(priv);
|
||||
}
|
||||
|
||||
err = ht16k33_display_on(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return i2c_smbus_write_byte(priv->client,
|
||||
REG_BRIGHTNESS | (brightness - 1));
|
||||
}
|
||||
|
||||
static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
|
||||
led);
|
||||
|
||||
return ht16k33_brightness_set(priv, brightness);
|
||||
}
|
||||
|
||||
static int ht16k33_blink_set(struct led_classdev *led_cdev,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv,
|
||||
led);
|
||||
unsigned int delay;
|
||||
uint8_t blink;
|
||||
int err;
|
||||
|
||||
if (!*delay_on && !*delay_off) {
|
||||
blink = REG_DISPLAY_SETUP_BLINK_1HZ;
|
||||
delay = 1000;
|
||||
} else if (*delay_on <= 750) {
|
||||
blink = REG_DISPLAY_SETUP_BLINK_2HZ;
|
||||
delay = 500;
|
||||
} else if (*delay_on <= 1500) {
|
||||
blink = REG_DISPLAY_SETUP_BLINK_1HZ;
|
||||
delay = 1000;
|
||||
} else {
|
||||
blink = REG_DISPLAY_SETUP_BLINK_0HZ5;
|
||||
delay = 2000;
|
||||
}
|
||||
|
||||
err = i2c_smbus_write_byte(priv->client,
|
||||
REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON |
|
||||
blink);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->blink = blink;
|
||||
*delay_on = *delay_off = delay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ht16k33_fb_queue(struct ht16k33_priv *priv)
|
||||
{
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
|
||||
schedule_delayed_work(&fbdev->work, HZ / fbdev->refresh_rate);
|
||||
schedule_delayed_work(&priv->work, HZ / fbdev->refresh_rate);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -125,10 +247,9 @@ static void ht16k33_fb_queue(struct ht16k33_priv *priv)
|
||||
*/
|
||||
static void ht16k33_fb_update(struct work_struct *work)
|
||||
{
|
||||
struct ht16k33_fbdev *fbdev =
|
||||
container_of(work, struct ht16k33_fbdev, work.work);
|
||||
struct ht16k33_priv *priv =
|
||||
container_of(fbdev, struct ht16k33_priv, fbdev);
|
||||
struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
|
||||
work.work);
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
|
||||
uint8_t *p1, *p2;
|
||||
int len, pos = 0, first = -1;
|
||||
@ -168,9 +289,9 @@ static void ht16k33_fb_update(struct work_struct *work)
|
||||
|
||||
static int ht16k33_initialize(struct ht16k33_priv *priv)
|
||||
{
|
||||
uint8_t data[HT16K33_FB_SIZE];
|
||||
uint8_t byte;
|
||||
int err;
|
||||
uint8_t data[HT16K33_MATRIX_LED_MAX_COLS * 2];
|
||||
|
||||
/* Clear RAM (8 * 16 bits) */
|
||||
memset(data, 0, sizeof(data));
|
||||
@ -198,13 +319,10 @@ static int ht16k33_bl_update_status(struct backlight_device *bl)
|
||||
|
||||
if (bl->props.power != FB_BLANK_UNBLANK ||
|
||||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
|
||||
bl->props.state & BL_CORE_FBBLANK || brightness == 0) {
|
||||
return ht16k33_display_off(priv);
|
||||
}
|
||||
bl->props.state & BL_CORE_FBBLANK)
|
||||
brightness = 0;
|
||||
|
||||
ht16k33_display_on(priv);
|
||||
return i2c_smbus_write_byte(priv->client,
|
||||
REG_BRIGHTNESS | (brightness - 1));
|
||||
return ht16k33_brightness_set(priv, brightness);
|
||||
}
|
||||
|
||||
static int ht16k33_bl_check_fb(struct backlight_device *bl, struct fb_info *fi)
|
||||
@ -219,6 +337,15 @@ static const struct backlight_ops ht16k33_bl_ops = {
|
||||
.check_fb = ht16k33_bl_check_fb,
|
||||
};
|
||||
|
||||
/*
|
||||
* Blank events will be passed to the actual device handling the backlight when
|
||||
* we return zero here.
|
||||
*/
|
||||
static int ht16k33_blank(int blank, struct fb_info *info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ht16k33_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct ht16k33_priv *priv = info->par;
|
||||
@ -231,6 +358,7 @@ static const struct fb_ops ht16k33_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_read = fb_sys_read,
|
||||
.fb_write = fb_sys_write,
|
||||
.fb_blank = ht16k33_blank,
|
||||
.fb_fillrect = sys_fillrect,
|
||||
.fb_copyarea = sys_copyarea,
|
||||
.fb_imageblit = sys_imageblit,
|
||||
@ -313,10 +441,82 @@ static void ht16k33_keypad_stop(struct input_dev *dev)
|
||||
disable_irq(keypad->client->irq);
|
||||
}
|
||||
|
||||
static void ht16k33_linedisp_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(linedisp, struct ht16k33_priv,
|
||||
seg.linedisp);
|
||||
|
||||
schedule_delayed_work(&priv->work, 0);
|
||||
}
|
||||
|
||||
static void ht16k33_seg7_update(struct work_struct *work)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
|
||||
work.work);
|
||||
struct ht16k33_seg *seg = &priv->seg;
|
||||
char *s = seg->curr;
|
||||
uint8_t buf[9];
|
||||
|
||||
buf[0] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[1] = 0;
|
||||
buf[2] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[3] = 0;
|
||||
buf[4] = 0;
|
||||
buf[5] = 0;
|
||||
buf[6] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[7] = 0;
|
||||
buf[8] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
|
||||
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
|
||||
}
|
||||
|
||||
static void ht16k33_seg14_update(struct work_struct *work)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
|
||||
work.work);
|
||||
struct ht16k33_seg *seg = &priv->seg;
|
||||
char *s = seg->curr;
|
||||
uint8_t buf[8];
|
||||
|
||||
put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf);
|
||||
put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 2);
|
||||
put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 4);
|
||||
put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 6);
|
||||
|
||||
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
|
||||
}
|
||||
|
||||
static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
|
||||
unsigned int brightness)
|
||||
{
|
||||
struct led_init_data init_data = {};
|
||||
int err;
|
||||
|
||||
/* The LED is optional */
|
||||
init_data.fwnode = device_get_named_child_node(dev, "led");
|
||||
if (!init_data.fwnode)
|
||||
return 0;
|
||||
|
||||
init_data.devicename = "auxdisplay";
|
||||
init_data.devname_mandatory = true;
|
||||
|
||||
led->brightness_set_blocking = ht16k33_brightness_set_blocking;
|
||||
led->blink_set = ht16k33_blink_set;
|
||||
led->flags = LED_CORE_SUSPENDRESUME;
|
||||
led->brightness = brightness;
|
||||
led->max_brightness = MAX_BRIGHTNESS;
|
||||
|
||||
err = devm_led_classdev_register_ext(dev, led, &init_data);
|
||||
if (err)
|
||||
dev_err(dev, "Failed to register LED\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ht16k33_keypad_probe(struct i2c_client *client,
|
||||
struct ht16k33_keypad *keypad)
|
||||
{
|
||||
struct device_node *node = client->dev.of_node;
|
||||
struct device *dev = &client->dev;
|
||||
u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;
|
||||
u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;
|
||||
int err;
|
||||
@ -324,7 +524,7 @@ static int ht16k33_keypad_probe(struct i2c_client *client,
|
||||
keypad->client = client;
|
||||
init_waitqueue_head(&keypad->wait);
|
||||
|
||||
keypad->dev = devm_input_allocate_device(&client->dev);
|
||||
keypad->dev = devm_input_allocate_device(dev);
|
||||
if (!keypad->dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -335,23 +535,23 @@ static int ht16k33_keypad_probe(struct i2c_client *client,
|
||||
keypad->dev->open = ht16k33_keypad_start;
|
||||
keypad->dev->close = ht16k33_keypad_stop;
|
||||
|
||||
if (!of_get_property(node, "linux,no-autorepeat", NULL))
|
||||
if (!device_property_read_bool(dev, "linux,no-autorepeat"))
|
||||
__set_bit(EV_REP, keypad->dev->evbit);
|
||||
|
||||
err = of_property_read_u32(node, "debounce-delay-ms",
|
||||
&keypad->debounce_ms);
|
||||
err = device_property_read_u32(dev, "debounce-delay-ms",
|
||||
&keypad->debounce_ms);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "key debounce delay not specified\n");
|
||||
dev_err(dev, "key debounce delay not specified\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
|
||||
err = matrix_keypad_parse_properties(dev, &rows, &cols);
|
||||
if (err)
|
||||
return err;
|
||||
if (rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS ||
|
||||
cols > HT16K33_MATRIX_KEYPAD_MAX_COLS) {
|
||||
dev_err(&client->dev, "%u rows or %u cols out of range in DT\n",
|
||||
rows, cols);
|
||||
dev_err(dev, "%u rows or %u cols out of range in DT\n", rows,
|
||||
cols);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
@ -362,89 +562,89 @@ static int ht16k33_keypad_probe(struct i2c_client *client,
|
||||
err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,
|
||||
keypad->dev);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "failed to build keymap\n");
|
||||
dev_err(dev, "failed to build keymap\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, ht16k33_keypad_irq_thread,
|
||||
err = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
ht16k33_keypad_irq_thread,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
DRIVER_NAME, keypad);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "irq request failed %d, error %d\n",
|
||||
client->irq, err);
|
||||
dev_err(dev, "irq request failed %d, error %d\n", client->irq,
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ht16k33_keypad_stop(keypad->dev);
|
||||
|
||||
err = input_register_device(keypad->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return input_register_device(keypad->dev);
|
||||
}
|
||||
|
||||
static int ht16k33_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,
|
||||
uint32_t brightness)
|
||||
{
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
struct backlight_device *bl = NULL;
|
||||
int err;
|
||||
uint32_t dft_brightness;
|
||||
struct backlight_device *bl;
|
||||
struct backlight_properties bl_props;
|
||||
struct ht16k33_priv *priv;
|
||||
struct ht16k33_fbdev *fbdev;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "i2c_check_functionality error\n");
|
||||
return -EIO;
|
||||
if (priv->led.dev) {
|
||||
err = ht16k33_brightness_set(priv, brightness);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
/* backwards compatibility with DT lacking an led subnode */
|
||||
struct backlight_properties bl_props;
|
||||
|
||||
memset(&bl_props, 0, sizeof(struct backlight_properties));
|
||||
bl_props.type = BACKLIGHT_RAW;
|
||||
bl_props.max_brightness = MAX_BRIGHTNESS;
|
||||
|
||||
bl = devm_backlight_device_register(dev, DRIVER_NAME"-bl", dev,
|
||||
priv, &ht16k33_bl_ops,
|
||||
&bl_props);
|
||||
if (IS_ERR(bl)) {
|
||||
dev_err(dev, "failed to register backlight\n");
|
||||
return PTR_ERR(bl);
|
||||
}
|
||||
|
||||
bl->props.brightness = brightness;
|
||||
ht16k33_bl_update_status(bl);
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->client = client;
|
||||
i2c_set_clientdata(client, priv);
|
||||
fbdev = &priv->fbdev;
|
||||
|
||||
err = ht16k33_initialize(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Framebuffer (2 bytes per column) */
|
||||
BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);
|
||||
fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!fbdev->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
fbdev->cache = devm_kmalloc(&client->dev, HT16K33_FB_SIZE, GFP_KERNEL);
|
||||
fbdev->cache = devm_kmalloc(dev, HT16K33_FB_SIZE, GFP_KERNEL);
|
||||
if (!fbdev->cache) {
|
||||
err = -ENOMEM;
|
||||
goto err_fbdev_buffer;
|
||||
}
|
||||
|
||||
fbdev->info = framebuffer_alloc(0, &client->dev);
|
||||
fbdev->info = framebuffer_alloc(0, dev);
|
||||
if (!fbdev->info) {
|
||||
err = -ENOMEM;
|
||||
goto err_fbdev_buffer;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(node, "refresh-rate-hz",
|
||||
&fbdev->refresh_rate);
|
||||
err = device_property_read_u32(dev, "refresh-rate-hz",
|
||||
&fbdev->refresh_rate);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "refresh rate not specified\n");
|
||||
dev_err(dev, "refresh rate not specified\n");
|
||||
goto err_fbdev_info;
|
||||
}
|
||||
fb_bl_default_curve(fbdev->info, 0, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
|
||||
|
||||
INIT_DELAYED_WORK(&fbdev->work, ht16k33_fb_update);
|
||||
INIT_DELAYED_WORK(&priv->work, ht16k33_fb_update);
|
||||
fbdev->info->fbops = &ht16k33_fb_ops;
|
||||
fbdev->info->screen_base = (char __iomem *) fbdev->buffer;
|
||||
fbdev->info->screen_size = HT16K33_FB_SIZE;
|
||||
fbdev->info->fix = ht16k33_fb_fix;
|
||||
fbdev->info->var = ht16k33_fb_var;
|
||||
fbdev->info->bl_dev = bl;
|
||||
fbdev->info->pseudo_palette = NULL;
|
||||
fbdev->info->flags = FBINFO_FLAG_DEFAULT;
|
||||
fbdev->info->par = priv;
|
||||
@ -453,46 +653,9 @@ static int ht16k33_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
goto err_fbdev_info;
|
||||
|
||||
/* Keypad */
|
||||
if (client->irq > 0) {
|
||||
err = ht16k33_keypad_probe(client, &priv->keypad);
|
||||
if (err)
|
||||
goto err_fbdev_unregister;
|
||||
}
|
||||
|
||||
/* Backlight */
|
||||
memset(&bl_props, 0, sizeof(struct backlight_properties));
|
||||
bl_props.type = BACKLIGHT_RAW;
|
||||
bl_props.max_brightness = MAX_BRIGHTNESS;
|
||||
|
||||
bl = devm_backlight_device_register(&client->dev, DRIVER_NAME"-bl",
|
||||
&client->dev, priv,
|
||||
&ht16k33_bl_ops, &bl_props);
|
||||
if (IS_ERR(bl)) {
|
||||
dev_err(&client->dev, "failed to register backlight\n");
|
||||
err = PTR_ERR(bl);
|
||||
goto err_fbdev_unregister;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(node, "default-brightness-level",
|
||||
&dft_brightness);
|
||||
if (err) {
|
||||
dft_brightness = MAX_BRIGHTNESS;
|
||||
} else if (dft_brightness > MAX_BRIGHTNESS) {
|
||||
dev_warn(&client->dev,
|
||||
"invalid default brightness level: %u, using %u\n",
|
||||
dft_brightness, MAX_BRIGHTNESS);
|
||||
dft_brightness = MAX_BRIGHTNESS;
|
||||
}
|
||||
|
||||
bl->props.brightness = dft_brightness;
|
||||
ht16k33_bl_update_status(bl);
|
||||
|
||||
ht16k33_fb_queue(priv);
|
||||
return 0;
|
||||
|
||||
err_fbdev_unregister:
|
||||
unregister_framebuffer(fbdev->info);
|
||||
err_fbdev_info:
|
||||
framebuffer_release(fbdev->info);
|
||||
err_fbdev_buffer:
|
||||
@ -501,15 +664,138 @@ static int ht16k33_probe(struct i2c_client *client,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ht16k33_seg_probe(struct device *dev, struct ht16k33_priv *priv,
|
||||
uint32_t brightness)
|
||||
{
|
||||
struct ht16k33_seg *seg = &priv->seg;
|
||||
int err;
|
||||
|
||||
err = ht16k33_brightness_set(priv, brightness);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (priv->type) {
|
||||
case DISP_MATRIX:
|
||||
/* not handled here */
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
||||
case DISP_QUAD_7SEG:
|
||||
INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update);
|
||||
seg->map.seg7 = initial_map_seg7;
|
||||
seg->map_size = sizeof(seg->map.seg7);
|
||||
err = device_create_file(dev, &dev_attr_map_seg7);
|
||||
break;
|
||||
|
||||
case DISP_QUAD_14SEG:
|
||||
INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update);
|
||||
seg->map.seg14 = initial_map_seg14;
|
||||
seg->map_size = sizeof(seg->map.seg14);
|
||||
err = device_create_file(dev, &dev_attr_map_seg14);
|
||||
break;
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = linedisp_register(&seg->linedisp, dev, 4, seg->curr,
|
||||
ht16k33_linedisp_update);
|
||||
if (err)
|
||||
goto err_remove_map_file;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_map_file:
|
||||
device_remove_file(dev, &dev_attr_map_seg7);
|
||||
device_remove_file(dev, &dev_attr_map_seg14);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ht16k33_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const struct of_device_id *id;
|
||||
struct ht16k33_priv *priv;
|
||||
uint32_t dft_brightness;
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(dev, "i2c_check_functionality error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->client = client;
|
||||
id = i2c_of_match_device(dev->driver->of_match_table, client);
|
||||
if (id)
|
||||
priv->type = (uintptr_t)id->data;
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
err = ht16k33_initialize(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = device_property_read_u32(dev, "default-brightness-level",
|
||||
&dft_brightness);
|
||||
if (err) {
|
||||
dft_brightness = MAX_BRIGHTNESS;
|
||||
} else if (dft_brightness > MAX_BRIGHTNESS) {
|
||||
dev_warn(dev,
|
||||
"invalid default brightness level: %u, using %u\n",
|
||||
dft_brightness, MAX_BRIGHTNESS);
|
||||
dft_brightness = MAX_BRIGHTNESS;
|
||||
}
|
||||
|
||||
/* LED */
|
||||
err = ht16k33_led_probe(dev, &priv->led, dft_brightness);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Keypad */
|
||||
if (client->irq > 0) {
|
||||
err = ht16k33_keypad_probe(client, &priv->keypad);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (priv->type) {
|
||||
case DISP_MATRIX:
|
||||
/* Frame Buffer Display */
|
||||
err = ht16k33_fbdev_probe(dev, priv, dft_brightness);
|
||||
break;
|
||||
|
||||
case DISP_QUAD_7SEG:
|
||||
case DISP_QUAD_14SEG:
|
||||
/* Segment Display */
|
||||
err = ht16k33_seg_probe(dev, priv, dft_brightness);
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ht16k33_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ht16k33_priv *priv = i2c_get_clientdata(client);
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
|
||||
cancel_delayed_work_sync(&fbdev->work);
|
||||
unregister_framebuffer(fbdev->info);
|
||||
framebuffer_release(fbdev->info);
|
||||
free_page((unsigned long) fbdev->buffer);
|
||||
cancel_delayed_work_sync(&priv->work);
|
||||
|
||||
switch (priv->type) {
|
||||
case DISP_MATRIX:
|
||||
unregister_framebuffer(fbdev->info);
|
||||
framebuffer_release(fbdev->info);
|
||||
free_page((unsigned long)fbdev->buffer);
|
||||
break;
|
||||
|
||||
case DISP_QUAD_7SEG:
|
||||
case DISP_QUAD_14SEG:
|
||||
linedisp_unregister(&priv->seg.linedisp);
|
||||
device_remove_file(&client->dev, &dev_attr_map_seg7);
|
||||
device_remove_file(&client->dev, &dev_attr_map_seg14);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -521,17 +807,26 @@ static const struct i2c_device_id ht16k33_i2c_match[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, ht16k33_i2c_match);
|
||||
|
||||
static const struct of_device_id ht16k33_of_match[] = {
|
||||
{ .compatible = "holtek,ht16k33", },
|
||||
{
|
||||
/* 0.56" 4-Digit 7-Segment FeatherWing Display (Red) */
|
||||
.compatible = "adafruit,3108", .data = (void *)DISP_QUAD_7SEG,
|
||||
}, {
|
||||
/* 0.54" Quad Alphanumeric FeatherWing Display (Red) */
|
||||
.compatible = "adafruit,3130", .data = (void *)DISP_QUAD_14SEG,
|
||||
}, {
|
||||
/* Generic, assumed Dot-Matrix Display */
|
||||
.compatible = "holtek,ht16k33", .data = (void *)DISP_MATRIX,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ht16k33_of_match);
|
||||
|
||||
static struct i2c_driver ht16k33_driver = {
|
||||
.probe = ht16k33_probe,
|
||||
.probe_new = ht16k33_probe,
|
||||
.remove = ht16k33_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(ht16k33_of_match),
|
||||
.of_match_table = ht16k33_of_match,
|
||||
},
|
||||
.id_table = ht16k33_i2c_match,
|
||||
};
|
||||
|
@ -4,7 +4,6 @@
|
||||
* Author: Paul Burton <paul.burton@mips.com>
|
||||
*/
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
@ -14,7 +13,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
struct img_ascii_lcd_ctx;
|
||||
|
||||
@ -27,36 +27,26 @@ struct img_ascii_lcd_ctx;
|
||||
struct img_ascii_lcd_config {
|
||||
unsigned int num_chars;
|
||||
bool external_regmap;
|
||||
void (*update)(struct img_ascii_lcd_ctx *ctx);
|
||||
void (*update)(struct linedisp *linedisp);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct img_ascii_lcd_ctx - Private data structure
|
||||
* @pdev: the ASCII LCD platform device
|
||||
* @base: the base address of the LCD registers
|
||||
* @regmap: the regmap through which LCD registers are accessed
|
||||
* @offset: the offset within regmap to the start of the LCD registers
|
||||
* @cfg: pointer to the LCD model configuration
|
||||
* @message: the full message to display or scroll on the LCD
|
||||
* @message_len: the length of the @message string
|
||||
* @scroll_pos: index of the first character of @message currently displayed
|
||||
* @scroll_rate: scroll interval in jiffies
|
||||
* @timer: timer used to implement scrolling
|
||||
* @linedisp: line display structure
|
||||
* @curr: the string currently displayed on the LCD
|
||||
*/
|
||||
struct img_ascii_lcd_ctx {
|
||||
struct platform_device *pdev;
|
||||
union {
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
u32 offset;
|
||||
const struct img_ascii_lcd_config *cfg;
|
||||
char *message;
|
||||
unsigned int message_len;
|
||||
unsigned int scroll_pos;
|
||||
unsigned int scroll_rate;
|
||||
struct timer_list timer;
|
||||
struct linedisp linedisp;
|
||||
char curr[] __aligned(8);
|
||||
};
|
||||
|
||||
@ -64,8 +54,10 @@ struct img_ascii_lcd_ctx {
|
||||
* MIPS Boston development board
|
||||
*/
|
||||
|
||||
static void boston_update(struct img_ascii_lcd_ctx *ctx)
|
||||
static void boston_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx =
|
||||
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
|
||||
ulong val;
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
@ -90,12 +82,14 @@ static struct img_ascii_lcd_config boston_config = {
|
||||
* MIPS Malta development board
|
||||
*/
|
||||
|
||||
static void malta_update(struct img_ascii_lcd_ctx *ctx)
|
||||
static void malta_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx =
|
||||
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < ctx->cfg->num_chars; i++) {
|
||||
for (i = 0; i < linedisp->num_chars; i++) {
|
||||
err = regmap_write(ctx->regmap,
|
||||
ctx->offset + (i * 8), ctx->curr[i]);
|
||||
if (err)
|
||||
@ -173,12 +167,14 @@ static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sead3_update(struct img_ascii_lcd_ctx *ctx)
|
||||
static void sead3_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx =
|
||||
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < ctx->cfg->num_chars; i++) {
|
||||
for (i = 0; i < linedisp->num_chars; i++) {
|
||||
err = sead3_wait_lcd_idle(ctx);
|
||||
if (err)
|
||||
break;
|
||||
@ -218,130 +214,6 @@ static const struct of_device_id img_ascii_lcd_matches[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
|
||||
|
||||
/**
|
||||
* img_ascii_lcd_scroll() - scroll the display by a character
|
||||
* @t: really a pointer to the private data structure
|
||||
*
|
||||
* Scroll the current message along the LCD by one character, rearming the
|
||||
* timer if required.
|
||||
*/
|
||||
static void img_ascii_lcd_scroll(struct timer_list *t)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx = from_timer(ctx, t, timer);
|
||||
unsigned int i, ch = ctx->scroll_pos;
|
||||
unsigned int num_chars = ctx->cfg->num_chars;
|
||||
|
||||
/* update the current message string */
|
||||
for (i = 0; i < num_chars;) {
|
||||
/* copy as many characters from the string as possible */
|
||||
for (; i < num_chars && ch < ctx->message_len; i++, ch++)
|
||||
ctx->curr[i] = ctx->message[ch];
|
||||
|
||||
/* wrap around to the start of the string */
|
||||
ch = 0;
|
||||
}
|
||||
|
||||
/* update the LCD */
|
||||
ctx->cfg->update(ctx);
|
||||
|
||||
/* move on to the next character */
|
||||
ctx->scroll_pos++;
|
||||
ctx->scroll_pos %= ctx->message_len;
|
||||
|
||||
/* rearm the timer */
|
||||
if (ctx->message_len > ctx->cfg->num_chars)
|
||||
mod_timer(&ctx->timer, jiffies + ctx->scroll_rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* img_ascii_lcd_display() - set the message to be displayed
|
||||
* @ctx: pointer to the private data structure
|
||||
* @msg: the message to display
|
||||
* @count: length of msg, or -1
|
||||
*
|
||||
* Display a new message @msg on the LCD. @msg can be longer than the number of
|
||||
* characters the LCD can display, in which case it will begin scrolling across
|
||||
* the LCD display.
|
||||
*
|
||||
* Return: 0 on success, -ENOMEM on memory allocation failure
|
||||
*/
|
||||
static int img_ascii_lcd_display(struct img_ascii_lcd_ctx *ctx,
|
||||
const char *msg, ssize_t count)
|
||||
{
|
||||
char *new_msg;
|
||||
|
||||
/* stop the scroll timer */
|
||||
del_timer_sync(&ctx->timer);
|
||||
|
||||
if (count == -1)
|
||||
count = strlen(msg);
|
||||
|
||||
/* if the string ends with a newline, trim it */
|
||||
if (msg[count - 1] == '\n')
|
||||
count--;
|
||||
|
||||
new_msg = devm_kmalloc(&ctx->pdev->dev, count + 1, GFP_KERNEL);
|
||||
if (!new_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(new_msg, msg, count);
|
||||
new_msg[count] = 0;
|
||||
|
||||
if (ctx->message)
|
||||
devm_kfree(&ctx->pdev->dev, ctx->message);
|
||||
|
||||
ctx->message = new_msg;
|
||||
ctx->message_len = count;
|
||||
ctx->scroll_pos = 0;
|
||||
|
||||
/* update the LCD */
|
||||
img_ascii_lcd_scroll(&ctx->timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* message_show() - read message via sysfs
|
||||
* @dev: the LCD device
|
||||
* @attr: the LCD message attribute
|
||||
* @buf: the buffer to read the message into
|
||||
*
|
||||
* Read the current message being displayed or scrolled across the LCD display
|
||||
* into @buf, for reads from sysfs.
|
||||
*
|
||||
* Return: the number of characters written to @buf
|
||||
*/
|
||||
static ssize_t message_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", ctx->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* message_store() - write a new message via sysfs
|
||||
* @dev: the LCD device
|
||||
* @attr: the LCD message attribute
|
||||
* @buf: the buffer containing the new message
|
||||
* @count: the size of the message in @buf
|
||||
*
|
||||
* Write a new message to display or scroll across the LCD display from sysfs.
|
||||
*
|
||||
* Return: the size of the message on success, else -ERRNO
|
||||
*/
|
||||
static ssize_t message_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = img_ascii_lcd_display(ctx, buf, count);
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(message);
|
||||
|
||||
/**
|
||||
* img_ascii_lcd_probe() - probe an LCD display device
|
||||
* @pdev: the LCD platform device
|
||||
@ -355,26 +227,25 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct img_ascii_lcd_config *cfg;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct img_ascii_lcd_ctx *ctx;
|
||||
int err;
|
||||
|
||||
match = of_match_device(img_ascii_lcd_matches, &pdev->dev);
|
||||
match = of_match_device(img_ascii_lcd_matches, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
cfg = match->data;
|
||||
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx) + cfg->num_chars,
|
||||
GFP_KERNEL);
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cfg->external_regmap) {
|
||||
ctx->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node);
|
||||
ctx->regmap = syscon_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(ctx->regmap))
|
||||
return PTR_ERR(ctx->regmap);
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "offset",
|
||||
&ctx->offset))
|
||||
if (of_property_read_u32(dev->of_node, "offset", &ctx->offset))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
ctx->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
@ -382,29 +253,23 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(ctx->base);
|
||||
}
|
||||
|
||||
ctx->pdev = pdev;
|
||||
ctx->cfg = cfg;
|
||||
ctx->message = NULL;
|
||||
ctx->scroll_pos = 0;
|
||||
ctx->scroll_rate = HZ / 2;
|
||||
err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
|
||||
cfg->update);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* initialise a timer for scrolling the message */
|
||||
timer_setup(&ctx->timer, img_ascii_lcd_scroll, 0);
|
||||
/* for backwards compatibility */
|
||||
err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj,
|
||||
&ctx->linedisp.dev.kobj,
|
||||
"message", NULL);
|
||||
if (err)
|
||||
goto err_unregister;
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
/* display a default message */
|
||||
err = img_ascii_lcd_display(ctx, "Linux " UTS_RELEASE " ", -1);
|
||||
if (err)
|
||||
goto out_del_timer;
|
||||
|
||||
err = device_create_file(&pdev->dev, &dev_attr_message);
|
||||
if (err)
|
||||
goto out_del_timer;
|
||||
|
||||
return 0;
|
||||
out_del_timer:
|
||||
del_timer_sync(&ctx->timer);
|
||||
|
||||
err_unregister:
|
||||
linedisp_unregister(&ctx->linedisp);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -421,8 +286,8 @@ static int img_ascii_lcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
device_remove_file(&pdev->dev, &dev_attr_message);
|
||||
del_timer_sync(&ctx->timer);
|
||||
sysfs_remove_link(&pdev->dev.kobj, "message");
|
||||
linedisp_unregister(&ctx->linedisp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -15,10 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/parport.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ks0108.h>
|
||||
|
||||
#define KS0108_NAME "ks0108"
|
||||
|
261
drivers/auxdisplay/line-display.c
Normal file
261
drivers/auxdisplay/line-display.c
Normal file
@ -0,0 +1,261 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Character line display core support
|
||||
*
|
||||
* Copyright (C) 2016 Imagination Technologies
|
||||
* Author: Paul Burton <paul.burton@mips.com>
|
||||
*
|
||||
* Copyright (C) 2021 Glider bv
|
||||
*/
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
#define DEFAULT_SCROLL_RATE (HZ / 2)
|
||||
|
||||
/**
|
||||
* linedisp_scroll() - scroll the display by a character
|
||||
* @t: really a pointer to the private data structure
|
||||
*
|
||||
* Scroll the current message along the display by one character, rearming the
|
||||
* timer if required.
|
||||
*/
|
||||
static void linedisp_scroll(struct timer_list *t)
|
||||
{
|
||||
struct linedisp *linedisp = from_timer(linedisp, t, timer);
|
||||
unsigned int i, ch = linedisp->scroll_pos;
|
||||
unsigned int num_chars = linedisp->num_chars;
|
||||
|
||||
/* update the current message string */
|
||||
for (i = 0; i < num_chars;) {
|
||||
/* copy as many characters from the string as possible */
|
||||
for (; i < num_chars && ch < linedisp->message_len; i++, ch++)
|
||||
linedisp->buf[i] = linedisp->message[ch];
|
||||
|
||||
/* wrap around to the start of the string */
|
||||
ch = 0;
|
||||
}
|
||||
|
||||
/* update the display */
|
||||
linedisp->update(linedisp);
|
||||
|
||||
/* move on to the next character */
|
||||
linedisp->scroll_pos++;
|
||||
linedisp->scroll_pos %= linedisp->message_len;
|
||||
|
||||
/* rearm the timer */
|
||||
if (linedisp->message_len > num_chars && linedisp->scroll_rate)
|
||||
mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* linedisp_display() - set the message to be displayed
|
||||
* @linedisp: pointer to the private data structure
|
||||
* @msg: the message to display
|
||||
* @count: length of msg, or -1
|
||||
*
|
||||
* Display a new message @msg on the display. @msg can be longer than the
|
||||
* number of characters the display can display, in which case it will begin
|
||||
* scrolling across the display.
|
||||
*
|
||||
* Return: 0 on success, -ENOMEM on memory allocation failure
|
||||
*/
|
||||
static int linedisp_display(struct linedisp *linedisp, const char *msg,
|
||||
ssize_t count)
|
||||
{
|
||||
char *new_msg;
|
||||
|
||||
/* stop the scroll timer */
|
||||
del_timer_sync(&linedisp->timer);
|
||||
|
||||
if (count == -1)
|
||||
count = strlen(msg);
|
||||
|
||||
/* if the string ends with a newline, trim it */
|
||||
if (msg[count - 1] == '\n')
|
||||
count--;
|
||||
|
||||
if (!count) {
|
||||
/* Clear the display */
|
||||
kfree(linedisp->message);
|
||||
linedisp->message = NULL;
|
||||
linedisp->message_len = 0;
|
||||
memset(linedisp->buf, ' ', linedisp->num_chars);
|
||||
linedisp->update(linedisp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_msg = kmemdup_nul(msg, count, GFP_KERNEL);
|
||||
if (!new_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
kfree(linedisp->message);
|
||||
|
||||
linedisp->message = new_msg;
|
||||
linedisp->message_len = count;
|
||||
linedisp->scroll_pos = 0;
|
||||
|
||||
/* update the display */
|
||||
linedisp_scroll(&linedisp->timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* message_show() - read message via sysfs
|
||||
* @dev: the display device
|
||||
* @attr: the display message attribute
|
||||
* @buf: the buffer to read the message into
|
||||
*
|
||||
* Read the current message being displayed or scrolled across the display into
|
||||
* @buf, for reads from sysfs.
|
||||
*
|
||||
* Return: the number of characters written to @buf
|
||||
*/
|
||||
static ssize_t message_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", linedisp->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* message_store() - write a new message via sysfs
|
||||
* @dev: the display device
|
||||
* @attr: the display message attribute
|
||||
* @buf: the buffer containing the new message
|
||||
* @count: the size of the message in @buf
|
||||
*
|
||||
* Write a new message to display or scroll across the display from sysfs.
|
||||
*
|
||||
* Return: the size of the message on success, else -ERRNO
|
||||
*/
|
||||
static ssize_t message_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
int err;
|
||||
|
||||
err = linedisp_display(linedisp, buf, count);
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(message);
|
||||
|
||||
static ssize_t scroll_step_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
|
||||
}
|
||||
|
||||
static ssize_t scroll_step_ms_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
unsigned int ms;
|
||||
|
||||
if (kstrtouint(buf, 10, &ms) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
linedisp->scroll_rate = msecs_to_jiffies(ms);
|
||||
if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
|
||||
del_timer_sync(&linedisp->timer);
|
||||
if (linedisp->scroll_rate)
|
||||
linedisp_scroll(&linedisp->timer);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(scroll_step_ms);
|
||||
|
||||
static struct attribute *linedisp_attrs[] = {
|
||||
&dev_attr_message.attr,
|
||||
&dev_attr_scroll_step_ms.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(linedisp);
|
||||
|
||||
static const struct device_type linedisp_type = {
|
||||
.groups = linedisp_groups,
|
||||
};
|
||||
|
||||
/**
|
||||
* linedisp_register - register a character line display
|
||||
* @linedisp: pointer to character line display structure
|
||||
* @parent: parent device
|
||||
* @num_chars: the number of characters that can be displayed
|
||||
* @buf: pointer to a buffer that can hold @num_chars characters
|
||||
* @update: Function called to update the display. This must not sleep!
|
||||
*
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
||||
unsigned int num_chars, char *buf,
|
||||
void (*update)(struct linedisp *linedisp))
|
||||
{
|
||||
static atomic_t linedisp_id = ATOMIC_INIT(-1);
|
||||
int err;
|
||||
|
||||
memset(linedisp, 0, sizeof(*linedisp));
|
||||
linedisp->dev.parent = parent;
|
||||
linedisp->dev.type = &linedisp_type;
|
||||
linedisp->update = update;
|
||||
linedisp->buf = buf;
|
||||
linedisp->num_chars = num_chars;
|
||||
linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
|
||||
|
||||
device_initialize(&linedisp->dev);
|
||||
dev_set_name(&linedisp->dev, "linedisp.%lu",
|
||||
(unsigned long)atomic_inc_return(&linedisp_id));
|
||||
|
||||
/* initialise a timer for scrolling the message */
|
||||
timer_setup(&linedisp->timer, linedisp_scroll, 0);
|
||||
|
||||
err = device_add(&linedisp->dev);
|
||||
if (err)
|
||||
goto out_del_timer;
|
||||
|
||||
/* display a default message */
|
||||
err = linedisp_display(linedisp, "Linux " UTS_RELEASE " ", -1);
|
||||
if (err)
|
||||
goto out_del_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_dev:
|
||||
device_del(&linedisp->dev);
|
||||
out_del_timer:
|
||||
del_timer_sync(&linedisp->timer);
|
||||
put_device(&linedisp->dev);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(linedisp_register);
|
||||
|
||||
/**
|
||||
* linedisp_unregister - unregister a character line display
|
||||
* @linedisp: pointer to character line display structure registered previously
|
||||
* with linedisp_register()
|
||||
*/
|
||||
void linedisp_unregister(struct linedisp *linedisp)
|
||||
{
|
||||
device_del(&linedisp->dev);
|
||||
del_timer_sync(&linedisp->timer);
|
||||
kfree(linedisp->message);
|
||||
put_device(&linedisp->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(linedisp_unregister);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
43
drivers/auxdisplay/line-display.h
Normal file
43
drivers/auxdisplay/line-display.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Character line display core support
|
||||
*
|
||||
* Copyright (C) 2016 Imagination Technologies
|
||||
* Author: Paul Burton <paul.burton@mips.com>
|
||||
*
|
||||
* Copyright (C) 2021 Glider bv
|
||||
*/
|
||||
|
||||
#ifndef _LINEDISP_H
|
||||
#define _LINEDISP_H
|
||||
|
||||
/**
|
||||
* struct linedisp - character line display private data structure
|
||||
* @dev: the line display device
|
||||
* @timer: timer used to implement scrolling
|
||||
* @update: function called to update the display
|
||||
* @buf: pointer to the buffer for the string currently displayed
|
||||
* @message: the full message to display or scroll on the display
|
||||
* @num_chars: the number of characters that can be displayed
|
||||
* @message_len: the length of the @message string
|
||||
* @scroll_pos: index of the first character of @message currently displayed
|
||||
* @scroll_rate: scroll interval in jiffies
|
||||
*/
|
||||
struct linedisp {
|
||||
struct device dev;
|
||||
struct timer_list timer;
|
||||
void (*update)(struct linedisp *linedisp);
|
||||
char *buf;
|
||||
char *message;
|
||||
unsigned int num_chars;
|
||||
unsigned int message_len;
|
||||
unsigned int scroll_pos;
|
||||
unsigned int scroll_rate;
|
||||
};
|
||||
|
||||
int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
||||
unsigned int num_chars, char *buf,
|
||||
void (*update)(struct linedisp *linedisp));
|
||||
void linedisp_unregister(struct linedisp *linedisp);
|
||||
|
||||
#endif /* LINEDISP_H */
|
241
include/uapi/linux/map_to_14segment.h
Normal file
241
include/uapi/linux/map_to_14segment.h
Normal file
@ -0,0 +1,241 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (C) 2021 Glider bv
|
||||
*
|
||||
* Based on include/uapi/linux/map_to_7segment.h:
|
||||
|
||||
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef MAP_TO_14SEGMENT_H
|
||||
#define MAP_TO_14SEGMENT_H
|
||||
|
||||
/* This file provides translation primitives and tables for the conversion
|
||||
* of (ASCII) characters to a 14-segments notation.
|
||||
*
|
||||
* The 14 segment's wikipedia notation below is used as standard.
|
||||
* See: https://en.wikipedia.org/wiki/Fourteen-segment_display
|
||||
*
|
||||
* Notation: +---a---+
|
||||
* |\ | /|
|
||||
* f h i j b
|
||||
* | \|/ |
|
||||
* +-g1+-g2+
|
||||
* | /|\ |
|
||||
* e k l m c
|
||||
* |/ | \|
|
||||
* +---d---+
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* Register a map variable, and fill it with a character set:
|
||||
* static SEG14_DEFAULT_MAP(map_seg14);
|
||||
*
|
||||
*
|
||||
* Then use for conversion:
|
||||
* seg14 = map_to_seg14(&map_seg14, some_char);
|
||||
* ...
|
||||
*
|
||||
* In device drivers it is recommended, if required, to make the char map
|
||||
* accessible via the sysfs interface using the following scheme:
|
||||
*
|
||||
* static ssize_t map_seg14_show(struct device *dev,
|
||||
* struct device_attribute *attr, char *buf)
|
||||
* {
|
||||
* memcpy(buf, &map_seg14, sizeof(map_seg14));
|
||||
* return sizeof(map_seg14);
|
||||
* }
|
||||
* static ssize_t map_seg14_store(struct device *dev,
|
||||
* struct device_attribute *attr,
|
||||
* const char *buf, size_t cnt)
|
||||
* {
|
||||
* if (cnt != sizeof(map_seg14))
|
||||
* return -EINVAL;
|
||||
* memcpy(&map_seg14, buf, cnt);
|
||||
* return cnt;
|
||||
* }
|
||||
* static DEVICE_ATTR_RW(map_seg14);
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#define BIT_SEG14_A 0
|
||||
#define BIT_SEG14_B 1
|
||||
#define BIT_SEG14_C 2
|
||||
#define BIT_SEG14_D 3
|
||||
#define BIT_SEG14_E 4
|
||||
#define BIT_SEG14_F 5
|
||||
#define BIT_SEG14_G1 6
|
||||
#define BIT_SEG14_G2 7
|
||||
#define BIT_SEG14_H 8
|
||||
#define BIT_SEG14_I 9
|
||||
#define BIT_SEG14_J 10
|
||||
#define BIT_SEG14_K 11
|
||||
#define BIT_SEG14_L 12
|
||||
#define BIT_SEG14_M 13
|
||||
#define BIT_SEG14_RESERVED1 14
|
||||
#define BIT_SEG14_RESERVED2 15
|
||||
|
||||
struct seg14_conversion_map {
|
||||
__be16 table[128];
|
||||
};
|
||||
|
||||
static __inline__ int map_to_seg14(struct seg14_conversion_map *map, int c)
|
||||
{
|
||||
if (c < 0 || c >= sizeof(map->table) / sizeof(map->table[0]))
|
||||
return -EINVAL;
|
||||
|
||||
return __be16_to_cpu(map->table[c]);
|
||||
}
|
||||
|
||||
#define SEG14_CONVERSION_MAP(_name, _map) \
|
||||
struct seg14_conversion_map _name = { .table = { _map } }
|
||||
|
||||
/*
|
||||
* It is recommended to use a facility that allows user space to redefine
|
||||
* custom character sets for LCD devices. Please use a sysfs interface
|
||||
* as described above.
|
||||
*/
|
||||
#define MAP_TO_SEG14_SYSFS_FILE "map_seg14"
|
||||
|
||||
/*******************************************************************************
|
||||
* ASCII conversion table
|
||||
******************************************************************************/
|
||||
|
||||
#define _SEG14(sym, a, b, c, d, e, f, g1, g2, h, j, k, l, m, n) \
|
||||
__cpu_to_be16( a << BIT_SEG14_A | b << BIT_SEG14_B | \
|
||||
c << BIT_SEG14_C | d << BIT_SEG14_D | \
|
||||
e << BIT_SEG14_E | f << BIT_SEG14_F | \
|
||||
g1 << BIT_SEG14_G1 | g2 << BIT_SEG14_G2 | \
|
||||
h << BIT_SEG14_H | j << BIT_SEG14_I | \
|
||||
k << BIT_SEG14_J | l << BIT_SEG14_K | \
|
||||
m << BIT_SEG14_L | n << BIT_SEG14_M )
|
||||
|
||||
#define _MAP_0_32_ASCII_SEG14_NON_PRINTABLE \
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
|
||||
#define _MAP_33_47_ASCII_SEG14_SYMBOL \
|
||||
_SEG14('!', 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('"', 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0), \
|
||||
_SEG14('#', 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('$', 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('%', 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0), \
|
||||
_SEG14('&', 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1), \
|
||||
_SEG14('\'',0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0), \
|
||||
_SEG14('(', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1), \
|
||||
_SEG14(')', 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0), \
|
||||
_SEG14('*', 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1), \
|
||||
_SEG14('+', 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14(',', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), \
|
||||
_SEG14('-', 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0),
|
||||
|
||||
#define _MAP_48_57_ASCII_SEG14_NUMERIC \
|
||||
_SEG14('0', 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0), \
|
||||
_SEG14('1', 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0), \
|
||||
_SEG14('2', 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('3', 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('4', 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('5', 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('6', 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('7', 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0), \
|
||||
_SEG14('8', 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('9', 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0),
|
||||
|
||||
#define _MAP_58_64_ASCII_SEG14_SYMBOL \
|
||||
_SEG14(':', 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14(';', 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0), \
|
||||
_SEG14('<', 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1), \
|
||||
_SEG14('=', 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('>', 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0), \
|
||||
_SEG14('?', 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0), \
|
||||
_SEG14('@', 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0),
|
||||
|
||||
#define _MAP_65_90_ASCII_SEG14_ALPHA_UPPER \
|
||||
_SEG14('A', 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('B', 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('C', 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('D', 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('E', 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('F', 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('G', 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('H', 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('I', 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('J', 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('K', 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1), \
|
||||
_SEG14('L', 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('M', 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0), \
|
||||
_SEG14('N', 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1), \
|
||||
_SEG14('O', 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('P', 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('Q', 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('R', 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('S', 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('T', 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('U', 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('V', 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0), \
|
||||
_SEG14('W', 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1), \
|
||||
_SEG14('X', 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1), \
|
||||
_SEG14('Y', 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0), \
|
||||
_SEG14('Z', 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0),
|
||||
|
||||
#define _MAP_91_96_ASCII_SEG14_SYMBOL \
|
||||
_SEG14('[', 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('\\',0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), \
|
||||
_SEG14(']', 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1), \
|
||||
_SEG14('_', 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('`', 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0),
|
||||
|
||||
#define _MAP_97_122_ASCII_SEG14_ALPHA_LOWER \
|
||||
_SEG14('a', 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0), \
|
||||
_SEG14('b', 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('c', 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('d', 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0), \
|
||||
_SEG14('e', 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0), \
|
||||
_SEG14('f', 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0), \
|
||||
_SEG14('g', 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0), \
|
||||
_SEG14('h', 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0), \
|
||||
_SEG14('i', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0), \
|
||||
_SEG14('j', 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0), \
|
||||
_SEG14('k', 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1), \
|
||||
_SEG14('l', 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('m', 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0), \
|
||||
_SEG14('n', 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0), \
|
||||
_SEG14('o', 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('p', 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0), \
|
||||
_SEG14('q', 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0), \
|
||||
_SEG14('r', 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('s', 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1), \
|
||||
_SEG14('t', 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('u', 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0), \
|
||||
_SEG14('v', 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0), \
|
||||
_SEG14('w', 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1), \
|
||||
_SEG14('x', 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1), \
|
||||
_SEG14('y', 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0), \
|
||||
_SEG14('z', 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0),
|
||||
|
||||
#define _MAP_123_126_ASCII_SEG14_SYMBOL \
|
||||
_SEG14('{', 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0), \
|
||||
_SEG14('|', 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0), \
|
||||
_SEG14('}', 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1), \
|
||||
_SEG14('~', 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0),
|
||||
|
||||
/* Maps */
|
||||
#define MAP_ASCII14SEG_ALPHANUM \
|
||||
_MAP_0_32_ASCII_SEG14_NON_PRINTABLE \
|
||||
_MAP_33_47_ASCII_SEG14_SYMBOL \
|
||||
_MAP_48_57_ASCII_SEG14_NUMERIC \
|
||||
_MAP_58_64_ASCII_SEG14_SYMBOL \
|
||||
_MAP_65_90_ASCII_SEG14_ALPHA_UPPER \
|
||||
_MAP_91_96_ASCII_SEG14_SYMBOL \
|
||||
_MAP_97_122_ASCII_SEG14_ALPHA_LOWER \
|
||||
_MAP_123_126_ASCII_SEG14_SYMBOL
|
||||
|
||||
#define SEG14_DEFAULT_MAP(_name) \
|
||||
SEG14_CONVERSION_MAP(_name, MAP_ASCII14SEG_ALPHANUM)
|
||||
|
||||
#endif /* MAP_TO_14SEGMENT_H */
|
Loading…
Reference in New Issue
Block a user