mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-06 05:13:18 +00:00
auxdisplay for v6.9-1
* New driver for GPIO based 7-segment LED display (Chris Packham) * New driver for Maxim MAX6958/6959 I²C 7-segment LED display controller * Refactor linedisp library to make the above happen * Update Holtek HT16k33 driver to follow the linedisp refactoring * Convert .remove to return void in platform drivers (Uwe Kleine-König) * Fix DT schemas (Krzysztof Kozlowski) * Refresh MAINTAINERS database The following is an automated git shortlog grouped by driver: Add 7-segment LED display drivers: - Add 7-segment LED display driver - Add driver for MAX695x 7-segment LED controllers - Add 7 and 14 segment mappings to MAINTAINERS cfag12864bfb: - Convert to platform remove callback returning void dt-bindings: - auxdisplay: Add bindings for generic 7-segment LED - auxdisplay: Add Maxim MAX6958/6959 - auxdisplay: hit,hd44780: use defines for GPIO flags - auxdisplay: adjust example indentation and use generic node names hd44780: - Convert to platform remove callback returning void ht16k33: - Drop struct ht16k33_seg - Switch to use line display character mapping - Define a few helper macros - Move ht16k33_linedisp_ops down - Add default to switch-cases img-ascii-lcd: - Convert to platform remove callback returning void - Make container_of() no-op for struct linedisp linedisp: - Allocate buffer for the string - Add support for overriding character mapping - Provide struct linedisp_ops for future extension - Move exported symbols to a namespace - Add missing header(s) - Unshadow error codes in ->store() - Use unique number for id - Free allocated resources in ->release() panel: - Switch to use module_parport_driver() seg-led-gpio: - Import linedisp namespace -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAmXxtUoACgkQb7wzTHR8 rCiyIg//TuyU7/1xqPW2Jri+jWDw3hdFtizE2TB6NjQhn5eS5uPjh3oFVgMPQv0x IwhALXzdG9V5s946er0APC1B7b7TppGuQf1bP/mDPuYzy+YqehEO6rSzrcHP2w6T Uj7l5ShjkUVJD60ZvognJxJKGcTEffzFTIAP8lqlLzVZ81I79rzfjSmvFnY25mbW Wv+m8fBHQTKR7cH8GZFNlhMOmJ2WmmeS0ba9GU09lXKMtvB/89yQinOi/JH5qntk kUWmD0M7rLopkKyYJhnlnuBzbCs8ZAwq8ogdrTST3g3V2PO36sxRHerC9Ve1six/ YarW+z+g/qNm/lbI/iBeNkpKczFENTXJEi5+cXseBcEPl5pV/xRjmI/RqTy6bMe+ 03rCDgrEzxFHZz7e1D88UMLN5cJoaQVkGeUpHHVS1fkAZ6euEXjFhOzS1GZ5B67H 6oaE/PJLVi0Hs0XDHKYsnhF+l6tkWrMyx2w9XykiXzCY2hkRnofPmNE8D3aOvoYQ kvl7hOBqcNbWYS73d/eUGCmISSajHcwiPPxysmw4wtKrrbe6VGbehLWG8neS/Dri Y9kpu0t86Ue7qLBcMHvW/U7lqcdKreqc5LFBCdmxa3SaEVmDb1m/RE+jwANGcYFQ fJHPmzn1zn9TPstFU2TAQU0JLu3f82kh4evKFPKt7ugwRBKCrnU= =Q7mz -----END PGP SIGNATURE----- Merge tag 'auxdisplay-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-auxdisplay Pull auxdisplay updates from Andy Shevchenko: - New driver for GPIO based 7-segment LED display (Chris Packham) - New driver for Maxim MAX6958/6959 I²C 7-segment LED display controller - Refactor linedisp library to make the above happen - Update Holtek HT16k33 driver to follow the linedisp refactoring - Convert .remove to return void in platform drivers (Uwe Kleine-König) - Fix DT schemas (Krzysztof Kozlowski) - Refresh MAINTAINERS database * tag 'auxdisplay-v6.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-auxdisplay: (27 commits) auxdisplay: img-ascii-lcd: Convert to platform remove callback returning void auxdisplay: hd44780: Convert to platform remove callback returning void auxdisplay: cfag12864bfb: Convert to platform remove callback returning void auxdisplay: seg-led-gpio: Import linedisp namespace dt-bindings: auxdisplay: Add bindings for generic 7-segment LED auxdisplay: Add 7-segment LED display driver auxdisplay: Add driver for MAX695x 7-segment LED controllers dt-bindings: auxdisplay: Add Maxim MAX6958/6959 auxdisplay: ht16k33: Drop struct ht16k33_seg auxdisplay: ht16k33: Switch to use line display character mapping auxdisplay: ht16k33: Define a few helper macros auxdisplay: ht16k33: Move ht16k33_linedisp_ops down auxdisplay: ht16k33: Add default to switch-cases auxdisplay: linedisp: Allocate buffer for the string auxdisplay: linedisp: Add support for overriding character mapping auxdisplay: linedisp: Provide struct linedisp_ops for future extension auxdisplay: linedisp: Move exported symbols to a namespace auxdisplay: linedisp: Add missing header(s) auxdisplay: linedisp: Unshadow error codes in ->store() auxdisplay: linedisp: Use unique number for id ...
This commit is contained in:
commit
b345ff698e
@ -39,6 +39,6 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
lcd@10008000 {
|
||||
compatible = "arm,versatile-lcd";
|
||||
reg = <0x10008000 0x1000>;
|
||||
compatible = "arm,versatile-lcd";
|
||||
reg = <0x10008000 0x1000>;
|
||||
};
|
||||
|
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/auxdisplay/gpio-7-segment.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: GPIO based LED segment display
|
||||
|
||||
maintainers:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: gpio-7-segment
|
||||
|
||||
segment-gpios:
|
||||
description: |
|
||||
An array of GPIOs one per segment. The first GPIO corresponds to the A
|
||||
segment, the seventh GPIO corresponds to the G segment. Some LED blocks
|
||||
also have a decimal point which can be specified as an optional eighth
|
||||
segment.
|
||||
|
||||
-a-
|
||||
| |
|
||||
f b
|
||||
| |
|
||||
-g-
|
||||
| |
|
||||
e c
|
||||
| |
|
||||
-d- dp
|
||||
|
||||
minItems: 7
|
||||
maxItems: 8
|
||||
|
||||
required:
|
||||
- segment-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
led-7seg {
|
||||
compatible = "gpio-7-segment";
|
||||
segment-gpios = <&gpio 0 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 1 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 2 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 3 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 4 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 5 GPIO_ACTIVE_LOW>,
|
||||
<&gpio 6 GPIO_ACTIVE_LOW>;
|
||||
};
|
@ -84,42 +84,44 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
auxdisplay {
|
||||
compatible = "hit,hd44780";
|
||||
display-controller {
|
||||
compatible = "hit,hd44780";
|
||||
|
||||
data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 1 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 2 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 3 GPIO_ACTIVE_HIGH>;
|
||||
enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>;
|
||||
rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>;
|
||||
data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 1 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 2 GPIO_ACTIVE_HIGH>,
|
||||
<&hc595 3 GPIO_ACTIVE_HIGH>;
|
||||
enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>;
|
||||
rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
display-height-chars = <2>;
|
||||
display-width-chars = <16>;
|
||||
display-height-chars = <2>;
|
||||
display-width-chars = <16>;
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pcf8574: pcf8574@27 {
|
||||
compatible = "nxp,pcf8574";
|
||||
reg = <0x27>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
pcf8574: gpio-expander@27 {
|
||||
compatible = "nxp,pcf8574";
|
||||
reg = <0x27>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
||||
hd44780 {
|
||||
compatible = "hit,hd44780";
|
||||
display-height-chars = <2>;
|
||||
display-width-chars = <16>;
|
||||
data-gpios = <&pcf8574 4 0>,
|
||||
<&pcf8574 5 0>,
|
||||
<&pcf8574 6 0>,
|
||||
<&pcf8574 7 0>;
|
||||
enable-gpios = <&pcf8574 2 0>;
|
||||
rs-gpios = <&pcf8574 0 0>;
|
||||
rw-gpios = <&pcf8574 1 0>;
|
||||
backlight-gpios = <&pcf8574 3 0>;
|
||||
|
||||
display-controller {
|
||||
compatible = "hit,hd44780";
|
||||
display-height-chars = <2>;
|
||||
display-width-chars = <16>;
|
||||
data-gpios = <&pcf8574 4 GPIO_ACTIVE_HIGH>,
|
||||
<&pcf8574 5 GPIO_ACTIVE_HIGH>,
|
||||
<&pcf8574 6 GPIO_ACTIVE_HIGH>,
|
||||
<&pcf8574 7 GPIO_ACTIVE_HIGH>;
|
||||
enable-gpios = <&pcf8574 2 GPIO_ACTIVE_HIGH>;
|
||||
rs-gpios = <&pcf8574 0 GPIO_ACTIVE_HIGH>;
|
||||
rw-gpios = <&pcf8574 1 GPIO_ACTIVE_HIGH>;
|
||||
backlight-gpios = <&pcf8574 3 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
@ -74,31 +74,31 @@ examples:
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ht16k33: ht16k33@70 {
|
||||
compatible = "holtek,ht16k33";
|
||||
reg = <0x70>;
|
||||
refresh-rate-hz = <20>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
|
||||
debounce-delay-ms = <50>;
|
||||
linux,keymap = <MATRIX_KEY(2, 0, KEY_F6)>,
|
||||
<MATRIX_KEY(3, 0, KEY_F8)>,
|
||||
<MATRIX_KEY(4, 0, KEY_F10)>,
|
||||
<MATRIX_KEY(5, 0, KEY_F4)>,
|
||||
<MATRIX_KEY(6, 0, KEY_F2)>,
|
||||
<MATRIX_KEY(2, 1, KEY_F5)>,
|
||||
<MATRIX_KEY(3, 1, KEY_F7)>,
|
||||
<MATRIX_KEY(4, 1, KEY_F9)>,
|
||||
<MATRIX_KEY(5, 1, KEY_F3)>,
|
||||
<MATRIX_KEY(6, 1, KEY_F1)>;
|
||||
display-controller@70 {
|
||||
compatible = "holtek,ht16k33";
|
||||
reg = <0x70>;
|
||||
refresh-rate-hz = <20>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>;
|
||||
debounce-delay-ms = <50>;
|
||||
linux,keymap = <MATRIX_KEY(2, 0, KEY_F6)>,
|
||||
<MATRIX_KEY(3, 0, KEY_F8)>,
|
||||
<MATRIX_KEY(4, 0, KEY_F10)>,
|
||||
<MATRIX_KEY(5, 0, KEY_F4)>,
|
||||
<MATRIX_KEY(6, 0, KEY_F2)>,
|
||||
<MATRIX_KEY(2, 1, KEY_F5)>,
|
||||
<MATRIX_KEY(3, 1, KEY_F7)>,
|
||||
<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";
|
||||
};
|
||||
led {
|
||||
color = <LED_COLOR_ID_RED>;
|
||||
function = LED_FUNCTION_BACKLIGHT;
|
||||
linux,default-trigger = "backlight";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -50,6 +50,6 @@ additionalProperties: false
|
||||
examples:
|
||||
- |
|
||||
lcd: lcd@17fff000 {
|
||||
compatible = "img,boston-lcd";
|
||||
reg = <0x17fff000 0x8>;
|
||||
compatible = "img,boston-lcd";
|
||||
reg = <0x17fff000 0x8>;
|
||||
};
|
||||
|
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/auxdisplay/maxim,max6959.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MAX6958/6959 7-segment LED display controller
|
||||
|
||||
maintainers:
|
||||
- Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
|
||||
description:
|
||||
The Maxim MAX6958/6959 7-segment LED display controller provides
|
||||
an I2C interface to up to four 7-segment LED digits. The MAX6959,
|
||||
in comparison to MAX6958, adds input support. Type of the chip can
|
||||
be autodetected via specific register read, and hence the features
|
||||
may be enabled in the driver at run-time, in case they are requested
|
||||
via Device Tree. A given hardware is simple and does not provide
|
||||
any additional pins, such as reset or power enable.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: maxim,max6959
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
display-controller@38 {
|
||||
compatible = "maxim,max6959";
|
||||
reg = <0x38>;
|
||||
};
|
||||
};
|
@ -3389,11 +3389,15 @@ F: drivers/base/auxiliary.c
|
||||
F: include/linux/auxiliary_bus.h
|
||||
|
||||
AUXILIARY DISPLAY DRIVERS
|
||||
M: Miguel Ojeda <ojeda@kernel.org>
|
||||
S: Maintained
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
R: Geert Uytterhoeven <geert@linux-m68k.org>
|
||||
S: Odd Fixes
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-auxdisplay.git
|
||||
F: Documentation/devicetree/bindings/auxdisplay/
|
||||
F: drivers/auxdisplay/
|
||||
F: include/linux/cfag12864b.h
|
||||
F: include/uapi/linux/map_to_14segment.h
|
||||
F: include/uapi/linux/map_to_7segment.h
|
||||
|
||||
AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
|
@ -177,6 +177,20 @@ config HT16K33
|
||||
Say yes here to add support for Holtek HT16K33, RAM mapping 16*8
|
||||
LED controller driver with keyscan.
|
||||
|
||||
config MAX6959
|
||||
tristate "Maxim MAX6958/6959 7-segment LED controller"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select LINEDISP
|
||||
help
|
||||
If you say yes here you get support for the following Maxim chips
|
||||
(I2C 7-segment LED display controller):
|
||||
- MAX6958
|
||||
- MAX6959 (input support)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max6959.
|
||||
|
||||
config LCD2S
|
||||
tristate "lcd2s 20x4 character display over I2C console"
|
||||
depends on I2C
|
||||
@ -197,6 +211,17 @@ config ARM_CHARLCD
|
||||
line and the Linux version on the second line, but that's
|
||||
still useful.
|
||||
|
||||
config SEG_LED_GPIO
|
||||
tristate "Generic 7-segment LED display"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select LINEDISP
|
||||
help
|
||||
This driver supports a generic 7-segment LED display made up
|
||||
of GPIO pins connected to the individual segments.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called seg-led-gpio.
|
||||
|
||||
menuconfig PARPORT_PANEL
|
||||
tristate "Parallel port LCD/Keypad Panel support"
|
||||
depends on PARPORT
|
||||
|
@ -14,3 +14,5 @@ obj-$(CONFIG_HT16K33) += ht16k33.o
|
||||
obj-$(CONFIG_PARPORT_PANEL) += panel.o
|
||||
obj-$(CONFIG_LCD2S) += lcd2s.o
|
||||
obj-$(CONFIG_LINEDISP) += line-display.o
|
||||
obj-$(CONFIG_MAX6959) += max6959.o
|
||||
obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
|
||||
|
@ -96,7 +96,7 @@ static int cfag12864bfb_probe(struct platform_device *device)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cfag12864bfb_remove(struct platform_device *device)
|
||||
static void cfag12864bfb_remove(struct platform_device *device)
|
||||
{
|
||||
struct fb_info *info = platform_get_drvdata(device);
|
||||
|
||||
@ -104,13 +104,11 @@ static int cfag12864bfb_remove(struct platform_device *device)
|
||||
unregister_framebuffer(info);
|
||||
framebuffer_release(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cfag12864bfb_driver = {
|
||||
.probe = cfag12864bfb_probe,
|
||||
.remove = cfag12864bfb_remove,
|
||||
.remove_new = cfag12864bfb_remove,
|
||||
.driver = {
|
||||
.name = CFAG12864BFB_NAME,
|
||||
},
|
||||
|
@ -319,7 +319,7 @@ static int hd44780_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hd44780_remove(struct platform_device *pdev)
|
||||
static void hd44780_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct charlcd *lcd = platform_get_drvdata(pdev);
|
||||
struct hd44780_common *hdc = lcd->drvdata;
|
||||
@ -329,7 +329,6 @@ static int hd44780_remove(struct platform_device *pdev)
|
||||
kfree(lcd->drvdata);
|
||||
|
||||
kfree(lcd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id hd44780_of_match[] = {
|
||||
@ -340,7 +339,7 @@ MODULE_DEVICE_TABLE(of, hd44780_of_match);
|
||||
|
||||
static struct platform_driver hd44780_driver = {
|
||||
.probe = hd44780_probe,
|
||||
.remove = hd44780_remove,
|
||||
.remove_new = hd44780_remove,
|
||||
.driver = {
|
||||
.name = "hd44780",
|
||||
.of_match_table = hd44780_of_match,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/leds.h>
|
||||
@ -85,16 +86,6 @@ struct ht16k33_fbdev {
|
||||
uint8_t *cache;
|
||||
};
|
||||
|
||||
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;
|
||||
@ -102,12 +93,21 @@ struct ht16k33_priv {
|
||||
struct ht16k33_keypad keypad;
|
||||
union {
|
||||
struct ht16k33_fbdev fbdev;
|
||||
struct ht16k33_seg seg;
|
||||
struct linedisp linedisp;
|
||||
};
|
||||
enum display_type type;
|
||||
uint8_t blink;
|
||||
};
|
||||
|
||||
#define ht16k33_work_to_priv(p) \
|
||||
container_of(p, struct ht16k33_priv, work.work)
|
||||
|
||||
#define ht16k33_led_to_priv(p) \
|
||||
container_of(p, struct ht16k33_priv, led)
|
||||
|
||||
#define ht16k33_linedisp_to_priv(p) \
|
||||
container_of(p, struct ht16k33_priv, linedisp)
|
||||
|
||||
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
|
||||
.id = DRIVER_NAME,
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
@ -135,33 +135,6 @@ 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 | priv->blink;
|
||||
@ -195,8 +168,7 @@ static int ht16k33_brightness_set(struct ht16k33_priv *priv,
|
||||
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);
|
||||
struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);
|
||||
|
||||
return ht16k33_brightness_set(priv, brightness);
|
||||
}
|
||||
@ -204,8 +176,7 @@ static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,
|
||||
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);
|
||||
struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);
|
||||
unsigned int delay;
|
||||
uint8_t blink;
|
||||
int err;
|
||||
@ -247,8 +218,7 @@ static void ht16k33_fb_queue(struct ht16k33_priv *priv)
|
||||
*/
|
||||
static void ht16k33_fb_update(struct work_struct *work)
|
||||
{
|
||||
struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv,
|
||||
work.work);
|
||||
struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
|
||||
struct ht16k33_fbdev *fbdev = &priv->fbdev;
|
||||
|
||||
uint8_t *p1, *p2;
|
||||
@ -440,51 +410,71 @@ 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;
|
||||
struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
|
||||
struct linedisp_map *map = priv->linedisp.map;
|
||||
char *s = priv->linedisp.buf;
|
||||
uint8_t buf[9];
|
||||
|
||||
buf[0] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[0] = map_to_seg7(&map->map.seg7, *s++);
|
||||
buf[1] = 0;
|
||||
buf[2] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[2] = map_to_seg7(&map->map.seg7, *s++);
|
||||
buf[3] = 0;
|
||||
buf[4] = 0;
|
||||
buf[5] = 0;
|
||||
buf[6] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[6] = map_to_seg7(&map->map.seg7, *s++);
|
||||
buf[7] = 0;
|
||||
buf[8] = map_to_seg7(&seg->map.seg7, *s++);
|
||||
buf[8] = map_to_seg7(&map->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;
|
||||
struct ht16k33_priv *priv = ht16k33_work_to_priv(work);
|
||||
struct linedisp_map *map = priv->linedisp.map;
|
||||
char *s = priv->linedisp.buf;
|
||||
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);
|
||||
put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 0);
|
||||
put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 2);
|
||||
put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 4);
|
||||
put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 6);
|
||||
|
||||
i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);
|
||||
}
|
||||
|
||||
static int ht16k33_linedisp_get_map_type(struct linedisp *linedisp)
|
||||
{
|
||||
struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);
|
||||
|
||||
switch (priv->type) {
|
||||
case DISP_QUAD_7SEG:
|
||||
INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update);
|
||||
return LINEDISP_MAP_SEG7;
|
||||
|
||||
case DISP_QUAD_14SEG:
|
||||
INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update);
|
||||
return LINEDISP_MAP_SEG14;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ht16k33_linedisp_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);
|
||||
|
||||
schedule_delayed_work(&priv->work, 0);
|
||||
}
|
||||
|
||||
static const struct linedisp_ops ht16k33_linedisp_ops = {
|
||||
.get_map_type = ht16k33_linedisp_get_map_type,
|
||||
.update = ht16k33_linedisp_update,
|
||||
};
|
||||
|
||||
static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,
|
||||
unsigned int brightness)
|
||||
{
|
||||
@ -666,47 +656,14 @@ static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,
|
||||
static int ht16k33_seg_probe(struct device *dev, struct ht16k33_priv *priv,
|
||||
uint32_t brightness)
|
||||
{
|
||||
struct ht16k33_seg *seg = &priv->seg;
|
||||
struct linedisp *linedisp = &priv->linedisp;
|
||||
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;
|
||||
return linedisp_register(linedisp, dev, 4, &ht16k33_linedisp_ops);
|
||||
}
|
||||
|
||||
static int ht16k33_probe(struct i2c_client *client)
|
||||
@ -770,6 +727,9 @@ static int ht16k33_probe(struct i2c_client *client)
|
||||
/* Segment Display */
|
||||
err = ht16k33_seg_probe(dev, priv, dft_brightness);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@ -790,9 +750,10 @@ static void ht16k33_remove(struct i2c_client *client)
|
||||
|
||||
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);
|
||||
linedisp_unregister(&priv->linedisp);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -831,4 +792,5 @@ module_i2c_driver(ht16k33_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Holtek HT16K33 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(LINEDISP);
|
||||
MODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");
|
||||
|
@ -22,32 +22,30 @@ struct img_ascii_lcd_ctx;
|
||||
* struct img_ascii_lcd_config - Configuration information about an LCD model
|
||||
* @num_chars: the number of characters the LCD can display
|
||||
* @external_regmap: true if registers are in a system controller, else false
|
||||
* @update: function called to update the LCD
|
||||
* @ops: character line display operations
|
||||
*/
|
||||
struct img_ascii_lcd_config {
|
||||
unsigned int num_chars;
|
||||
bool external_regmap;
|
||||
void (*update)(struct linedisp *linedisp);
|
||||
const struct linedisp_ops ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct img_ascii_lcd_ctx - Private data structure
|
||||
* @linedisp: line display structure
|
||||
* @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
|
||||
* @linedisp: line display structure
|
||||
* @curr: the string currently displayed on the LCD
|
||||
*/
|
||||
struct img_ascii_lcd_ctx {
|
||||
struct linedisp linedisp;
|
||||
union {
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
u32 offset;
|
||||
const struct img_ascii_lcd_config *cfg;
|
||||
struct linedisp linedisp;
|
||||
char curr[] __aligned(8);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -61,12 +59,12 @@ static void boston_update(struct linedisp *linedisp)
|
||||
ulong val;
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
val = *((u64 *)&ctx->curr[0]);
|
||||
val = *((u64 *)&linedisp->buf[0]);
|
||||
__raw_writeq(val, ctx->base);
|
||||
#elif BITS_PER_LONG == 32
|
||||
val = *((u32 *)&ctx->curr[0]);
|
||||
val = *((u32 *)&linedisp->buf[0]);
|
||||
__raw_writel(val, ctx->base);
|
||||
val = *((u32 *)&ctx->curr[4]);
|
||||
val = *((u32 *)&linedisp->buf[4]);
|
||||
__raw_writel(val, ctx->base + 4);
|
||||
#else
|
||||
# error Not 32 or 64 bit
|
||||
@ -75,7 +73,9 @@ static void boston_update(struct linedisp *linedisp)
|
||||
|
||||
static struct img_ascii_lcd_config boston_config = {
|
||||
.num_chars = 8,
|
||||
.update = boston_update,
|
||||
.ops = {
|
||||
.update = boston_update,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -91,7 +91,7 @@ static void malta_update(struct linedisp *linedisp)
|
||||
|
||||
for (i = 0; i < linedisp->num_chars; i++) {
|
||||
err = regmap_write(ctx->regmap,
|
||||
ctx->offset + (i * 8), ctx->curr[i]);
|
||||
ctx->offset + (i * 8), linedisp->buf[i]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
@ -103,7 +103,9 @@ static void malta_update(struct linedisp *linedisp)
|
||||
static struct img_ascii_lcd_config malta_config = {
|
||||
.num_chars = 8,
|
||||
.external_regmap = true,
|
||||
.update = malta_update,
|
||||
.ops = {
|
||||
.update = malta_update,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@ -191,7 +193,7 @@ static void sead3_update(struct linedisp *linedisp)
|
||||
|
||||
err = regmap_write(ctx->regmap,
|
||||
ctx->offset + SEAD3_REG_LCD_DATA,
|
||||
ctx->curr[i]);
|
||||
linedisp->buf[i]);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
@ -203,7 +205,9 @@ static void sead3_update(struct linedisp *linedisp)
|
||||
static struct img_ascii_lcd_config sead3_config = {
|
||||
.num_chars = 16,
|
||||
.external_regmap = true,
|
||||
.update = sead3_update,
|
||||
.ops = {
|
||||
.update = sead3_update,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id img_ascii_lcd_matches[] = {
|
||||
@ -230,7 +234,7 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
|
||||
struct img_ascii_lcd_ctx *ctx;
|
||||
int err;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL);
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -247,8 +251,7 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(ctx->base);
|
||||
}
|
||||
|
||||
err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr,
|
||||
cfg->update);
|
||||
err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, &cfg->ops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -273,16 +276,13 @@ static int img_ascii_lcd_probe(struct platform_device *pdev)
|
||||
*
|
||||
* Remove an LCD display device, freeing private resources & ensuring that the
|
||||
* driver stops using the LCD display registers.
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
static int img_ascii_lcd_remove(struct platform_device *pdev)
|
||||
static void img_ascii_lcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
sysfs_remove_link(&pdev->dev.kobj, "message");
|
||||
linedisp_unregister(&ctx->linedisp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver img_ascii_lcd_driver = {
|
||||
@ -291,10 +291,11 @@ static struct platform_driver img_ascii_lcd_driver = {
|
||||
.of_match_table = img_ascii_lcd_matches,
|
||||
},
|
||||
.probe = img_ascii_lcd_probe,
|
||||
.remove = img_ascii_lcd_remove,
|
||||
.remove_new = img_ascii_lcd_remove,
|
||||
};
|
||||
module_platform_driver(img_ascii_lcd_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
|
||||
MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(LINEDISP);
|
||||
|
@ -10,13 +10,21 @@
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include <linux/map_to_7segment.h>
|
||||
#include <linux/map_to_14segment.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
#define DEFAULT_SCROLL_RATE (HZ / 2)
|
||||
@ -45,7 +53,7 @@ static void linedisp_scroll(struct timer_list *t)
|
||||
}
|
||||
|
||||
/* update the display */
|
||||
linedisp->update(linedisp);
|
||||
linedisp->ops->update(linedisp);
|
||||
|
||||
/* move on to the next character */
|
||||
linedisp->scroll_pos++;
|
||||
@ -89,7 +97,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
|
||||
linedisp->message = NULL;
|
||||
linedisp->message_len = 0;
|
||||
memset(linedisp->buf, ' ', linedisp->num_chars);
|
||||
linedisp->update(linedisp);
|
||||
linedisp->ops->update(linedisp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -165,9 +173,11 @@ static ssize_t scroll_step_ms_store(struct device *dev,
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
unsigned int ms;
|
||||
int err;
|
||||
|
||||
if (kstrtouint(buf, 10, &ms) != 0)
|
||||
return -EINVAL;
|
||||
err = kstrtouint(buf, 10, &ms);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
linedisp->scroll_rate = msecs_to_jiffies(ms);
|
||||
if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
|
||||
@ -181,45 +191,165 @@ static ssize_t scroll_step_ms_store(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR_RW(scroll_step_ms);
|
||||
|
||||
static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
struct linedisp_map *map = linedisp->map;
|
||||
|
||||
memcpy(buf, &map->map, map->size);
|
||||
return map->size;
|
||||
}
|
||||
|
||||
static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
struct linedisp_map *map = linedisp->map;
|
||||
|
||||
if (count != map->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&map->map, buf, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const SEG7_DEFAULT_MAP(initial_map_seg7);
|
||||
static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store);
|
||||
|
||||
static const SEG14_DEFAULT_MAP(initial_map_seg14);
|
||||
static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
|
||||
|
||||
static struct attribute *linedisp_attrs[] = {
|
||||
&dev_attr_message.attr,
|
||||
&dev_attr_scroll_step_ms.attr,
|
||||
NULL,
|
||||
&dev_attr_map_seg7.attr,
|
||||
&dev_attr_map_seg14.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(linedisp);
|
||||
|
||||
static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
struct linedisp_map *map = linedisp->map;
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_map_seg7.attr) {
|
||||
if (!map)
|
||||
return 0;
|
||||
if (map->type != LINEDISP_MAP_SEG7)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attr == &dev_attr_map_seg14.attr) {
|
||||
if (!map)
|
||||
return 0;
|
||||
if (map->type != LINEDISP_MAP_SEG14)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
};
|
||||
|
||||
static const struct attribute_group linedisp_group = {
|
||||
.is_visible = linedisp_attr_is_visible,
|
||||
.attrs = linedisp_attrs,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(linedisp);
|
||||
|
||||
static DEFINE_IDA(linedisp_id);
|
||||
|
||||
static void linedisp_release(struct device *dev)
|
||||
{
|
||||
struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
|
||||
|
||||
kfree(linedisp->map);
|
||||
kfree(linedisp->message);
|
||||
kfree(linedisp->buf);
|
||||
ida_free(&linedisp_id, linedisp->id);
|
||||
}
|
||||
|
||||
static const struct device_type linedisp_type = {
|
||||
.groups = linedisp_groups,
|
||||
.release = linedisp_release,
|
||||
};
|
||||
|
||||
static int linedisp_init_map(struct linedisp *linedisp)
|
||||
{
|
||||
struct linedisp_map *map;
|
||||
int err;
|
||||
|
||||
if (!linedisp->ops->get_map_type)
|
||||
return 0;
|
||||
|
||||
err = linedisp->ops->get_map_type(linedisp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
map = kmalloc(sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
map->type = err;
|
||||
|
||||
/* assign initial mapping */
|
||||
switch (map->type) {
|
||||
case LINEDISP_MAP_SEG7:
|
||||
map->map.seg7 = initial_map_seg7;
|
||||
map->size = sizeof(map->map.seg7);
|
||||
break;
|
||||
case LINEDISP_MAP_SEG14:
|
||||
map->map.seg14 = initial_map_seg14;
|
||||
map->size = sizeof(map->map.seg14);
|
||||
break;
|
||||
default:
|
||||
kfree(map);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
linedisp->map = map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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!
|
||||
* @ops: character line display operations
|
||||
*
|
||||
* 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))
|
||||
unsigned int num_chars, const struct linedisp_ops *ops)
|
||||
{
|
||||
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->ops = ops;
|
||||
linedisp->num_chars = num_chars;
|
||||
linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
|
||||
|
||||
err = ida_alloc(&linedisp_id, GFP_KERNEL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
linedisp->id = err;
|
||||
|
||||
device_initialize(&linedisp->dev);
|
||||
dev_set_name(&linedisp->dev, "linedisp.%lu",
|
||||
(unsigned long)atomic_inc_return(&linedisp_id));
|
||||
dev_set_name(&linedisp->dev, "linedisp.%u", linedisp->id);
|
||||
|
||||
err = -ENOMEM;
|
||||
linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
|
||||
if (!linedisp->buf)
|
||||
goto out_put_device;
|
||||
|
||||
/* initialise a character mapping, if required */
|
||||
err = linedisp_init_map(linedisp);
|
||||
if (err)
|
||||
goto out_put_device;
|
||||
|
||||
/* initialise a timer for scrolling the message */
|
||||
timer_setup(&linedisp->timer, linedisp_scroll, 0);
|
||||
@ -239,10 +369,11 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
||||
device_del(&linedisp->dev);
|
||||
out_del_timer:
|
||||
del_timer_sync(&linedisp->timer);
|
||||
out_put_device:
|
||||
put_device(&linedisp->dev);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(linedisp_register);
|
||||
EXPORT_SYMBOL_NS_GPL(linedisp_register, LINEDISP);
|
||||
|
||||
/**
|
||||
* linedisp_unregister - unregister a character line display
|
||||
@ -253,9 +384,8 @@ 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);
|
||||
EXPORT_SYMBOL_NS_GPL(linedisp_unregister, LINEDISP);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -11,33 +11,78 @@
|
||||
#ifndef _LINEDISP_H
|
||||
#define _LINEDISP_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/timer_types.h>
|
||||
|
||||
#include <linux/map_to_7segment.h>
|
||||
#include <linux/map_to_14segment.h>
|
||||
|
||||
struct linedisp;
|
||||
|
||||
/**
|
||||
* enum linedisp_map_type - type of the character mapping
|
||||
* @LINEDISP_MAP_SEG7: Map characters to 7 segment display
|
||||
* @LINEDISP_MAP_SEG14: Map characters to 14 segment display
|
||||
*/
|
||||
enum linedisp_map_type {
|
||||
LINEDISP_MAP_SEG7,
|
||||
LINEDISP_MAP_SEG14,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct linedisp_map - character mapping
|
||||
* @type: type of the character mapping
|
||||
* @map: conversion character mapping
|
||||
* @size: size of the @map
|
||||
*/
|
||||
struct linedisp_map {
|
||||
enum linedisp_map_type type;
|
||||
union {
|
||||
struct seg7_conversion_map seg7;
|
||||
struct seg14_conversion_map seg14;
|
||||
} map;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct linedisp_ops - character line display operations
|
||||
* @get_map_type: Function called to get the character mapping, if required
|
||||
* @update: Function called to update the display. This must not sleep!
|
||||
*/
|
||||
struct linedisp_ops {
|
||||
int (*get_map_type)(struct linedisp *linedisp);
|
||||
void (*update)(struct linedisp *linedisp);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @ops: character line display operations
|
||||
* @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
|
||||
* @id: instance id of this display
|
||||
*/
|
||||
struct linedisp {
|
||||
struct device dev;
|
||||
struct timer_list timer;
|
||||
void (*update)(struct linedisp *linedisp);
|
||||
const struct linedisp_ops *ops;
|
||||
struct linedisp_map *map;
|
||||
char *buf;
|
||||
char *message;
|
||||
unsigned int num_chars;
|
||||
unsigned int message_len;
|
||||
unsigned int scroll_pos;
|
||||
unsigned int scroll_rate;
|
||||
unsigned int id;
|
||||
};
|
||||
|
||||
int linedisp_register(struct linedisp *linedisp, struct device *parent,
|
||||
unsigned int num_chars, char *buf,
|
||||
void (*update)(struct linedisp *linedisp));
|
||||
unsigned int num_chars, const struct linedisp_ops *ops);
|
||||
void linedisp_unregister(struct linedisp *linedisp);
|
||||
|
||||
#endif /* LINEDISP_H */
|
||||
|
194
drivers/auxdisplay/max6959.c
Normal file
194
drivers/auxdisplay/max6959.c
Normal file
@ -0,0 +1,194 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MAX6958/6959 7-segment LED display controller
|
||||
* Datasheet:
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6958-MAX6959.pdf
|
||||
*
|
||||
* Copyright (c) 2024, Intel Corporation.
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*/
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/map_to_7segment.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
/* Registers */
|
||||
#define REG_DECODE_MODE 0x01
|
||||
#define REG_INTENSITY 0x02
|
||||
#define REG_SCAN_LIMIT 0x03
|
||||
#define REG_CONFIGURATION 0x04
|
||||
#define REG_CONFIGURATION_S_BIT BIT(0)
|
||||
|
||||
#define REG_DIGIT(x) (0x20 + (x))
|
||||
#define REG_DIGIT0 0x20
|
||||
#define REG_DIGIT1 0x21
|
||||
#define REG_DIGIT2 0x22
|
||||
#define REG_DIGIT3 0x23
|
||||
|
||||
#define REG_SEGMENTS 0x24
|
||||
#define REG_MAX REG_SEGMENTS
|
||||
|
||||
struct max6959_priv {
|
||||
struct linedisp linedisp;
|
||||
struct delayed_work work;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static void max6959_disp_update(struct work_struct *work)
|
||||
{
|
||||
struct max6959_priv *priv = container_of(work, struct max6959_priv, work.work);
|
||||
struct linedisp *linedisp = &priv->linedisp;
|
||||
struct linedisp_map *map = linedisp->map;
|
||||
char *s = linedisp->buf;
|
||||
u8 buf[4];
|
||||
|
||||
/* Map segments according to datasheet */
|
||||
buf[0] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
|
||||
buf[1] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
|
||||
buf[2] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
|
||||
buf[3] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
|
||||
|
||||
regmap_bulk_write(priv->regmap, REG_DIGIT(0), buf, ARRAY_SIZE(buf));
|
||||
}
|
||||
|
||||
static int max6959_linedisp_get_map_type(struct linedisp *linedisp)
|
||||
{
|
||||
struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
|
||||
|
||||
INIT_DELAYED_WORK(&priv->work, max6959_disp_update);
|
||||
return LINEDISP_MAP_SEG7;
|
||||
}
|
||||
|
||||
static void max6959_linedisp_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
|
||||
|
||||
schedule_delayed_work(&priv->work, 0);
|
||||
}
|
||||
|
||||
static const struct linedisp_ops max6959_linedisp_ops = {
|
||||
.get_map_type = max6959_linedisp_get_map_type,
|
||||
.update = max6959_linedisp_update,
|
||||
};
|
||||
|
||||
static int max6959_enable(struct max6959_priv *priv, bool enable)
|
||||
{
|
||||
u8 mask = REG_CONFIGURATION_S_BIT;
|
||||
u8 value = enable ? mask : 0;
|
||||
|
||||
return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value);
|
||||
}
|
||||
|
||||
static void max6959_power_off(void *priv)
|
||||
{
|
||||
max6959_enable(priv, false);
|
||||
}
|
||||
|
||||
static int max6959_power_on(struct max6959_priv *priv)
|
||||
{
|
||||
struct device *dev = regmap_get_device(priv->regmap);
|
||||
int ret;
|
||||
|
||||
ret = max6959_enable(priv, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, max6959_power_off, priv);
|
||||
}
|
||||
|
||||
static const struct regmap_config max6959_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = REG_MAX,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static int max6959_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct max6959_priv *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(client, &max6959_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
ret = max6959_power_on(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = linedisp_register(&priv->linedisp, dev, 4, &max6959_linedisp_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max6959_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max6959_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&priv->work);
|
||||
linedisp_unregister(&priv->linedisp);
|
||||
}
|
||||
|
||||
static int max6959_suspend(struct device *dev)
|
||||
{
|
||||
return max6959_enable(dev_get_drvdata(dev), false);
|
||||
}
|
||||
|
||||
static int max6959_resume(struct device *dev)
|
||||
{
|
||||
return max6959_enable(dev_get_drvdata(dev), true);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(max6959_pm_ops, max6959_suspend, max6959_resume);
|
||||
|
||||
static const struct i2c_device_id max6959_i2c_id[] = {
|
||||
{ "max6959" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max6959_i2c_id);
|
||||
|
||||
static const struct of_device_id max6959_of_table[] = {
|
||||
{ .compatible = "maxim,max6959" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max6959_of_table);
|
||||
|
||||
static struct i2c_driver max6959_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max6959",
|
||||
.pm = pm_sleep_ptr(&max6959_pm_ops),
|
||||
.of_match_table = max6959_of_table,
|
||||
},
|
||||
.probe = max6959_i2c_probe,
|
||||
.remove = max6959_i2c_remove,
|
||||
.id_table = max6959_i2c_id,
|
||||
};
|
||||
module_i2c_driver(max6959_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MAX6958/6959 7-segment LED controller");
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(LINEDISP);
|
@ -1519,8 +1519,103 @@ static void keypad_init(void)
|
||||
|
||||
static void panel_attach(struct parport *port)
|
||||
{
|
||||
int selected_keypad_type = NOT_SET;
|
||||
struct pardev_cb panel_cb;
|
||||
|
||||
/* take care of an eventual profile */
|
||||
switch (profile) {
|
||||
case PANEL_PROFILE_CUSTOM:
|
||||
/* custom profile */
|
||||
selected_keypad_type = DEFAULT_KEYPAD_TYPE;
|
||||
selected_lcd_type = DEFAULT_LCD_TYPE;
|
||||
break;
|
||||
case PANEL_PROFILE_OLD:
|
||||
/* 8 bits, 2*16, old keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_OLD;
|
||||
selected_lcd_type = LCD_TYPE_OLD;
|
||||
|
||||
/* TODO: This two are a little hacky, sort it out later */
|
||||
if (lcd_width == NOT_SET)
|
||||
lcd_width = 16;
|
||||
if (lcd_hwidth == NOT_SET)
|
||||
lcd_hwidth = 16;
|
||||
break;
|
||||
case PANEL_PROFILE_NEW:
|
||||
/* serial, 2*16, new keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_NEW;
|
||||
selected_lcd_type = LCD_TYPE_KS0074;
|
||||
break;
|
||||
case PANEL_PROFILE_HANTRONIX:
|
||||
/* 8 bits, 2*16 hantronix-like, no keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_NONE;
|
||||
selected_lcd_type = LCD_TYPE_HANTRONIX;
|
||||
break;
|
||||
case PANEL_PROFILE_NEXCOM:
|
||||
/* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */
|
||||
selected_keypad_type = KEYPAD_TYPE_NEXCOM;
|
||||
selected_lcd_type = LCD_TYPE_NEXCOM;
|
||||
break;
|
||||
case PANEL_PROFILE_LARGE:
|
||||
/* 8 bits, 2*40, old keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_OLD;
|
||||
selected_lcd_type = LCD_TYPE_OLD;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overwrite selection with module param values (both keypad and lcd),
|
||||
* where the deprecated params have lower prio.
|
||||
*/
|
||||
if (keypad_enabled != NOT_SET)
|
||||
selected_keypad_type = keypad_enabled;
|
||||
if (keypad_type != NOT_SET)
|
||||
selected_keypad_type = keypad_type;
|
||||
|
||||
keypad.enabled = (selected_keypad_type > 0);
|
||||
|
||||
if (lcd_enabled != NOT_SET)
|
||||
selected_lcd_type = lcd_enabled;
|
||||
if (lcd_type != NOT_SET)
|
||||
selected_lcd_type = lcd_type;
|
||||
|
||||
lcd.enabled = (selected_lcd_type > 0);
|
||||
|
||||
if (lcd.enabled) {
|
||||
/*
|
||||
* Init lcd struct with load-time values to preserve exact
|
||||
* current functionality (at least for now).
|
||||
*/
|
||||
lcd.charset = lcd_charset;
|
||||
lcd.proto = lcd_proto;
|
||||
lcd.pins.e = lcd_e_pin;
|
||||
lcd.pins.rs = lcd_rs_pin;
|
||||
lcd.pins.rw = lcd_rw_pin;
|
||||
lcd.pins.cl = lcd_cl_pin;
|
||||
lcd.pins.da = lcd_da_pin;
|
||||
lcd.pins.bl = lcd_bl_pin;
|
||||
}
|
||||
|
||||
switch (selected_keypad_type) {
|
||||
case KEYPAD_TYPE_OLD:
|
||||
keypad_profile = old_keypad_profile;
|
||||
break;
|
||||
case KEYPAD_TYPE_NEW:
|
||||
keypad_profile = new_keypad_profile;
|
||||
break;
|
||||
case KEYPAD_TYPE_NEXCOM:
|
||||
keypad_profile = nexcom_keypad_profile;
|
||||
break;
|
||||
default:
|
||||
keypad_profile = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lcd.enabled && !keypad.enabled) {
|
||||
/* no device enabled, let's exit */
|
||||
pr_err("panel driver disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (port->number != parport)
|
||||
return;
|
||||
|
||||
@ -1613,126 +1708,7 @@ static struct parport_driver panel_driver = {
|
||||
.detach = panel_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
module_parport_driver(panel_driver);
|
||||
|
||||
/* init function */
|
||||
static int __init panel_init_module(void)
|
||||
{
|
||||
int selected_keypad_type = NOT_SET, err;
|
||||
|
||||
/* take care of an eventual profile */
|
||||
switch (profile) {
|
||||
case PANEL_PROFILE_CUSTOM:
|
||||
/* custom profile */
|
||||
selected_keypad_type = DEFAULT_KEYPAD_TYPE;
|
||||
selected_lcd_type = DEFAULT_LCD_TYPE;
|
||||
break;
|
||||
case PANEL_PROFILE_OLD:
|
||||
/* 8 bits, 2*16, old keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_OLD;
|
||||
selected_lcd_type = LCD_TYPE_OLD;
|
||||
|
||||
/* TODO: This two are a little hacky, sort it out later */
|
||||
if (lcd_width == NOT_SET)
|
||||
lcd_width = 16;
|
||||
if (lcd_hwidth == NOT_SET)
|
||||
lcd_hwidth = 16;
|
||||
break;
|
||||
case PANEL_PROFILE_NEW:
|
||||
/* serial, 2*16, new keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_NEW;
|
||||
selected_lcd_type = LCD_TYPE_KS0074;
|
||||
break;
|
||||
case PANEL_PROFILE_HANTRONIX:
|
||||
/* 8 bits, 2*16 hantronix-like, no keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_NONE;
|
||||
selected_lcd_type = LCD_TYPE_HANTRONIX;
|
||||
break;
|
||||
case PANEL_PROFILE_NEXCOM:
|
||||
/* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */
|
||||
selected_keypad_type = KEYPAD_TYPE_NEXCOM;
|
||||
selected_lcd_type = LCD_TYPE_NEXCOM;
|
||||
break;
|
||||
case PANEL_PROFILE_LARGE:
|
||||
/* 8 bits, 2*40, old keypad */
|
||||
selected_keypad_type = KEYPAD_TYPE_OLD;
|
||||
selected_lcd_type = LCD_TYPE_OLD;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overwrite selection with module param values (both keypad and lcd),
|
||||
* where the deprecated params have lower prio.
|
||||
*/
|
||||
if (keypad_enabled != NOT_SET)
|
||||
selected_keypad_type = keypad_enabled;
|
||||
if (keypad_type != NOT_SET)
|
||||
selected_keypad_type = keypad_type;
|
||||
|
||||
keypad.enabled = (selected_keypad_type > 0);
|
||||
|
||||
if (lcd_enabled != NOT_SET)
|
||||
selected_lcd_type = lcd_enabled;
|
||||
if (lcd_type != NOT_SET)
|
||||
selected_lcd_type = lcd_type;
|
||||
|
||||
lcd.enabled = (selected_lcd_type > 0);
|
||||
|
||||
if (lcd.enabled) {
|
||||
/*
|
||||
* Init lcd struct with load-time values to preserve exact
|
||||
* current functionality (at least for now).
|
||||
*/
|
||||
lcd.charset = lcd_charset;
|
||||
lcd.proto = lcd_proto;
|
||||
lcd.pins.e = lcd_e_pin;
|
||||
lcd.pins.rs = lcd_rs_pin;
|
||||
lcd.pins.rw = lcd_rw_pin;
|
||||
lcd.pins.cl = lcd_cl_pin;
|
||||
lcd.pins.da = lcd_da_pin;
|
||||
lcd.pins.bl = lcd_bl_pin;
|
||||
}
|
||||
|
||||
switch (selected_keypad_type) {
|
||||
case KEYPAD_TYPE_OLD:
|
||||
keypad_profile = old_keypad_profile;
|
||||
break;
|
||||
case KEYPAD_TYPE_NEW:
|
||||
keypad_profile = new_keypad_profile;
|
||||
break;
|
||||
case KEYPAD_TYPE_NEXCOM:
|
||||
keypad_profile = nexcom_keypad_profile;
|
||||
break;
|
||||
default:
|
||||
keypad_profile = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lcd.enabled && !keypad.enabled) {
|
||||
/* no device enabled, let's exit */
|
||||
pr_err("panel driver disabled.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = parport_register_driver(&panel_driver);
|
||||
if (err) {
|
||||
pr_err("could not register with parport. Aborting.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pprt)
|
||||
pr_info("panel driver registered on parport%d (io=0x%lx).\n",
|
||||
parport, pprt->port->base);
|
||||
else
|
||||
pr_info("panel driver not yet registered\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit panel_cleanup_module(void)
|
||||
{
|
||||
parport_unregister_driver(&panel_driver);
|
||||
}
|
||||
|
||||
module_init(panel_init_module);
|
||||
module_exit(panel_cleanup_module);
|
||||
MODULE_AUTHOR("Willy Tarreau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
113
drivers/auxdisplay/seg-led-gpio.c
Normal file
113
drivers/auxdisplay/seg-led-gpio.c
Normal file
@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for a 7-segment LED display
|
||||
*
|
||||
* The decimal point LED present on some devices is currently not
|
||||
* supported.
|
||||
*
|
||||
* Copyright (C) Allied Telesis Labs
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/map_to_7segment.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "line-display.h"
|
||||
|
||||
struct seg_led_priv {
|
||||
struct linedisp linedisp;
|
||||
struct delayed_work work;
|
||||
struct gpio_descs *segment_gpios;
|
||||
};
|
||||
|
||||
static void seg_led_update(struct work_struct *work)
|
||||
{
|
||||
struct seg_led_priv *priv = container_of(work, struct seg_led_priv, work.work);
|
||||
struct linedisp *linedisp = &priv->linedisp;
|
||||
struct linedisp_map *map = linedisp->map;
|
||||
DECLARE_BITMAP(values, 8) = { };
|
||||
|
||||
bitmap_set_value8(values, map_to_seg7(&map->map.seg7, linedisp->buf[0]), 0);
|
||||
|
||||
gpiod_set_array_value_cansleep(priv->segment_gpios->ndescs, priv->segment_gpios->desc,
|
||||
priv->segment_gpios->info, values);
|
||||
}
|
||||
|
||||
static int seg_led_linedisp_get_map_type(struct linedisp *linedisp)
|
||||
{
|
||||
struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp);
|
||||
|
||||
INIT_DELAYED_WORK(&priv->work, seg_led_update);
|
||||
return LINEDISP_MAP_SEG7;
|
||||
}
|
||||
|
||||
static void seg_led_linedisp_update(struct linedisp *linedisp)
|
||||
{
|
||||
struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp);
|
||||
|
||||
schedule_delayed_work(&priv->work, 0);
|
||||
}
|
||||
|
||||
static const struct linedisp_ops seg_led_linedisp_ops = {
|
||||
.get_map_type = seg_led_linedisp_get_map_type,
|
||||
.update = seg_led_linedisp_update,
|
||||
};
|
||||
|
||||
static int seg_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct seg_led_priv *priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->segment_gpios = devm_gpiod_get_array(dev, "segment", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->segment_gpios))
|
||||
return PTR_ERR(priv->segment_gpios);
|
||||
|
||||
if (priv->segment_gpios->ndescs < 7 || priv->segment_gpios->ndescs > 8)
|
||||
return -EINVAL;
|
||||
|
||||
return linedisp_register(&priv->linedisp, dev, 1, &seg_led_linedisp_ops);
|
||||
}
|
||||
|
||||
static int seg_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct seg_led_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&priv->work);
|
||||
linedisp_unregister(&priv->linedisp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id seg_led_of_match[] = {
|
||||
{ .compatible = "gpio-7-segment"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, seg_led_of_match);
|
||||
|
||||
static struct platform_driver seg_led_driver = {
|
||||
.probe = seg_led_probe,
|
||||
.remove = seg_led_remove,
|
||||
.driver = {
|
||||
.name = "seg-led-gpio",
|
||||
.of_match_table = seg_led_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(seg_led_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
|
||||
MODULE_DESCRIPTION("7 segment LED driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(LINEDISP);
|
Loading…
Reference in New Issue
Block a user