Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

auxdisplay: Extract character line display core support

Extract the character line display core support from the simple ASCII
LCD driver for the MIPS Boston, Malta & SEAD3 development boards into
its own subdriver, so it can be reused for other displays.

As this moves the "message" device attribute in sysfs in a "linedisp.N"
subdirectory, a symlink is added to preserve backwards compatibility.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>

authored by

Geert Uytterhoeven and committed by
Miguel Ojeda
7e76aece 12a19324

+313 -175
+7
drivers/auxdisplay/Kconfig
··· 25 25 This is some character LCD core interface that multiple drivers can 26 26 use. 27 27 28 + config LINEDISP 29 + tristate "Character line display core support" if COMPILE_TEST 30 + help 31 + This is the core support for single-line character displays, to be 32 + selected by drivers that use it. 33 + 28 34 config HD44780_COMMON 29 35 tristate "Common functions for HD44780 (and compatibles) LCD displays" if COMPILE_TEST 30 36 select CHARLCD ··· 161 155 depends on HAS_IOMEM 162 156 default y if MIPS_MALTA 163 157 select MFD_SYSCON 158 + select LINEDISP 164 159 help 165 160 Enable this to support the simple ASCII LCD displays found on 166 161 development boards such as the MIPS Boston, MIPS Malta & MIPS SEAD3
+1
drivers/auxdisplay/Makefile
··· 13 13 obj-$(CONFIG_HT16K33) += ht16k33.o 14 14 obj-$(CONFIG_PARPORT_PANEL) += panel.o 15 15 obj-$(CONFIG_LCD2S) += lcd2s.o 16 + obj-$(CONFIG_LINEDISP) += line-display.o
+31 -175
drivers/auxdisplay/img-ascii-lcd.c
··· 4 4 * Author: Paul Burton <paul.burton@mips.com> 5 5 */ 6 6 7 - #include <generated/utsrelease.h> 8 7 #include <linux/kernel.h> 9 8 #include <linux/io.h> 10 9 #include <linux/mfd/syscon.h> ··· 13 14 #include <linux/platform_device.h> 14 15 #include <linux/regmap.h> 15 16 #include <linux/slab.h> 16 - #include <linux/sysfs.h> 17 + 18 + #include "line-display.h" 17 19 18 20 struct img_ascii_lcd_ctx; 19 21 ··· 27 27 struct img_ascii_lcd_config { 28 28 unsigned int num_chars; 29 29 bool external_regmap; 30 - void (*update)(struct img_ascii_lcd_ctx *ctx); 30 + void (*update)(struct linedisp *linedisp); 31 31 }; 32 32 33 33 /** 34 34 * struct img_ascii_lcd_ctx - Private data structure 35 - * @pdev: the ASCII LCD platform device 36 35 * @base: the base address of the LCD registers 37 36 * @regmap: the regmap through which LCD registers are accessed 38 37 * @offset: the offset within regmap to the start of the LCD registers 39 38 * @cfg: pointer to the LCD model configuration 40 - * @message: the full message to display or scroll on the LCD 41 - * @message_len: the length of the @message string 42 - * @scroll_pos: index of the first character of @message currently displayed 43 - * @scroll_rate: scroll interval in jiffies 44 - * @timer: timer used to implement scrolling 39 + * @linedisp: line display structure 45 40 * @curr: the string currently displayed on the LCD 46 41 */ 47 42 struct img_ascii_lcd_ctx { 48 - struct platform_device *pdev; 49 43 union { 50 44 void __iomem *base; 51 45 struct regmap *regmap; 52 46 }; 53 47 u32 offset; 54 48 const struct img_ascii_lcd_config *cfg; 55 - char *message; 56 - unsigned int message_len; 57 - unsigned int scroll_pos; 58 - unsigned int scroll_rate; 59 - struct timer_list timer; 49 + struct linedisp linedisp; 60 50 char curr[] __aligned(8); 61 51 }; 62 52 ··· 54 64 * MIPS Boston development board 55 65 */ 56 66 57 - static void boston_update(struct img_ascii_lcd_ctx *ctx) 67 + static void boston_update(struct linedisp *linedisp) 58 68 { 69 + struct img_ascii_lcd_ctx *ctx = 70 + container_of(linedisp, struct img_ascii_lcd_ctx, linedisp); 59 71 ulong val; 60 72 61 73 #if BITS_PER_LONG == 64 ··· 82 90 * MIPS Malta development board 83 91 */ 84 92 85 - static void malta_update(struct img_ascii_lcd_ctx *ctx) 93 + static void malta_update(struct linedisp *linedisp) 86 94 { 95 + struct img_ascii_lcd_ctx *ctx = 96 + container_of(linedisp, struct img_ascii_lcd_ctx, linedisp); 87 97 unsigned int i; 88 98 int err = 0; 89 99 90 - for (i = 0; i < ctx->cfg->num_chars; i++) { 100 + for (i = 0; i < linedisp->num_chars; i++) { 91 101 err = regmap_write(ctx->regmap, 92 102 ctx->offset + (i * 8), ctx->curr[i]); 93 103 if (err) ··· 167 173 return 0; 168 174 } 169 175 170 - static void sead3_update(struct img_ascii_lcd_ctx *ctx) 176 + static void sead3_update(struct linedisp *linedisp) 171 177 { 178 + struct img_ascii_lcd_ctx *ctx = 179 + container_of(linedisp, struct img_ascii_lcd_ctx, linedisp); 172 180 unsigned int i; 173 181 int err = 0; 174 182 175 - for (i = 0; i < ctx->cfg->num_chars; i++) { 183 + for (i = 0; i < linedisp->num_chars; i++) { 176 184 err = sead3_wait_lcd_idle(ctx); 177 185 if (err) 178 186 break; ··· 215 219 MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches); 216 220 217 221 /** 218 - * img_ascii_lcd_scroll() - scroll the display by a character 219 - * @t: really a pointer to the private data structure 220 - * 221 - * Scroll the current message along the LCD by one character, rearming the 222 - * timer if required. 223 - */ 224 - static void img_ascii_lcd_scroll(struct timer_list *t) 225 - { 226 - struct img_ascii_lcd_ctx *ctx = from_timer(ctx, t, timer); 227 - unsigned int i, ch = ctx->scroll_pos; 228 - unsigned int num_chars = ctx->cfg->num_chars; 229 - 230 - /* update the current message string */ 231 - for (i = 0; i < num_chars;) { 232 - /* copy as many characters from the string as possible */ 233 - for (; i < num_chars && ch < ctx->message_len; i++, ch++) 234 - ctx->curr[i] = ctx->message[ch]; 235 - 236 - /* wrap around to the start of the string */ 237 - ch = 0; 238 - } 239 - 240 - /* update the LCD */ 241 - ctx->cfg->update(ctx); 242 - 243 - /* move on to the next character */ 244 - ctx->scroll_pos++; 245 - ctx->scroll_pos %= ctx->message_len; 246 - 247 - /* rearm the timer */ 248 - if (ctx->message_len > ctx->cfg->num_chars) 249 - mod_timer(&ctx->timer, jiffies + ctx->scroll_rate); 250 - } 251 - 252 - /** 253 - * img_ascii_lcd_display() - set the message to be displayed 254 - * @ctx: pointer to the private data structure 255 - * @msg: the message to display 256 - * @count: length of msg, or -1 257 - * 258 - * Display a new message @msg on the LCD. @msg can be longer than the number of 259 - * characters the LCD can display, in which case it will begin scrolling across 260 - * the LCD display. 261 - * 262 - * Return: 0 on success, -ENOMEM on memory allocation failure 263 - */ 264 - static int img_ascii_lcd_display(struct img_ascii_lcd_ctx *ctx, 265 - const char *msg, ssize_t count) 266 - { 267 - char *new_msg; 268 - 269 - /* stop the scroll timer */ 270 - del_timer_sync(&ctx->timer); 271 - 272 - if (count == -1) 273 - count = strlen(msg); 274 - 275 - /* if the string ends with a newline, trim it */ 276 - if (msg[count - 1] == '\n') 277 - count--; 278 - 279 - if (!count) { 280 - /* clear the LCD */ 281 - devm_kfree(&ctx->pdev->dev, ctx->message); 282 - ctx->message = NULL; 283 - ctx->message_len = 0; 284 - memset(ctx->curr, ' ', ctx->cfg->num_chars); 285 - ctx->cfg->update(ctx); 286 - return 0; 287 - } 288 - 289 - new_msg = devm_kmalloc(&ctx->pdev->dev, count + 1, GFP_KERNEL); 290 - if (!new_msg) 291 - return -ENOMEM; 292 - 293 - memcpy(new_msg, msg, count); 294 - new_msg[count] = 0; 295 - 296 - if (ctx->message) 297 - devm_kfree(&ctx->pdev->dev, ctx->message); 298 - 299 - ctx->message = new_msg; 300 - ctx->message_len = count; 301 - ctx->scroll_pos = 0; 302 - 303 - /* update the LCD */ 304 - img_ascii_lcd_scroll(&ctx->timer); 305 - 306 - return 0; 307 - } 308 - 309 - /** 310 - * message_show() - read message via sysfs 311 - * @dev: the LCD device 312 - * @attr: the LCD message attribute 313 - * @buf: the buffer to read the message into 314 - * 315 - * Read the current message being displayed or scrolled across the LCD display 316 - * into @buf, for reads from sysfs. 317 - * 318 - * Return: the number of characters written to @buf 319 - */ 320 - static ssize_t message_show(struct device *dev, struct device_attribute *attr, 321 - char *buf) 322 - { 323 - struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev); 324 - 325 - return sysfs_emit(buf, "%s\n", ctx->message); 326 - } 327 - 328 - /** 329 - * message_store() - write a new message via sysfs 330 - * @dev: the LCD device 331 - * @attr: the LCD message attribute 332 - * @buf: the buffer containing the new message 333 - * @count: the size of the message in @buf 334 - * 335 - * Write a new message to display or scroll across the LCD display from sysfs. 336 - * 337 - * Return: the size of the message on success, else -ERRNO 338 - */ 339 - static ssize_t message_store(struct device *dev, struct device_attribute *attr, 340 - const char *buf, size_t count) 341 - { 342 - struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev); 343 - int err; 344 - 345 - err = img_ascii_lcd_display(ctx, buf, count); 346 - return err ?: count; 347 - } 348 - 349 - static DEVICE_ATTR_RW(message); 350 - 351 - /** 352 222 * img_ascii_lcd_probe() - probe an LCD display device 353 223 * @pdev: the LCD platform device 354 224 * ··· 253 391 return PTR_ERR(ctx->base); 254 392 } 255 393 256 - ctx->pdev = pdev; 257 - ctx->cfg = cfg; 258 - ctx->message = NULL; 259 - ctx->scroll_pos = 0; 260 - ctx->scroll_rate = HZ / 2; 394 + err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr, 395 + cfg->update); 396 + if (err) 397 + return err; 261 398 262 - /* initialise a timer for scrolling the message */ 263 - timer_setup(&ctx->timer, img_ascii_lcd_scroll, 0); 399 + /* for backwards compatibility */ 400 + err = compat_only_sysfs_link_entry_to_kobj(&dev->kobj, 401 + &ctx->linedisp.dev.kobj, 402 + "message", NULL); 403 + if (err) 404 + goto err_unregister; 264 405 265 406 platform_set_drvdata(pdev, ctx); 266 - 267 - /* display a default message */ 268 - err = img_ascii_lcd_display(ctx, "Linux " UTS_RELEASE " ", -1); 269 - if (err) 270 - goto out_del_timer; 271 - 272 - err = device_create_file(dev, &dev_attr_message); 273 - if (err) 274 - goto out_del_timer; 275 - 276 407 return 0; 277 - out_del_timer: 278 - del_timer_sync(&ctx->timer); 408 + 409 + err_unregister: 410 + linedisp_unregister(&ctx->linedisp); 279 411 return err; 280 412 } 281 413 ··· 286 430 { 287 431 struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev); 288 432 289 - device_remove_file(&pdev->dev, &dev_attr_message); 290 - del_timer_sync(&ctx->timer); 433 + sysfs_remove_link(&pdev->dev.kobj, "message"); 434 + linedisp_unregister(&ctx->linedisp); 291 435 return 0; 292 436 } 293 437
+231
drivers/auxdisplay/line-display.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Character line display core support 4 + * 5 + * Copyright (C) 2016 Imagination Technologies 6 + * Author: Paul Burton <paul.burton@mips.com> 7 + * 8 + * Copyright (C) 2021 Glider bv 9 + */ 10 + 11 + #include <generated/utsrelease.h> 12 + 13 + #include <linux/device.h> 14 + #include <linux/module.h> 15 + #include <linux/slab.h> 16 + #include <linux/string.h> 17 + #include <linux/sysfs.h> 18 + #include <linux/timer.h> 19 + 20 + #include "line-display.h" 21 + 22 + /** 23 + * linedisp_scroll() - scroll the display by a character 24 + * @t: really a pointer to the private data structure 25 + * 26 + * Scroll the current message along the display by one character, rearming the 27 + * timer if required. 28 + */ 29 + static void linedisp_scroll(struct timer_list *t) 30 + { 31 + struct linedisp *linedisp = from_timer(linedisp, t, timer); 32 + unsigned int i, ch = linedisp->scroll_pos; 33 + unsigned int num_chars = linedisp->num_chars; 34 + 35 + /* update the current message string */ 36 + for (i = 0; i < num_chars;) { 37 + /* copy as many characters from the string as possible */ 38 + for (; i < num_chars && ch < linedisp->message_len; i++, ch++) 39 + linedisp->buf[i] = linedisp->message[ch]; 40 + 41 + /* wrap around to the start of the string */ 42 + ch = 0; 43 + } 44 + 45 + /* update the display */ 46 + linedisp->update(linedisp); 47 + 48 + /* move on to the next character */ 49 + linedisp->scroll_pos++; 50 + linedisp->scroll_pos %= linedisp->message_len; 51 + 52 + /* rearm the timer */ 53 + if (linedisp->message_len > num_chars) 54 + mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate); 55 + } 56 + 57 + /** 58 + * linedisp_display() - set the message to be displayed 59 + * @linedisp: pointer to the private data structure 60 + * @msg: the message to display 61 + * @count: length of msg, or -1 62 + * 63 + * Display a new message @msg on the display. @msg can be longer than the 64 + * number of characters the display can display, in which case it will begin 65 + * scrolling across the display. 66 + * 67 + * Return: 0 on success, -ENOMEM on memory allocation failure 68 + */ 69 + static int linedisp_display(struct linedisp *linedisp, const char *msg, 70 + ssize_t count) 71 + { 72 + char *new_msg; 73 + 74 + /* stop the scroll timer */ 75 + del_timer_sync(&linedisp->timer); 76 + 77 + if (count == -1) 78 + count = strlen(msg); 79 + 80 + /* if the string ends with a newline, trim it */ 81 + if (msg[count - 1] == '\n') 82 + count--; 83 + 84 + if (!count) { 85 + /* Clear the display */ 86 + kfree(linedisp->message); 87 + linedisp->message = NULL; 88 + linedisp->message_len = 0; 89 + memset(linedisp->buf, ' ', linedisp->num_chars); 90 + linedisp->update(linedisp); 91 + return 0; 92 + } 93 + 94 + new_msg = kmalloc(count + 1, GFP_KERNEL); 95 + if (!new_msg) 96 + return -ENOMEM; 97 + 98 + memcpy(new_msg, msg, count); 99 + new_msg[count] = 0; 100 + 101 + kfree(linedisp->message); 102 + 103 + linedisp->message = new_msg; 104 + linedisp->message_len = count; 105 + linedisp->scroll_pos = 0; 106 + 107 + /* update the display */ 108 + linedisp_scroll(&linedisp->timer); 109 + 110 + return 0; 111 + } 112 + 113 + /** 114 + * message_show() - read message via sysfs 115 + * @dev: the display device 116 + * @attr: the display message attribute 117 + * @buf: the buffer to read the message into 118 + * 119 + * Read the current message being displayed or scrolled across the display into 120 + * @buf, for reads from sysfs. 121 + * 122 + * Return: the number of characters written to @buf 123 + */ 124 + static ssize_t message_show(struct device *dev, struct device_attribute *attr, 125 + char *buf) 126 + { 127 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 128 + 129 + return sysfs_emit(buf, "%s\n", linedisp->message); 130 + } 131 + 132 + /** 133 + * message_store() - write a new message via sysfs 134 + * @dev: the display device 135 + * @attr: the display message attribute 136 + * @buf: the buffer containing the new message 137 + * @count: the size of the message in @buf 138 + * 139 + * Write a new message to display or scroll across the display from sysfs. 140 + * 141 + * Return: the size of the message on success, else -ERRNO 142 + */ 143 + static ssize_t message_store(struct device *dev, struct device_attribute *attr, 144 + const char *buf, size_t count) 145 + { 146 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 147 + int err; 148 + 149 + err = linedisp_display(linedisp, buf, count); 150 + return err ?: count; 151 + } 152 + 153 + static DEVICE_ATTR_RW(message); 154 + 155 + static struct attribute *linedisp_attrs[] = { 156 + &dev_attr_message.attr, 157 + NULL, 158 + }; 159 + ATTRIBUTE_GROUPS(linedisp); 160 + 161 + static const struct device_type linedisp_type = { 162 + .groups = linedisp_groups, 163 + }; 164 + 165 + /** 166 + * linedisp_register - register a character line display 167 + * @linedisp: pointer to character line display structure 168 + * @parent: parent device 169 + * @num_chars: the number of characters that can be displayed 170 + * @buf: pointer to a buffer that can hold @num_chars characters 171 + * @update: Function called to update the display. This must not sleep! 172 + * 173 + * Return: zero on success, else a negative error code. 174 + */ 175 + int linedisp_register(struct linedisp *linedisp, struct device *parent, 176 + unsigned int num_chars, char *buf, 177 + void (*update)(struct linedisp *linedisp)) 178 + { 179 + static atomic_t linedisp_id = ATOMIC_INIT(-1); 180 + int err; 181 + 182 + memset(linedisp, 0, sizeof(*linedisp)); 183 + linedisp->dev.parent = parent; 184 + linedisp->dev.type = &linedisp_type; 185 + linedisp->update = update; 186 + linedisp->buf = buf; 187 + linedisp->num_chars = num_chars; 188 + linedisp->scroll_rate = HZ / 2; 189 + 190 + device_initialize(&linedisp->dev); 191 + dev_set_name(&linedisp->dev, "linedisp.%lu", 192 + (unsigned long)atomic_inc_return(&linedisp_id)); 193 + 194 + /* initialise a timer for scrolling the message */ 195 + timer_setup(&linedisp->timer, linedisp_scroll, 0); 196 + 197 + err = device_add(&linedisp->dev); 198 + if (err) 199 + goto out_del_timer; 200 + 201 + /* display a default message */ 202 + err = linedisp_display(linedisp, "Linux " UTS_RELEASE " ", -1); 203 + if (err) 204 + goto out_del_dev; 205 + 206 + return 0; 207 + 208 + out_del_dev: 209 + device_del(&linedisp->dev); 210 + out_del_timer: 211 + del_timer_sync(&linedisp->timer); 212 + put_device(&linedisp->dev); 213 + return err; 214 + } 215 + EXPORT_SYMBOL_GPL(linedisp_register); 216 + 217 + /** 218 + * linedisp_unregister - unregister a character line display 219 + * @linedisp: pointer to character line display structure registered previously 220 + * with linedisp_register() 221 + */ 222 + void linedisp_unregister(struct linedisp *linedisp) 223 + { 224 + device_del(&linedisp->dev); 225 + del_timer_sync(&linedisp->timer); 226 + kfree(linedisp->message); 227 + put_device(&linedisp->dev); 228 + } 229 + EXPORT_SYMBOL_GPL(linedisp_unregister); 230 + 231 + MODULE_LICENSE("GPL");
+43
drivers/auxdisplay/line-display.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Character line display core support 4 + * 5 + * Copyright (C) 2016 Imagination Technologies 6 + * Author: Paul Burton <paul.burton@mips.com> 7 + * 8 + * Copyright (C) 2021 Glider bv 9 + */ 10 + 11 + #ifndef _LINEDISP_H 12 + #define _LINEDISP_H 13 + 14 + /** 15 + * struct linedisp - character line display private data structure 16 + * @dev: the line display device 17 + * @timer: timer used to implement scrolling 18 + * @update: function called to update the display 19 + * @buf: pointer to the buffer for the string currently displayed 20 + * @message: the full message to display or scroll on the display 21 + * @num_chars: the number of characters that can be displayed 22 + * @message_len: the length of the @message string 23 + * @scroll_pos: index of the first character of @message currently displayed 24 + * @scroll_rate: scroll interval in jiffies 25 + */ 26 + struct linedisp { 27 + struct device dev; 28 + struct timer_list timer; 29 + void (*update)(struct linedisp *linedisp); 30 + char *buf; 31 + char *message; 32 + unsigned int num_chars; 33 + unsigned int message_len; 34 + unsigned int scroll_pos; 35 + unsigned int scroll_rate; 36 + }; 37 + 38 + int linedisp_register(struct linedisp *linedisp, struct device *parent, 39 + unsigned int num_chars, char *buf, 40 + void (*update)(struct linedisp *linedisp)); 41 + void linedisp_unregister(struct linedisp *linedisp); 42 + 43 + #endif /* LINEDISP_H */