1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for a 7-segment LED display
5 * The decimal point LED present on some devices is currently not
8 * Copyright (C) Allied Telesis Labs
11 #include <linux/bitmap.h>
12 #include <linux/container_of.h>
13 #include <linux/errno.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/map_to_7segment.h>
16 #include <linux/mod_devicetable.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/types.h>
20 #include <linux/workqueue.h>
22 #include "line-display.h"
25 struct linedisp linedisp
;
26 struct delayed_work work
;
27 struct gpio_descs
*segment_gpios
;
30 static void seg_led_update(struct work_struct
*work
)
32 struct seg_led_priv
*priv
= container_of(work
, struct seg_led_priv
, work
.work
);
33 struct linedisp
*linedisp
= &priv
->linedisp
;
34 struct linedisp_map
*map
= linedisp
->map
;
35 DECLARE_BITMAP(values
, 8) = { };
37 bitmap_set_value8(values
, map_to_seg7(&map
->map
.seg7
, linedisp
->buf
[0]), 0);
39 gpiod_set_array_value_cansleep(priv
->segment_gpios
->ndescs
, priv
->segment_gpios
->desc
,
40 priv
->segment_gpios
->info
, values
);
43 static int seg_led_linedisp_get_map_type(struct linedisp
*linedisp
)
45 struct seg_led_priv
*priv
= container_of(linedisp
, struct seg_led_priv
, linedisp
);
47 INIT_DELAYED_WORK(&priv
->work
, seg_led_update
);
48 return LINEDISP_MAP_SEG7
;
51 static void seg_led_linedisp_update(struct linedisp
*linedisp
)
53 struct seg_led_priv
*priv
= container_of(linedisp
, struct seg_led_priv
, linedisp
);
55 schedule_delayed_work(&priv
->work
, 0);
58 static const struct linedisp_ops seg_led_linedisp_ops
= {
59 .get_map_type
= seg_led_linedisp_get_map_type
,
60 .update
= seg_led_linedisp_update
,
63 static int seg_led_probe(struct platform_device
*pdev
)
65 struct seg_led_priv
*priv
;
66 struct device
*dev
= &pdev
->dev
;
68 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
72 platform_set_drvdata(pdev
, priv
);
74 priv
->segment_gpios
= devm_gpiod_get_array(dev
, "segment", GPIOD_OUT_LOW
);
75 if (IS_ERR(priv
->segment_gpios
))
76 return PTR_ERR(priv
->segment_gpios
);
78 if (priv
->segment_gpios
->ndescs
< 7 || priv
->segment_gpios
->ndescs
> 8)
81 return linedisp_register(&priv
->linedisp
, dev
, 1, &seg_led_linedisp_ops
);
84 static void seg_led_remove(struct platform_device
*pdev
)
86 struct seg_led_priv
*priv
= platform_get_drvdata(pdev
);
88 cancel_delayed_work_sync(&priv
->work
);
89 linedisp_unregister(&priv
->linedisp
);
92 static const struct of_device_id seg_led_of_match
[] = {
93 { .compatible
= "gpio-7-segment"},
96 MODULE_DEVICE_TABLE(of
, seg_led_of_match
);
98 static struct platform_driver seg_led_driver
= {
99 .probe
= seg_led_probe
,
100 .remove_new
= seg_led_remove
,
102 .name
= "seg-led-gpio",
103 .of_match_table
= seg_led_of_match
,
106 module_platform_driver(seg_led_driver
);
108 MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
109 MODULE_DESCRIPTION("7 segment LED driver");
110 MODULE_LICENSE("GPL");
111 MODULE_IMPORT_NS(LINEDISP
);