Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/auxdisplay/max6959.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* MAX6958/6959 7-segment LED display controller
4
* Datasheet:
5
* https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6958-MAX6959.pdf
6
*
7
* Copyright (c) 2024, Intel Corporation.
8
* Author: Andy Shevchenko <[email protected]>
9
*/
10
#include <linux/array_size.h>
11
#include <linux/bitrev.h>
12
#include <linux/bits.h>
13
#include <linux/container_of.h>
14
#include <linux/err.h>
15
#include <linux/i2c.h>
16
#include <linux/mod_devicetable.h>
17
#include <linux/module.h>
18
#include <linux/pm.h>
19
#include <linux/regmap.h>
20
#include <linux/slab.h>
21
#include <linux/types.h>
22
#include <linux/workqueue.h>
23
24
#include <linux/map_to_7segment.h>
25
26
#include "line-display.h"
27
28
/* Registers */
29
#define REG_DECODE_MODE 0x01
30
#define REG_INTENSITY 0x02
31
#define REG_SCAN_LIMIT 0x03
32
#define REG_CONFIGURATION 0x04
33
#define REG_CONFIGURATION_S_BIT BIT(0)
34
35
#define REG_DIGIT(x) (0x20 + (x))
36
#define REG_DIGIT0 0x20
37
#define REG_DIGIT1 0x21
38
#define REG_DIGIT2 0x22
39
#define REG_DIGIT3 0x23
40
41
#define REG_SEGMENTS 0x24
42
#define REG_MAX REG_SEGMENTS
43
44
struct max6959_priv {
45
struct linedisp linedisp;
46
struct delayed_work work;
47
struct regmap *regmap;
48
};
49
50
static void max6959_disp_update(struct work_struct *work)
51
{
52
struct max6959_priv *priv = container_of(work, struct max6959_priv, work.work);
53
struct linedisp *linedisp = &priv->linedisp;
54
struct linedisp_map *map = linedisp->map;
55
char *s = linedisp->buf;
56
u8 buf[4];
57
58
/* Map segments according to datasheet */
59
buf[0] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
60
buf[1] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
61
buf[2] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
62
buf[3] = bitrev8(map_to_seg7(&map->map.seg7, *s++)) >> 1;
63
64
regmap_bulk_write(priv->regmap, REG_DIGIT(0), buf, ARRAY_SIZE(buf));
65
}
66
67
static int max6959_linedisp_get_map_type(struct linedisp *linedisp)
68
{
69
struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
70
71
INIT_DELAYED_WORK(&priv->work, max6959_disp_update);
72
return LINEDISP_MAP_SEG7;
73
}
74
75
static void max6959_linedisp_update(struct linedisp *linedisp)
76
{
77
struct max6959_priv *priv = container_of(linedisp, struct max6959_priv, linedisp);
78
79
schedule_delayed_work(&priv->work, 0);
80
}
81
82
static const struct linedisp_ops max6959_linedisp_ops = {
83
.get_map_type = max6959_linedisp_get_map_type,
84
.update = max6959_linedisp_update,
85
};
86
87
static int max6959_enable(struct max6959_priv *priv, bool enable)
88
{
89
u8 mask = REG_CONFIGURATION_S_BIT;
90
u8 value = enable ? mask : 0;
91
92
return regmap_update_bits(priv->regmap, REG_CONFIGURATION, mask, value);
93
}
94
95
static void max6959_power_off(void *priv)
96
{
97
max6959_enable(priv, false);
98
}
99
100
static int max6959_power_on(struct max6959_priv *priv)
101
{
102
struct device *dev = regmap_get_device(priv->regmap);
103
int ret;
104
105
ret = max6959_enable(priv, true);
106
if (ret)
107
return ret;
108
109
return devm_add_action_or_reset(dev, max6959_power_off, priv);
110
}
111
112
static const struct regmap_config max6959_regmap_config = {
113
.reg_bits = 8,
114
.val_bits = 8,
115
116
.max_register = REG_MAX,
117
.cache_type = REGCACHE_MAPLE,
118
};
119
120
static int max6959_i2c_probe(struct i2c_client *client)
121
{
122
struct device *dev = &client->dev;
123
struct max6959_priv *priv;
124
int ret;
125
126
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
127
if (!priv)
128
return -ENOMEM;
129
130
priv->regmap = devm_regmap_init_i2c(client, &max6959_regmap_config);
131
if (IS_ERR(priv->regmap))
132
return PTR_ERR(priv->regmap);
133
134
ret = max6959_power_on(priv);
135
if (ret)
136
return ret;
137
138
ret = linedisp_register(&priv->linedisp, dev, 4, &max6959_linedisp_ops);
139
if (ret)
140
return ret;
141
142
i2c_set_clientdata(client, priv);
143
144
return 0;
145
}
146
147
static void max6959_i2c_remove(struct i2c_client *client)
148
{
149
struct max6959_priv *priv = i2c_get_clientdata(client);
150
151
cancel_delayed_work_sync(&priv->work);
152
linedisp_unregister(&priv->linedisp);
153
}
154
155
static int max6959_suspend(struct device *dev)
156
{
157
return max6959_enable(dev_get_drvdata(dev), false);
158
}
159
160
static int max6959_resume(struct device *dev)
161
{
162
return max6959_enable(dev_get_drvdata(dev), true);
163
}
164
165
static DEFINE_SIMPLE_DEV_PM_OPS(max6959_pm_ops, max6959_suspend, max6959_resume);
166
167
static const struct i2c_device_id max6959_i2c_id[] = {
168
{ "max6959" },
169
{ }
170
};
171
MODULE_DEVICE_TABLE(i2c, max6959_i2c_id);
172
173
static const struct of_device_id max6959_of_table[] = {
174
{ .compatible = "maxim,max6959" },
175
{ }
176
};
177
MODULE_DEVICE_TABLE(of, max6959_of_table);
178
179
static struct i2c_driver max6959_i2c_driver = {
180
.driver = {
181
.name = "max6959",
182
.pm = pm_sleep_ptr(&max6959_pm_ops),
183
.of_match_table = max6959_of_table,
184
},
185
.probe = max6959_i2c_probe,
186
.remove = max6959_i2c_remove,
187
.id_table = max6959_i2c_id,
188
};
189
module_i2c_driver(max6959_i2c_driver);
190
191
MODULE_DESCRIPTION("MAX6958/6959 7-segment LED controller");
192
MODULE_AUTHOR("Andy Shevchenko <[email protected]>");
193
MODULE_LICENSE("GPL");
194
MODULE_IMPORT_NS("LINEDISP");
195
196