Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/auxdisplay/img-ascii-lcd.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2016 Imagination Technologies
4
* Author: Paul Burton <[email protected]>
5
*/
6
7
#include <linux/kernel.h>
8
#include <linux/io.h>
9
#include <linux/mfd/syscon.h>
10
#include <linux/module.h>
11
#include <linux/of.h>
12
#include <linux/platform_device.h>
13
#include <linux/property.h>
14
#include <linux/regmap.h>
15
#include <linux/slab.h>
16
17
#include "line-display.h"
18
19
struct img_ascii_lcd_ctx;
20
21
/**
22
* struct img_ascii_lcd_config - Configuration information about an LCD model
23
* @num_chars: the number of characters the LCD can display
24
* @external_regmap: true if registers are in a system controller, else false
25
* @ops: character line display operations
26
*/
27
struct img_ascii_lcd_config {
28
unsigned int num_chars;
29
bool external_regmap;
30
const struct linedisp_ops ops;
31
};
32
33
/**
34
* struct img_ascii_lcd_ctx - Private data structure
35
* @linedisp: line display structure
36
* @base: the base address of the LCD registers
37
* @regmap: the regmap through which LCD registers are accessed
38
* @offset: the offset within regmap to the start of the LCD registers
39
*/
40
struct img_ascii_lcd_ctx {
41
struct linedisp linedisp;
42
union {
43
void __iomem *base;
44
struct regmap *regmap;
45
};
46
u32 offset;
47
};
48
49
/*
50
* MIPS Boston development board
51
*/
52
53
static void boston_update(struct linedisp *linedisp)
54
{
55
struct img_ascii_lcd_ctx *ctx =
56
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
57
ulong val;
58
59
#if BITS_PER_LONG == 64
60
val = *((u64 *)&linedisp->buf[0]);
61
__raw_writeq(val, ctx->base);
62
#elif BITS_PER_LONG == 32
63
val = *((u32 *)&linedisp->buf[0]);
64
__raw_writel(val, ctx->base);
65
val = *((u32 *)&linedisp->buf[4]);
66
__raw_writel(val, ctx->base + 4);
67
#else
68
# error Not 32 or 64 bit
69
#endif
70
}
71
72
static const struct img_ascii_lcd_config boston_config = {
73
.num_chars = 8,
74
.ops = {
75
.update = boston_update,
76
},
77
};
78
79
/*
80
* MIPS Malta development board
81
*/
82
83
static void malta_update(struct linedisp *linedisp)
84
{
85
struct img_ascii_lcd_ctx *ctx =
86
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
87
unsigned int i;
88
int err = 0;
89
90
for (i = 0; i < linedisp->num_chars; i++) {
91
err = regmap_write(ctx->regmap,
92
ctx->offset + (i * 8), linedisp->buf[i]);
93
if (err)
94
break;
95
}
96
97
if (unlikely(err))
98
pr_err_ratelimited("Failed to update LCD display: %d\n", err);
99
}
100
101
static const struct img_ascii_lcd_config malta_config = {
102
.num_chars = 8,
103
.external_regmap = true,
104
.ops = {
105
.update = malta_update,
106
},
107
};
108
109
/*
110
* MIPS SEAD3 development board
111
*/
112
113
enum {
114
SEAD3_REG_LCD_CTRL = 0x00,
115
#define SEAD3_REG_LCD_CTRL_SETDRAM BIT(7)
116
SEAD3_REG_LCD_DATA = 0x08,
117
SEAD3_REG_CPLD_STATUS = 0x10,
118
#define SEAD3_REG_CPLD_STATUS_BUSY BIT(0)
119
SEAD3_REG_CPLD_DATA = 0x18,
120
#define SEAD3_REG_CPLD_DATA_BUSY BIT(7)
121
};
122
123
static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx *ctx)
124
{
125
unsigned int status;
126
int err;
127
128
do {
129
err = regmap_read(ctx->regmap,
130
ctx->offset + SEAD3_REG_CPLD_STATUS,
131
&status);
132
if (err)
133
return err;
134
} while (status & SEAD3_REG_CPLD_STATUS_BUSY);
135
136
return 0;
137
138
}
139
140
static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
141
{
142
unsigned int cpld_data;
143
int err;
144
145
err = sead3_wait_sm_idle(ctx);
146
if (err)
147
return err;
148
149
do {
150
err = regmap_read(ctx->regmap,
151
ctx->offset + SEAD3_REG_LCD_CTRL,
152
&cpld_data);
153
if (err)
154
return err;
155
156
err = sead3_wait_sm_idle(ctx);
157
if (err)
158
return err;
159
160
err = regmap_read(ctx->regmap,
161
ctx->offset + SEAD3_REG_CPLD_DATA,
162
&cpld_data);
163
if (err)
164
return err;
165
} while (cpld_data & SEAD3_REG_CPLD_DATA_BUSY);
166
167
return 0;
168
}
169
170
static void sead3_update(struct linedisp *linedisp)
171
{
172
struct img_ascii_lcd_ctx *ctx =
173
container_of(linedisp, struct img_ascii_lcd_ctx, linedisp);
174
unsigned int i;
175
int err = 0;
176
177
for (i = 0; i < linedisp->num_chars; i++) {
178
err = sead3_wait_lcd_idle(ctx);
179
if (err)
180
break;
181
182
err = regmap_write(ctx->regmap,
183
ctx->offset + SEAD3_REG_LCD_CTRL,
184
SEAD3_REG_LCD_CTRL_SETDRAM | i);
185
if (err)
186
break;
187
188
err = sead3_wait_lcd_idle(ctx);
189
if (err)
190
break;
191
192
err = regmap_write(ctx->regmap,
193
ctx->offset + SEAD3_REG_LCD_DATA,
194
linedisp->buf[i]);
195
if (err)
196
break;
197
}
198
199
if (unlikely(err))
200
pr_err_ratelimited("Failed to update LCD display: %d\n", err);
201
}
202
203
static const struct img_ascii_lcd_config sead3_config = {
204
.num_chars = 16,
205
.external_regmap = true,
206
.ops = {
207
.update = sead3_update,
208
},
209
};
210
211
static const struct of_device_id img_ascii_lcd_matches[] = {
212
{ .compatible = "img,boston-lcd", .data = &boston_config },
213
{ .compatible = "mti,malta-lcd", .data = &malta_config },
214
{ .compatible = "mti,sead3-lcd", .data = &sead3_config },
215
{ /* sentinel */ }
216
};
217
MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
218
219
/**
220
* img_ascii_lcd_probe() - probe an LCD display device
221
* @pdev: the LCD platform device
222
*
223
* Probe an LCD display device, ensuring that we have the required resources in
224
* order to access the LCD & setting up private data as well as sysfs files.
225
*
226
* Return: 0 on success, else -ERRNO
227
*/
228
static int img_ascii_lcd_probe(struct platform_device *pdev)
229
{
230
struct device *dev = &pdev->dev;
231
const struct img_ascii_lcd_config *cfg = device_get_match_data(dev);
232
struct img_ascii_lcd_ctx *ctx;
233
int err;
234
235
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
236
if (!ctx)
237
return -ENOMEM;
238
239
if (cfg->external_regmap) {
240
ctx->regmap = syscon_node_to_regmap(dev->parent->of_node);
241
if (IS_ERR(ctx->regmap))
242
return PTR_ERR(ctx->regmap);
243
244
if (of_property_read_u32(dev->of_node, "offset", &ctx->offset))
245
return -EINVAL;
246
} else {
247
ctx->base = devm_platform_ioremap_resource(pdev, 0);
248
if (IS_ERR(ctx->base))
249
return PTR_ERR(ctx->base);
250
}
251
252
err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, &cfg->ops);
253
if (err)
254
return err;
255
256
/* for backwards compatibility */
257
err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj,
258
&ctx->linedisp.dev.kobj,
259
"message", NULL);
260
if (err)
261
goto err_unregister;
262
263
platform_set_drvdata(pdev, ctx);
264
return 0;
265
266
err_unregister:
267
linedisp_unregister(&ctx->linedisp);
268
return err;
269
}
270
271
/**
272
* img_ascii_lcd_remove() - remove an LCD display device
273
* @pdev: the LCD platform device
274
*
275
* Remove an LCD display device, freeing private resources & ensuring that the
276
* driver stops using the LCD display registers.
277
*/
278
static void img_ascii_lcd_remove(struct platform_device *pdev)
279
{
280
struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
281
282
sysfs_remove_link(&pdev->dev.kobj, "message");
283
linedisp_unregister(&ctx->linedisp);
284
}
285
286
static struct platform_driver img_ascii_lcd_driver = {
287
.driver = {
288
.name = "img-ascii-lcd",
289
.of_match_table = img_ascii_lcd_matches,
290
},
291
.probe = img_ascii_lcd_probe,
292
.remove = img_ascii_lcd_remove,
293
};
294
module_platform_driver(img_ascii_lcd_driver);
295
296
MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
297
MODULE_AUTHOR("Paul Burton <[email protected]>");
298
MODULE_LICENSE("GPL");
299
MODULE_IMPORT_NS("LINEDISP");
300
301