Commit e582e08e authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents e54ffb96 97fbb29f
......@@ -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;
......
This diff is collapsed.
......@@ -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;
/* initialise a timer for scrolling the message */
timer_setup(&ctx->timer, img_ascii_lcd_scroll, 0);
platform_set_drvdata(pdev, ctx);
/* display a default message */
err = img_ascii_lcd_display(ctx, "Linux " UTS_RELEASE " ", -1);
err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
cfg->update);
if (err)
goto out_del_timer;
return err;
err = device_create_file(&pdev->dev, &dev_attr_message);
/* for backwards compatibility */
err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj,
&ctx->linedisp.dev.kobj,
"message", NULL);
if (err)
goto out_del_timer;
goto err_unregister;
platform_set_drvdata(pdev, ctx);
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"
......
// 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");
/* 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 */
/* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment