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

Configure Feed

Select the types of activity you want to include in your feed.

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
...

+888 -338
+2 -2
Documentation/devicetree/bindings/auxdisplay/arm,versatile-lcd.yaml
··· 39 39 examples: 40 40 - | 41 41 lcd@10008000 { 42 - compatible = "arm,versatile-lcd"; 43 - reg = <0x10008000 0x1000>; 42 + compatible = "arm,versatile-lcd"; 43 + reg = <0x10008000 0x1000>; 44 44 };
+55
Documentation/devicetree/bindings/auxdisplay/gpio-7-segment.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/auxdisplay/gpio-7-segment.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: GPIO based LED segment display 8 + 9 + maintainers: 10 + - Chris Packham <chris.packham@alliedtelesis.co.nz> 11 + 12 + properties: 13 + compatible: 14 + const: gpio-7-segment 15 + 16 + segment-gpios: 17 + description: | 18 + An array of GPIOs one per segment. The first GPIO corresponds to the A 19 + segment, the seventh GPIO corresponds to the G segment. Some LED blocks 20 + also have a decimal point which can be specified as an optional eighth 21 + segment. 22 + 23 + -a- 24 + | | 25 + f b 26 + | | 27 + -g- 28 + | | 29 + e c 30 + | | 31 + -d- dp 32 + 33 + minItems: 7 34 + maxItems: 8 35 + 36 + required: 37 + - segment-gpios 38 + 39 + additionalProperties: false 40 + 41 + examples: 42 + - | 43 + 44 + #include <dt-bindings/gpio/gpio.h> 45 + 46 + led-7seg { 47 + compatible = "gpio-7-segment"; 48 + segment-gpios = <&gpio 0 GPIO_ACTIVE_LOW>, 49 + <&gpio 1 GPIO_ACTIVE_LOW>, 50 + <&gpio 2 GPIO_ACTIVE_LOW>, 51 + <&gpio 3 GPIO_ACTIVE_LOW>, 52 + <&gpio 4 GPIO_ACTIVE_LOW>, 53 + <&gpio 5 GPIO_ACTIVE_LOW>, 54 + <&gpio 6 GPIO_ACTIVE_LOW>; 55 + };
+32 -30
Documentation/devicetree/bindings/auxdisplay/hit,hd44780.yaml
··· 84 84 examples: 85 85 - | 86 86 #include <dt-bindings/gpio/gpio.h> 87 - auxdisplay { 88 - compatible = "hit,hd44780"; 87 + display-controller { 88 + compatible = "hit,hd44780"; 89 89 90 - data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>, 91 - <&hc595 1 GPIO_ACTIVE_HIGH>, 92 - <&hc595 2 GPIO_ACTIVE_HIGH>, 93 - <&hc595 3 GPIO_ACTIVE_HIGH>; 94 - enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>; 95 - rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>; 90 + data-gpios = <&hc595 0 GPIO_ACTIVE_HIGH>, 91 + <&hc595 1 GPIO_ACTIVE_HIGH>, 92 + <&hc595 2 GPIO_ACTIVE_HIGH>, 93 + <&hc595 3 GPIO_ACTIVE_HIGH>; 94 + enable-gpios = <&hc595 4 GPIO_ACTIVE_HIGH>; 95 + rs-gpios = <&hc595 5 GPIO_ACTIVE_HIGH>; 96 96 97 - display-height-chars = <2>; 98 - display-width-chars = <16>; 97 + display-height-chars = <2>; 98 + display-width-chars = <16>; 99 99 }; 100 + 100 101 - | 101 102 #include <dt-bindings/gpio/gpio.h> 102 103 i2c { 103 - #address-cells = <1>; 104 - #size-cells = <0>; 104 + #address-cells = <1>; 105 + #size-cells = <0>; 105 106 106 - pcf8574: pcf8574@27 { 107 - compatible = "nxp,pcf8574"; 108 - reg = <0x27>; 109 - gpio-controller; 110 - #gpio-cells = <2>; 111 - }; 107 + pcf8574: gpio-expander@27 { 108 + compatible = "nxp,pcf8574"; 109 + reg = <0x27>; 110 + gpio-controller; 111 + #gpio-cells = <2>; 112 + }; 112 113 }; 113 - hd44780 { 114 - compatible = "hit,hd44780"; 115 - display-height-chars = <2>; 116 - display-width-chars = <16>; 117 - data-gpios = <&pcf8574 4 0>, 118 - <&pcf8574 5 0>, 119 - <&pcf8574 6 0>, 120 - <&pcf8574 7 0>; 121 - enable-gpios = <&pcf8574 2 0>; 122 - rs-gpios = <&pcf8574 0 0>; 123 - rw-gpios = <&pcf8574 1 0>; 124 - backlight-gpios = <&pcf8574 3 0>; 114 + 115 + display-controller { 116 + compatible = "hit,hd44780"; 117 + display-height-chars = <2>; 118 + display-width-chars = <16>; 119 + data-gpios = <&pcf8574 4 GPIO_ACTIVE_HIGH>, 120 + <&pcf8574 5 GPIO_ACTIVE_HIGH>, 121 + <&pcf8574 6 GPIO_ACTIVE_HIGH>, 122 + <&pcf8574 7 GPIO_ACTIVE_HIGH>; 123 + enable-gpios = <&pcf8574 2 GPIO_ACTIVE_HIGH>; 124 + rs-gpios = <&pcf8574 0 GPIO_ACTIVE_HIGH>; 125 + rw-gpios = <&pcf8574 1 GPIO_ACTIVE_HIGH>; 126 + backlight-gpios = <&pcf8574 3 GPIO_ACTIVE_HIGH>; 125 127 };
+25 -25
Documentation/devicetree/bindings/auxdisplay/holtek,ht16k33.yaml
··· 74 74 #include <dt-bindings/input/input.h> 75 75 #include <dt-bindings/leds/common.h> 76 76 i2c { 77 - #address-cells = <1>; 78 - #size-cells = <0>; 77 + #address-cells = <1>; 78 + #size-cells = <0>; 79 79 80 - ht16k33: ht16k33@70 { 81 - compatible = "holtek,ht16k33"; 82 - reg = <0x70>; 83 - refresh-rate-hz = <20>; 84 - interrupt-parent = <&gpio4>; 85 - interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>; 86 - debounce-delay-ms = <50>; 87 - linux,keymap = <MATRIX_KEY(2, 0, KEY_F6)>, 88 - <MATRIX_KEY(3, 0, KEY_F8)>, 89 - <MATRIX_KEY(4, 0, KEY_F10)>, 90 - <MATRIX_KEY(5, 0, KEY_F4)>, 91 - <MATRIX_KEY(6, 0, KEY_F2)>, 92 - <MATRIX_KEY(2, 1, KEY_F5)>, 93 - <MATRIX_KEY(3, 1, KEY_F7)>, 94 - <MATRIX_KEY(4, 1, KEY_F9)>, 95 - <MATRIX_KEY(5, 1, KEY_F3)>, 96 - <MATRIX_KEY(6, 1, KEY_F1)>; 80 + display-controller@70 { 81 + compatible = "holtek,ht16k33"; 82 + reg = <0x70>; 83 + refresh-rate-hz = <20>; 84 + interrupt-parent = <&gpio4>; 85 + interrupts = <5 (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING)>; 86 + debounce-delay-ms = <50>; 87 + linux,keymap = <MATRIX_KEY(2, 0, KEY_F6)>, 88 + <MATRIX_KEY(3, 0, KEY_F8)>, 89 + <MATRIX_KEY(4, 0, KEY_F10)>, 90 + <MATRIX_KEY(5, 0, KEY_F4)>, 91 + <MATRIX_KEY(6, 0, KEY_F2)>, 92 + <MATRIX_KEY(2, 1, KEY_F5)>, 93 + <MATRIX_KEY(3, 1, KEY_F7)>, 94 + <MATRIX_KEY(4, 1, KEY_F9)>, 95 + <MATRIX_KEY(5, 1, KEY_F3)>, 96 + <MATRIX_KEY(6, 1, KEY_F1)>; 97 97 98 - led { 99 - color = <LED_COLOR_ID_RED>; 100 - function = LED_FUNCTION_BACKLIGHT; 101 - linux,default-trigger = "backlight"; 102 - }; 98 + led { 99 + color = <LED_COLOR_ID_RED>; 100 + function = LED_FUNCTION_BACKLIGHT; 101 + linux,default-trigger = "backlight"; 103 102 }; 104 - }; 103 + }; 104 + };
+2 -2
Documentation/devicetree/bindings/auxdisplay/img,ascii-lcd.yaml
··· 50 50 examples: 51 51 - | 52 52 lcd: lcd@17fff000 { 53 - compatible = "img,boston-lcd"; 54 - reg = <0x17fff000 0x8>; 53 + compatible = "img,boston-lcd"; 54 + reg = <0x17fff000 0x8>; 55 55 };
+44
Documentation/devicetree/bindings/auxdisplay/maxim,max6959.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/auxdisplay/maxim,max6959.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: MAX6958/6959 7-segment LED display controller 8 + 9 + maintainers: 10 + - Andy Shevchenko <andriy.shevchenko@linux.intel.com> 11 + 12 + description: 13 + The Maxim MAX6958/6959 7-segment LED display controller provides 14 + an I2C interface to up to four 7-segment LED digits. The MAX6959, 15 + in comparison to MAX6958, adds input support. Type of the chip can 16 + be autodetected via specific register read, and hence the features 17 + may be enabled in the driver at run-time, in case they are requested 18 + via Device Tree. A given hardware is simple and does not provide 19 + any additional pins, such as reset or power enable. 20 + 21 + properties: 22 + compatible: 23 + const: maxim,max6959 24 + 25 + reg: 26 + maxItems: 1 27 + 28 + required: 29 + - compatible 30 + - reg 31 + 32 + additionalProperties: false 33 + 34 + examples: 35 + - | 36 + i2c { 37 + #address-cells = <1>; 38 + #size-cells = <0>; 39 + 40 + display-controller@38 { 41 + compatible = "maxim,max6959"; 42 + reg = <0x38>; 43 + }; 44 + };
+6 -2
MAINTAINERS
··· 3389 3389 F: include/linux/auxiliary_bus.h 3390 3390 3391 3391 AUXILIARY DISPLAY DRIVERS 3392 - M: Miguel Ojeda <ojeda@kernel.org> 3393 - S: Maintained 3392 + M: Andy Shevchenko <andy@kernel.org> 3393 + R: Geert Uytterhoeven <geert@linux-m68k.org> 3394 + S: Odd Fixes 3395 + T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-auxdisplay.git 3394 3396 F: Documentation/devicetree/bindings/auxdisplay/ 3395 3397 F: drivers/auxdisplay/ 3396 3398 F: include/linux/cfag12864b.h 3399 + F: include/uapi/linux/map_to_14segment.h 3400 + F: include/uapi/linux/map_to_7segment.h 3397 3401 3398 3402 AVIA HX711 ANALOG DIGITAL CONVERTER IIO DRIVER 3399 3403 M: Andreas Klinger <ak@it-klinger.de>
+25
drivers/auxdisplay/Kconfig
··· 177 177 Say yes here to add support for Holtek HT16K33, RAM mapping 16*8 178 178 LED controller driver with keyscan. 179 179 180 + config MAX6959 181 + tristate "Maxim MAX6958/6959 7-segment LED controller" 182 + depends on I2C 183 + select REGMAP_I2C 184 + select LINEDISP 185 + help 186 + If you say yes here you get support for the following Maxim chips 187 + (I2C 7-segment LED display controller): 188 + - MAX6958 189 + - MAX6959 (input support) 190 + 191 + This driver can also be built as a module. If so, the module 192 + will be called max6959. 193 + 180 194 config LCD2S 181 195 tristate "lcd2s 20x4 character display over I2C console" 182 196 depends on I2C ··· 210 196 very much more than display the text "ARM Linux" on the first 211 197 line and the Linux version on the second line, but that's 212 198 still useful. 199 + 200 + config SEG_LED_GPIO 201 + tristate "Generic 7-segment LED display" 202 + depends on GPIOLIB || COMPILE_TEST 203 + select LINEDISP 204 + help 205 + This driver supports a generic 7-segment LED display made up 206 + of GPIO pins connected to the individual segments. 207 + 208 + This driver can also be built as a module. If so, the module 209 + will be called seg-led-gpio. 213 210 214 211 menuconfig PARPORT_PANEL 215 212 tristate "Parallel port LCD/Keypad Panel support"
+2
drivers/auxdisplay/Makefile
··· 14 14 obj-$(CONFIG_PARPORT_PANEL) += panel.o 15 15 obj-$(CONFIG_LCD2S) += lcd2s.o 16 16 obj-$(CONFIG_LINEDISP) += line-display.o 17 + obj-$(CONFIG_MAX6959) += max6959.o 18 + obj-$(CONFIG_SEG_LED_GPIO) += seg-led-gpio.o
+2 -4
drivers/auxdisplay/cfag12864bfb.c
··· 96 96 return ret; 97 97 } 98 98 99 - static int cfag12864bfb_remove(struct platform_device *device) 99 + static void cfag12864bfb_remove(struct platform_device *device) 100 100 { 101 101 struct fb_info *info = platform_get_drvdata(device); 102 102 ··· 104 104 unregister_framebuffer(info); 105 105 framebuffer_release(info); 106 106 } 107 - 108 - return 0; 109 107 } 110 108 111 109 static struct platform_driver cfag12864bfb_driver = { 112 110 .probe = cfag12864bfb_probe, 113 - .remove = cfag12864bfb_remove, 111 + .remove_new = cfag12864bfb_remove, 114 112 .driver = { 115 113 .name = CFAG12864BFB_NAME, 116 114 },
+2 -3
drivers/auxdisplay/hd44780.c
··· 319 319 return ret; 320 320 } 321 321 322 - static int hd44780_remove(struct platform_device *pdev) 322 + static void hd44780_remove(struct platform_device *pdev) 323 323 { 324 324 struct charlcd *lcd = platform_get_drvdata(pdev); 325 325 struct hd44780_common *hdc = lcd->drvdata; ··· 329 329 kfree(lcd->drvdata); 330 330 331 331 kfree(lcd); 332 - return 0; 333 332 } 334 333 335 334 static const struct of_device_id hd44780_of_match[] = { ··· 339 340 340 341 static struct platform_driver hd44780_driver = { 341 342 .probe = hd44780_probe, 342 - .remove = hd44780_remove, 343 + .remove_new = hd44780_remove, 343 344 .driver = { 344 345 .name = "hd44780", 345 346 .of_match_table = hd44780_of_match,
+68 -106
drivers/auxdisplay/ht16k33.c
··· 15 15 #include <linux/property.h> 16 16 #include <linux/fb.h> 17 17 #include <linux/backlight.h> 18 + #include <linux/container_of.h> 18 19 #include <linux/input.h> 19 20 #include <linux/input/matrix_keypad.h> 20 21 #include <linux/leds.h> ··· 86 85 uint8_t *cache; 87 86 }; 88 87 89 - struct ht16k33_seg { 90 - struct linedisp linedisp; 91 - union { 92 - struct seg7_conversion_map seg7; 93 - struct seg14_conversion_map seg14; 94 - } map; 95 - unsigned int map_size; 96 - char curr[4]; 97 - }; 98 - 99 88 struct ht16k33_priv { 100 89 struct i2c_client *client; 101 90 struct delayed_work work; ··· 93 102 struct ht16k33_keypad keypad; 94 103 union { 95 104 struct ht16k33_fbdev fbdev; 96 - struct ht16k33_seg seg; 105 + struct linedisp linedisp; 97 106 }; 98 107 enum display_type type; 99 108 uint8_t blink; 100 109 }; 110 + 111 + #define ht16k33_work_to_priv(p) \ 112 + container_of(p, struct ht16k33_priv, work.work) 113 + 114 + #define ht16k33_led_to_priv(p) \ 115 + container_of(p, struct ht16k33_priv, led) 116 + 117 + #define ht16k33_linedisp_to_priv(p) \ 118 + container_of(p, struct ht16k33_priv, linedisp) 101 119 102 120 static const struct fb_fix_screeninfo ht16k33_fb_fix = { 103 121 .id = DRIVER_NAME, ··· 134 134 .lower_margin = 0, 135 135 .vmode = FB_VMODE_NONINTERLACED, 136 136 }; 137 - 138 - static const SEG7_DEFAULT_MAP(initial_map_seg7); 139 - static const SEG14_DEFAULT_MAP(initial_map_seg14); 140 - 141 - static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, 142 - char *buf) 143 - { 144 - struct ht16k33_priv *priv = dev_get_drvdata(dev); 145 - 146 - memcpy(buf, &priv->seg.map, priv->seg.map_size); 147 - return priv->seg.map_size; 148 - } 149 - 150 - static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr, 151 - const char *buf, size_t cnt) 152 - { 153 - struct ht16k33_priv *priv = dev_get_drvdata(dev); 154 - 155 - if (cnt != priv->seg.map_size) 156 - return -EINVAL; 157 - 158 - memcpy(&priv->seg.map, buf, cnt); 159 - return cnt; 160 - } 161 - 162 - static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store); 163 - static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store); 164 137 165 138 static int ht16k33_display_on(struct ht16k33_priv *priv) 166 139 { ··· 168 195 static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev, 169 196 enum led_brightness brightness) 170 197 { 171 - struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv, 172 - led); 198 + struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev); 173 199 174 200 return ht16k33_brightness_set(priv, brightness); 175 201 } ··· 176 204 static int ht16k33_blink_set(struct led_classdev *led_cdev, 177 205 unsigned long *delay_on, unsigned long *delay_off) 178 206 { 179 - struct ht16k33_priv *priv = container_of(led_cdev, struct ht16k33_priv, 180 - led); 207 + struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev); 181 208 unsigned int delay; 182 209 uint8_t blink; 183 210 int err; ··· 218 247 */ 219 248 static void ht16k33_fb_update(struct work_struct *work) 220 249 { 221 - struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv, 222 - work.work); 250 + struct ht16k33_priv *priv = ht16k33_work_to_priv(work); 223 251 struct ht16k33_fbdev *fbdev = &priv->fbdev; 224 252 225 253 uint8_t *p1, *p2; ··· 410 440 disable_irq(keypad->client->irq); 411 441 } 412 442 413 - static void ht16k33_linedisp_update(struct linedisp *linedisp) 414 - { 415 - struct ht16k33_priv *priv = container_of(linedisp, struct ht16k33_priv, 416 - seg.linedisp); 417 - 418 - schedule_delayed_work(&priv->work, 0); 419 - } 420 - 421 443 static void ht16k33_seg7_update(struct work_struct *work) 422 444 { 423 - struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv, 424 - work.work); 425 - struct ht16k33_seg *seg = &priv->seg; 426 - char *s = seg->curr; 445 + struct ht16k33_priv *priv = ht16k33_work_to_priv(work); 446 + struct linedisp_map *map = priv->linedisp.map; 447 + char *s = priv->linedisp.buf; 427 448 uint8_t buf[9]; 428 449 429 - buf[0] = map_to_seg7(&seg->map.seg7, *s++); 450 + buf[0] = map_to_seg7(&map->map.seg7, *s++); 430 451 buf[1] = 0; 431 - buf[2] = map_to_seg7(&seg->map.seg7, *s++); 452 + buf[2] = map_to_seg7(&map->map.seg7, *s++); 432 453 buf[3] = 0; 433 454 buf[4] = 0; 434 455 buf[5] = 0; 435 - buf[6] = map_to_seg7(&seg->map.seg7, *s++); 456 + buf[6] = map_to_seg7(&map->map.seg7, *s++); 436 457 buf[7] = 0; 437 - buf[8] = map_to_seg7(&seg->map.seg7, *s++); 458 + buf[8] = map_to_seg7(&map->map.seg7, *s++); 438 459 439 460 i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf); 440 461 } 441 462 442 463 static void ht16k33_seg14_update(struct work_struct *work) 443 464 { 444 - struct ht16k33_priv *priv = container_of(work, struct ht16k33_priv, 445 - work.work); 446 - struct ht16k33_seg *seg = &priv->seg; 447 - char *s = seg->curr; 465 + struct ht16k33_priv *priv = ht16k33_work_to_priv(work); 466 + struct linedisp_map *map = priv->linedisp.map; 467 + char *s = priv->linedisp.buf; 448 468 uint8_t buf[8]; 449 469 450 - put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf); 451 - put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 2); 452 - put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 4); 453 - put_unaligned_le16(map_to_seg14(&seg->map.seg14, *s++), buf + 6); 470 + put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 0); 471 + put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 2); 472 + put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 4); 473 + put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 6); 454 474 455 475 i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf); 456 476 } 477 + 478 + static int ht16k33_linedisp_get_map_type(struct linedisp *linedisp) 479 + { 480 + struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp); 481 + 482 + switch (priv->type) { 483 + case DISP_QUAD_7SEG: 484 + INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update); 485 + return LINEDISP_MAP_SEG7; 486 + 487 + case DISP_QUAD_14SEG: 488 + INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update); 489 + return LINEDISP_MAP_SEG14; 490 + 491 + default: 492 + return -EINVAL; 493 + } 494 + } 495 + 496 + static void ht16k33_linedisp_update(struct linedisp *linedisp) 497 + { 498 + struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp); 499 + 500 + schedule_delayed_work(&priv->work, 0); 501 + } 502 + 503 + static const struct linedisp_ops ht16k33_linedisp_ops = { 504 + .get_map_type = ht16k33_linedisp_get_map_type, 505 + .update = ht16k33_linedisp_update, 506 + }; 457 507 458 508 static int ht16k33_led_probe(struct device *dev, struct led_classdev *led, 459 509 unsigned int brightness) ··· 656 666 static int ht16k33_seg_probe(struct device *dev, struct ht16k33_priv *priv, 657 667 uint32_t brightness) 658 668 { 659 - struct ht16k33_seg *seg = &priv->seg; 669 + struct linedisp *linedisp = &priv->linedisp; 660 670 int err; 661 671 662 672 err = ht16k33_brightness_set(priv, brightness); 663 673 if (err) 664 674 return err; 665 675 666 - switch (priv->type) { 667 - case DISP_MATRIX: 668 - /* not handled here */ 669 - err = -EINVAL; 670 - break; 671 - 672 - case DISP_QUAD_7SEG: 673 - INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update); 674 - seg->map.seg7 = initial_map_seg7; 675 - seg->map_size = sizeof(seg->map.seg7); 676 - err = device_create_file(dev, &dev_attr_map_seg7); 677 - break; 678 - 679 - case DISP_QUAD_14SEG: 680 - INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update); 681 - seg->map.seg14 = initial_map_seg14; 682 - seg->map_size = sizeof(seg->map.seg14); 683 - err = device_create_file(dev, &dev_attr_map_seg14); 684 - break; 685 - } 686 - if (err) 687 - return err; 688 - 689 - err = linedisp_register(&seg->linedisp, dev, 4, seg->curr, 690 - ht16k33_linedisp_update); 691 - if (err) 692 - goto err_remove_map_file; 693 - 694 - return 0; 695 - 696 - err_remove_map_file: 697 - device_remove_file(dev, &dev_attr_map_seg7); 698 - device_remove_file(dev, &dev_attr_map_seg14); 699 - return err; 676 + return linedisp_register(linedisp, dev, 4, &ht16k33_linedisp_ops); 700 677 } 701 678 702 679 static int ht16k33_probe(struct i2c_client *client) ··· 727 770 /* Segment Display */ 728 771 err = ht16k33_seg_probe(dev, priv, dft_brightness); 729 772 break; 773 + 774 + default: 775 + return -EINVAL; 730 776 } 731 777 return err; 732 778 } ··· 750 790 751 791 case DISP_QUAD_7SEG: 752 792 case DISP_QUAD_14SEG: 753 - linedisp_unregister(&priv->seg.linedisp); 754 - device_remove_file(&client->dev, &dev_attr_map_seg7); 755 - device_remove_file(&client->dev, &dev_attr_map_seg14); 793 + linedisp_unregister(&priv->linedisp); 794 + break; 795 + 796 + default: 756 797 break; 757 798 } 758 799 } ··· 792 831 793 832 MODULE_DESCRIPTION("Holtek HT16K33 driver"); 794 833 MODULE_LICENSE("GPL"); 834 + MODULE_IMPORT_NS(LINEDISP); 795 835 MODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");
+23 -22
drivers/auxdisplay/img-ascii-lcd.c
··· 22 22 * struct img_ascii_lcd_config - Configuration information about an LCD model 23 23 * @num_chars: the number of characters the LCD can display 24 24 * @external_regmap: true if registers are in a system controller, else false 25 - * @update: function called to update the LCD 25 + * @ops: character line display operations 26 26 */ 27 27 struct img_ascii_lcd_config { 28 28 unsigned int num_chars; 29 29 bool external_regmap; 30 - void (*update)(struct linedisp *linedisp); 30 + const struct linedisp_ops ops; 31 31 }; 32 32 33 33 /** 34 34 * struct img_ascii_lcd_ctx - Private data structure 35 + * @linedisp: line display structure 35 36 * @base: the base address of the LCD registers 36 37 * @regmap: the regmap through which LCD registers are accessed 37 38 * @offset: the offset within regmap to the start of the LCD registers 38 39 * @cfg: pointer to the LCD model configuration 39 - * @linedisp: line display structure 40 - * @curr: the string currently displayed on the LCD 41 40 */ 42 41 struct img_ascii_lcd_ctx { 42 + struct linedisp linedisp; 43 43 union { 44 44 void __iomem *base; 45 45 struct regmap *regmap; 46 46 }; 47 47 u32 offset; 48 48 const struct img_ascii_lcd_config *cfg; 49 - struct linedisp linedisp; 50 - char curr[] __aligned(8); 51 49 }; 52 50 53 51 /* ··· 59 61 ulong val; 60 62 61 63 #if BITS_PER_LONG == 64 62 - val = *((u64 *)&ctx->curr[0]); 64 + val = *((u64 *)&linedisp->buf[0]); 63 65 __raw_writeq(val, ctx->base); 64 66 #elif BITS_PER_LONG == 32 65 - val = *((u32 *)&ctx->curr[0]); 67 + val = *((u32 *)&linedisp->buf[0]); 66 68 __raw_writel(val, ctx->base); 67 - val = *((u32 *)&ctx->curr[4]); 69 + val = *((u32 *)&linedisp->buf[4]); 68 70 __raw_writel(val, ctx->base + 4); 69 71 #else 70 72 # error Not 32 or 64 bit ··· 73 75 74 76 static struct img_ascii_lcd_config boston_config = { 75 77 .num_chars = 8, 76 - .update = boston_update, 78 + .ops = { 79 + .update = boston_update, 80 + }, 77 81 }; 78 82 79 83 /* ··· 91 91 92 92 for (i = 0; i < linedisp->num_chars; i++) { 93 93 err = regmap_write(ctx->regmap, 94 - ctx->offset + (i * 8), ctx->curr[i]); 94 + ctx->offset + (i * 8), linedisp->buf[i]); 95 95 if (err) 96 96 break; 97 97 } ··· 103 103 static struct img_ascii_lcd_config malta_config = { 104 104 .num_chars = 8, 105 105 .external_regmap = true, 106 - .update = malta_update, 106 + .ops = { 107 + .update = malta_update, 108 + }, 107 109 }; 108 110 109 111 /* ··· 193 191 194 192 err = regmap_write(ctx->regmap, 195 193 ctx->offset + SEAD3_REG_LCD_DATA, 196 - ctx->curr[i]); 194 + linedisp->buf[i]); 197 195 if (err) 198 196 break; 199 197 } ··· 205 203 static struct img_ascii_lcd_config sead3_config = { 206 204 .num_chars = 16, 207 205 .external_regmap = true, 208 - .update = sead3_update, 206 + .ops = { 207 + .update = sead3_update, 208 + }, 209 209 }; 210 210 211 211 static const struct of_device_id img_ascii_lcd_matches[] = { ··· 234 230 struct img_ascii_lcd_ctx *ctx; 235 231 int err; 236 232 237 - ctx = devm_kzalloc(dev, sizeof(*ctx) + cfg->num_chars, GFP_KERNEL); 233 + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 238 234 if (!ctx) 239 235 return -ENOMEM; 240 236 ··· 251 247 return PTR_ERR(ctx->base); 252 248 } 253 249 254 - err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, ctx->curr, 255 - cfg->update); 250 + err = linedisp_register(&ctx->linedisp, dev, cfg->num_chars, &cfg->ops); 256 251 if (err) 257 252 return err; 258 253 ··· 276 273 * 277 274 * Remove an LCD display device, freeing private resources & ensuring that the 278 275 * driver stops using the LCD display registers. 279 - * 280 - * Return: 0 281 276 */ 282 - static int img_ascii_lcd_remove(struct platform_device *pdev) 277 + static void img_ascii_lcd_remove(struct platform_device *pdev) 283 278 { 284 279 struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev); 285 280 286 281 sysfs_remove_link(&pdev->dev.kobj, "message"); 287 282 linedisp_unregister(&ctx->linedisp); 288 - return 0; 289 283 } 290 284 291 285 static struct platform_driver img_ascii_lcd_driver = { ··· 291 291 .of_match_table = img_ascii_lcd_matches, 292 292 }, 293 293 .probe = img_ascii_lcd_probe, 294 - .remove = img_ascii_lcd_remove, 294 + .remove_new = img_ascii_lcd_remove, 295 295 }; 296 296 module_platform_driver(img_ascii_lcd_driver); 297 297 298 298 MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display"); 299 299 MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>"); 300 300 MODULE_LICENSE("GPL"); 301 + MODULE_IMPORT_NS(LINEDISP);
+148 -18
drivers/auxdisplay/line-display.c
··· 10 10 11 11 #include <generated/utsrelease.h> 12 12 13 + #include <linux/container_of.h> 13 14 #include <linux/device.h> 15 + #include <linux/export.h> 16 + #include <linux/idr.h> 17 + #include <linux/jiffies.h> 18 + #include <linux/kstrtox.h> 14 19 #include <linux/module.h> 15 20 #include <linux/slab.h> 16 21 #include <linux/string.h> 17 22 #include <linux/sysfs.h> 18 23 #include <linux/timer.h> 24 + 25 + #include <linux/map_to_7segment.h> 26 + #include <linux/map_to_14segment.h> 19 27 20 28 #include "line-display.h" 21 29 ··· 53 45 } 54 46 55 47 /* update the display */ 56 - linedisp->update(linedisp); 48 + linedisp->ops->update(linedisp); 57 49 58 50 /* move on to the next character */ 59 51 linedisp->scroll_pos++; ··· 97 89 linedisp->message = NULL; 98 90 linedisp->message_len = 0; 99 91 memset(linedisp->buf, ' ', linedisp->num_chars); 100 - linedisp->update(linedisp); 92 + linedisp->ops->update(linedisp); 101 93 return 0; 102 94 } 103 95 ··· 173 165 { 174 166 struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 175 167 unsigned int ms; 168 + int err; 176 169 177 - if (kstrtouint(buf, 10, &ms) != 0) 178 - return -EINVAL; 170 + err = kstrtouint(buf, 10, &ms); 171 + if (err) 172 + return err; 179 173 180 174 linedisp->scroll_rate = msecs_to_jiffies(ms); 181 175 if (linedisp->message && linedisp->message_len > linedisp->num_chars) { ··· 191 181 192 182 static DEVICE_ATTR_RW(scroll_step_ms); 193 183 184 + static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf) 185 + { 186 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 187 + struct linedisp_map *map = linedisp->map; 188 + 189 + memcpy(buf, &map->map, map->size); 190 + return map->size; 191 + } 192 + 193 + static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr, 194 + const char *buf, size_t count) 195 + { 196 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 197 + struct linedisp_map *map = linedisp->map; 198 + 199 + if (count != map->size) 200 + return -EINVAL; 201 + 202 + memcpy(&map->map, buf, count); 203 + return count; 204 + } 205 + 206 + static const SEG7_DEFAULT_MAP(initial_map_seg7); 207 + static DEVICE_ATTR(map_seg7, 0644, map_seg_show, map_seg_store); 208 + 209 + static const SEG14_DEFAULT_MAP(initial_map_seg14); 210 + static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store); 211 + 194 212 static struct attribute *linedisp_attrs[] = { 195 213 &dev_attr_message.attr, 196 214 &dev_attr_scroll_step_ms.attr, 197 - NULL, 215 + &dev_attr_map_seg7.attr, 216 + &dev_attr_map_seg14.attr, 217 + NULL 198 218 }; 199 - ATTRIBUTE_GROUPS(linedisp); 219 + 220 + static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) 221 + { 222 + struct device *dev = kobj_to_dev(kobj); 223 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 224 + struct linedisp_map *map = linedisp->map; 225 + umode_t mode = attr->mode; 226 + 227 + if (attr == &dev_attr_map_seg7.attr) { 228 + if (!map) 229 + return 0; 230 + if (map->type != LINEDISP_MAP_SEG7) 231 + return 0; 232 + } 233 + 234 + if (attr == &dev_attr_map_seg14.attr) { 235 + if (!map) 236 + return 0; 237 + if (map->type != LINEDISP_MAP_SEG14) 238 + return 0; 239 + } 240 + 241 + return mode; 242 + }; 243 + 244 + static const struct attribute_group linedisp_group = { 245 + .is_visible = linedisp_attr_is_visible, 246 + .attrs = linedisp_attrs, 247 + }; 248 + __ATTRIBUTE_GROUPS(linedisp); 249 + 250 + static DEFINE_IDA(linedisp_id); 251 + 252 + static void linedisp_release(struct device *dev) 253 + { 254 + struct linedisp *linedisp = container_of(dev, struct linedisp, dev); 255 + 256 + kfree(linedisp->map); 257 + kfree(linedisp->message); 258 + kfree(linedisp->buf); 259 + ida_free(&linedisp_id, linedisp->id); 260 + } 200 261 201 262 static const struct device_type linedisp_type = { 202 263 .groups = linedisp_groups, 264 + .release = linedisp_release, 203 265 }; 266 + 267 + static int linedisp_init_map(struct linedisp *linedisp) 268 + { 269 + struct linedisp_map *map; 270 + int err; 271 + 272 + if (!linedisp->ops->get_map_type) 273 + return 0; 274 + 275 + err = linedisp->ops->get_map_type(linedisp); 276 + if (err < 0) 277 + return err; 278 + 279 + map = kmalloc(sizeof(*map), GFP_KERNEL); 280 + if (!map) 281 + return -ENOMEM; 282 + 283 + map->type = err; 284 + 285 + /* assign initial mapping */ 286 + switch (map->type) { 287 + case LINEDISP_MAP_SEG7: 288 + map->map.seg7 = initial_map_seg7; 289 + map->size = sizeof(map->map.seg7); 290 + break; 291 + case LINEDISP_MAP_SEG14: 292 + map->map.seg14 = initial_map_seg14; 293 + map->size = sizeof(map->map.seg14); 294 + break; 295 + default: 296 + kfree(map); 297 + return -EINVAL; 298 + } 299 + 300 + linedisp->map = map; 301 + 302 + return 0; 303 + } 204 304 205 305 /** 206 306 * linedisp_register - register a character line display 207 307 * @linedisp: pointer to character line display structure 208 308 * @parent: parent device 209 309 * @num_chars: the number of characters that can be displayed 210 - * @buf: pointer to a buffer that can hold @num_chars characters 211 - * @update: Function called to update the display. This must not sleep! 310 + * @ops: character line display operations 212 311 * 213 312 * Return: zero on success, else a negative error code. 214 313 */ 215 314 int linedisp_register(struct linedisp *linedisp, struct device *parent, 216 - unsigned int num_chars, char *buf, 217 - void (*update)(struct linedisp *linedisp)) 315 + unsigned int num_chars, const struct linedisp_ops *ops) 218 316 { 219 - static atomic_t linedisp_id = ATOMIC_INIT(-1); 220 317 int err; 221 318 222 319 memset(linedisp, 0, sizeof(*linedisp)); 223 320 linedisp->dev.parent = parent; 224 321 linedisp->dev.type = &linedisp_type; 225 - linedisp->update = update; 226 - linedisp->buf = buf; 322 + linedisp->ops = ops; 227 323 linedisp->num_chars = num_chars; 228 324 linedisp->scroll_rate = DEFAULT_SCROLL_RATE; 229 325 326 + err = ida_alloc(&linedisp_id, GFP_KERNEL); 327 + if (err < 0) 328 + return err; 329 + linedisp->id = err; 330 + 230 331 device_initialize(&linedisp->dev); 231 - dev_set_name(&linedisp->dev, "linedisp.%lu", 232 - (unsigned long)atomic_inc_return(&linedisp_id)); 332 + dev_set_name(&linedisp->dev, "linedisp.%u", linedisp->id); 333 + 334 + err = -ENOMEM; 335 + linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL); 336 + if (!linedisp->buf) 337 + goto out_put_device; 338 + 339 + /* initialise a character mapping, if required */ 340 + err = linedisp_init_map(linedisp); 341 + if (err) 342 + goto out_put_device; 233 343 234 344 /* initialise a timer for scrolling the message */ 235 345 timer_setup(&linedisp->timer, linedisp_scroll, 0); ··· 369 239 device_del(&linedisp->dev); 370 240 out_del_timer: 371 241 del_timer_sync(&linedisp->timer); 242 + out_put_device: 372 243 put_device(&linedisp->dev); 373 244 return err; 374 245 } 375 - EXPORT_SYMBOL_GPL(linedisp_register); 246 + EXPORT_SYMBOL_NS_GPL(linedisp_register, LINEDISP); 376 247 377 248 /** 378 249 * linedisp_unregister - unregister a character line display ··· 384 253 { 385 254 device_del(&linedisp->dev); 386 255 del_timer_sync(&linedisp->timer); 387 - kfree(linedisp->message); 388 256 put_device(&linedisp->dev); 389 257 } 390 - EXPORT_SYMBOL_GPL(linedisp_unregister); 258 + EXPORT_SYMBOL_NS_GPL(linedisp_unregister, LINEDISP); 391 259 392 260 MODULE_LICENSE("GPL");
+49 -4
drivers/auxdisplay/line-display.h
··· 11 11 #ifndef _LINEDISP_H 12 12 #define _LINEDISP_H 13 13 14 + #include <linux/device.h> 15 + #include <linux/timer_types.h> 16 + 17 + #include <linux/map_to_7segment.h> 18 + #include <linux/map_to_14segment.h> 19 + 20 + struct linedisp; 21 + 22 + /** 23 + * enum linedisp_map_type - type of the character mapping 24 + * @LINEDISP_MAP_SEG7: Map characters to 7 segment display 25 + * @LINEDISP_MAP_SEG14: Map characters to 14 segment display 26 + */ 27 + enum linedisp_map_type { 28 + LINEDISP_MAP_SEG7, 29 + LINEDISP_MAP_SEG14, 30 + }; 31 + 32 + /** 33 + * struct linedisp_map - character mapping 34 + * @type: type of the character mapping 35 + * @map: conversion character mapping 36 + * @size: size of the @map 37 + */ 38 + struct linedisp_map { 39 + enum linedisp_map_type type; 40 + union { 41 + struct seg7_conversion_map seg7; 42 + struct seg14_conversion_map seg14; 43 + } map; 44 + unsigned int size; 45 + }; 46 + 47 + /** 48 + * struct linedisp_ops - character line display operations 49 + * @get_map_type: Function called to get the character mapping, if required 50 + * @update: Function called to update the display. This must not sleep! 51 + */ 52 + struct linedisp_ops { 53 + int (*get_map_type)(struct linedisp *linedisp); 54 + void (*update)(struct linedisp *linedisp); 55 + }; 56 + 14 57 /** 15 58 * struct linedisp - character line display private data structure 16 59 * @dev: the line display device 17 60 * @timer: timer used to implement scrolling 18 - * @update: function called to update the display 61 + * @ops: character line display operations 19 62 * @buf: pointer to the buffer for the string currently displayed 20 63 * @message: the full message to display or scroll on the display 21 64 * @num_chars: the number of characters that can be displayed 22 65 * @message_len: the length of the @message string 23 66 * @scroll_pos: index of the first character of @message currently displayed 24 67 * @scroll_rate: scroll interval in jiffies 68 + * @id: instance id of this display 25 69 */ 26 70 struct linedisp { 27 71 struct device dev; 28 72 struct timer_list timer; 29 - void (*update)(struct linedisp *linedisp); 73 + const struct linedisp_ops *ops; 74 + struct linedisp_map *map; 30 75 char *buf; 31 76 char *message; 32 77 unsigned int num_chars; 33 78 unsigned int message_len; 34 79 unsigned int scroll_pos; 35 80 unsigned int scroll_rate; 81 + unsigned int id; 36 82 }; 37 83 38 84 int linedisp_register(struct linedisp *linedisp, struct device *parent, 39 - unsigned int num_chars, char *buf, 40 - void (*update)(struct linedisp *linedisp)); 85 + unsigned int num_chars, const struct linedisp_ops *ops); 41 86 void linedisp_unregister(struct linedisp *linedisp); 42 87 43 88 #endif /* LINEDISP_H */
+194
drivers/auxdisplay/max6959.c
··· 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 <andriy.shevchenko@linux.intel.com> 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 <andriy.shevchenko@linux.intel.com>"); 193 + MODULE_LICENSE("GPL"); 194 + MODULE_IMPORT_NS(LINEDISP);
+96 -120
drivers/auxdisplay/panel.c
··· 1519 1519 1520 1520 static void panel_attach(struct parport *port) 1521 1521 { 1522 + int selected_keypad_type = NOT_SET; 1522 1523 struct pardev_cb panel_cb; 1524 + 1525 + /* take care of an eventual profile */ 1526 + switch (profile) { 1527 + case PANEL_PROFILE_CUSTOM: 1528 + /* custom profile */ 1529 + selected_keypad_type = DEFAULT_KEYPAD_TYPE; 1530 + selected_lcd_type = DEFAULT_LCD_TYPE; 1531 + break; 1532 + case PANEL_PROFILE_OLD: 1533 + /* 8 bits, 2*16, old keypad */ 1534 + selected_keypad_type = KEYPAD_TYPE_OLD; 1535 + selected_lcd_type = LCD_TYPE_OLD; 1536 + 1537 + /* TODO: This two are a little hacky, sort it out later */ 1538 + if (lcd_width == NOT_SET) 1539 + lcd_width = 16; 1540 + if (lcd_hwidth == NOT_SET) 1541 + lcd_hwidth = 16; 1542 + break; 1543 + case PANEL_PROFILE_NEW: 1544 + /* serial, 2*16, new keypad */ 1545 + selected_keypad_type = KEYPAD_TYPE_NEW; 1546 + selected_lcd_type = LCD_TYPE_KS0074; 1547 + break; 1548 + case PANEL_PROFILE_HANTRONIX: 1549 + /* 8 bits, 2*16 hantronix-like, no keypad */ 1550 + selected_keypad_type = KEYPAD_TYPE_NONE; 1551 + selected_lcd_type = LCD_TYPE_HANTRONIX; 1552 + break; 1553 + case PANEL_PROFILE_NEXCOM: 1554 + /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */ 1555 + selected_keypad_type = KEYPAD_TYPE_NEXCOM; 1556 + selected_lcd_type = LCD_TYPE_NEXCOM; 1557 + break; 1558 + case PANEL_PROFILE_LARGE: 1559 + /* 8 bits, 2*40, old keypad */ 1560 + selected_keypad_type = KEYPAD_TYPE_OLD; 1561 + selected_lcd_type = LCD_TYPE_OLD; 1562 + break; 1563 + } 1564 + 1565 + /* 1566 + * Overwrite selection with module param values (both keypad and lcd), 1567 + * where the deprecated params have lower prio. 1568 + */ 1569 + if (keypad_enabled != NOT_SET) 1570 + selected_keypad_type = keypad_enabled; 1571 + if (keypad_type != NOT_SET) 1572 + selected_keypad_type = keypad_type; 1573 + 1574 + keypad.enabled = (selected_keypad_type > 0); 1575 + 1576 + if (lcd_enabled != NOT_SET) 1577 + selected_lcd_type = lcd_enabled; 1578 + if (lcd_type != NOT_SET) 1579 + selected_lcd_type = lcd_type; 1580 + 1581 + lcd.enabled = (selected_lcd_type > 0); 1582 + 1583 + if (lcd.enabled) { 1584 + /* 1585 + * Init lcd struct with load-time values to preserve exact 1586 + * current functionality (at least for now). 1587 + */ 1588 + lcd.charset = lcd_charset; 1589 + lcd.proto = lcd_proto; 1590 + lcd.pins.e = lcd_e_pin; 1591 + lcd.pins.rs = lcd_rs_pin; 1592 + lcd.pins.rw = lcd_rw_pin; 1593 + lcd.pins.cl = lcd_cl_pin; 1594 + lcd.pins.da = lcd_da_pin; 1595 + lcd.pins.bl = lcd_bl_pin; 1596 + } 1597 + 1598 + switch (selected_keypad_type) { 1599 + case KEYPAD_TYPE_OLD: 1600 + keypad_profile = old_keypad_profile; 1601 + break; 1602 + case KEYPAD_TYPE_NEW: 1603 + keypad_profile = new_keypad_profile; 1604 + break; 1605 + case KEYPAD_TYPE_NEXCOM: 1606 + keypad_profile = nexcom_keypad_profile; 1607 + break; 1608 + default: 1609 + keypad_profile = NULL; 1610 + break; 1611 + } 1612 + 1613 + if (!lcd.enabled && !keypad.enabled) { 1614 + /* no device enabled, let's exit */ 1615 + pr_err("panel driver disabled.\n"); 1616 + return; 1617 + } 1523 1618 1524 1619 if (port->number != parport) 1525 1620 return; ··· 1708 1613 .detach = panel_detach, 1709 1614 .devmodel = true, 1710 1615 }; 1616 + module_parport_driver(panel_driver); 1711 1617 1712 - /* init function */ 1713 - static int __init panel_init_module(void) 1714 - { 1715 - int selected_keypad_type = NOT_SET, err; 1716 - 1717 - /* take care of an eventual profile */ 1718 - switch (profile) { 1719 - case PANEL_PROFILE_CUSTOM: 1720 - /* custom profile */ 1721 - selected_keypad_type = DEFAULT_KEYPAD_TYPE; 1722 - selected_lcd_type = DEFAULT_LCD_TYPE; 1723 - break; 1724 - case PANEL_PROFILE_OLD: 1725 - /* 8 bits, 2*16, old keypad */ 1726 - selected_keypad_type = KEYPAD_TYPE_OLD; 1727 - selected_lcd_type = LCD_TYPE_OLD; 1728 - 1729 - /* TODO: This two are a little hacky, sort it out later */ 1730 - if (lcd_width == NOT_SET) 1731 - lcd_width = 16; 1732 - if (lcd_hwidth == NOT_SET) 1733 - lcd_hwidth = 16; 1734 - break; 1735 - case PANEL_PROFILE_NEW: 1736 - /* serial, 2*16, new keypad */ 1737 - selected_keypad_type = KEYPAD_TYPE_NEW; 1738 - selected_lcd_type = LCD_TYPE_KS0074; 1739 - break; 1740 - case PANEL_PROFILE_HANTRONIX: 1741 - /* 8 bits, 2*16 hantronix-like, no keypad */ 1742 - selected_keypad_type = KEYPAD_TYPE_NONE; 1743 - selected_lcd_type = LCD_TYPE_HANTRONIX; 1744 - break; 1745 - case PANEL_PROFILE_NEXCOM: 1746 - /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */ 1747 - selected_keypad_type = KEYPAD_TYPE_NEXCOM; 1748 - selected_lcd_type = LCD_TYPE_NEXCOM; 1749 - break; 1750 - case PANEL_PROFILE_LARGE: 1751 - /* 8 bits, 2*40, old keypad */ 1752 - selected_keypad_type = KEYPAD_TYPE_OLD; 1753 - selected_lcd_type = LCD_TYPE_OLD; 1754 - break; 1755 - } 1756 - 1757 - /* 1758 - * Overwrite selection with module param values (both keypad and lcd), 1759 - * where the deprecated params have lower prio. 1760 - */ 1761 - if (keypad_enabled != NOT_SET) 1762 - selected_keypad_type = keypad_enabled; 1763 - if (keypad_type != NOT_SET) 1764 - selected_keypad_type = keypad_type; 1765 - 1766 - keypad.enabled = (selected_keypad_type > 0); 1767 - 1768 - if (lcd_enabled != NOT_SET) 1769 - selected_lcd_type = lcd_enabled; 1770 - if (lcd_type != NOT_SET) 1771 - selected_lcd_type = lcd_type; 1772 - 1773 - lcd.enabled = (selected_lcd_type > 0); 1774 - 1775 - if (lcd.enabled) { 1776 - /* 1777 - * Init lcd struct with load-time values to preserve exact 1778 - * current functionality (at least for now). 1779 - */ 1780 - lcd.charset = lcd_charset; 1781 - lcd.proto = lcd_proto; 1782 - lcd.pins.e = lcd_e_pin; 1783 - lcd.pins.rs = lcd_rs_pin; 1784 - lcd.pins.rw = lcd_rw_pin; 1785 - lcd.pins.cl = lcd_cl_pin; 1786 - lcd.pins.da = lcd_da_pin; 1787 - lcd.pins.bl = lcd_bl_pin; 1788 - } 1789 - 1790 - switch (selected_keypad_type) { 1791 - case KEYPAD_TYPE_OLD: 1792 - keypad_profile = old_keypad_profile; 1793 - break; 1794 - case KEYPAD_TYPE_NEW: 1795 - keypad_profile = new_keypad_profile; 1796 - break; 1797 - case KEYPAD_TYPE_NEXCOM: 1798 - keypad_profile = nexcom_keypad_profile; 1799 - break; 1800 - default: 1801 - keypad_profile = NULL; 1802 - break; 1803 - } 1804 - 1805 - if (!lcd.enabled && !keypad.enabled) { 1806 - /* no device enabled, let's exit */ 1807 - pr_err("panel driver disabled.\n"); 1808 - return -ENODEV; 1809 - } 1810 - 1811 - err = parport_register_driver(&panel_driver); 1812 - if (err) { 1813 - pr_err("could not register with parport. Aborting.\n"); 1814 - return err; 1815 - } 1816 - 1817 - if (pprt) 1818 - pr_info("panel driver registered on parport%d (io=0x%lx).\n", 1819 - parport, pprt->port->base); 1820 - else 1821 - pr_info("panel driver not yet registered\n"); 1822 - return 0; 1823 - } 1824 - 1825 - static void __exit panel_cleanup_module(void) 1826 - { 1827 - parport_unregister_driver(&panel_driver); 1828 - } 1829 - 1830 - module_init(panel_init_module); 1831 - module_exit(panel_cleanup_module); 1832 1618 MODULE_AUTHOR("Willy Tarreau"); 1833 1619 MODULE_LICENSE("GPL");
+113
drivers/auxdisplay/seg-led-gpio.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Driver for a 7-segment LED display 4 + * 5 + * The decimal point LED present on some devices is currently not 6 + * supported. 7 + * 8 + * Copyright (C) Allied Telesis Labs 9 + */ 10 + 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> 21 + 22 + #include "line-display.h" 23 + 24 + struct seg_led_priv { 25 + struct linedisp linedisp; 26 + struct delayed_work work; 27 + struct gpio_descs *segment_gpios; 28 + }; 29 + 30 + static void seg_led_update(struct work_struct *work) 31 + { 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) = { }; 36 + 37 + bitmap_set_value8(values, map_to_seg7(&map->map.seg7, linedisp->buf[0]), 0); 38 + 39 + gpiod_set_array_value_cansleep(priv->segment_gpios->ndescs, priv->segment_gpios->desc, 40 + priv->segment_gpios->info, values); 41 + } 42 + 43 + static int seg_led_linedisp_get_map_type(struct linedisp *linedisp) 44 + { 45 + struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp); 46 + 47 + INIT_DELAYED_WORK(&priv->work, seg_led_update); 48 + return LINEDISP_MAP_SEG7; 49 + } 50 + 51 + static void seg_led_linedisp_update(struct linedisp *linedisp) 52 + { 53 + struct seg_led_priv *priv = container_of(linedisp, struct seg_led_priv, linedisp); 54 + 55 + schedule_delayed_work(&priv->work, 0); 56 + } 57 + 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, 61 + }; 62 + 63 + static int seg_led_probe(struct platform_device *pdev) 64 + { 65 + struct seg_led_priv *priv; 66 + struct device *dev = &pdev->dev; 67 + 68 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 69 + if (!priv) 70 + return -ENOMEM; 71 + 72 + platform_set_drvdata(pdev, priv); 73 + 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); 77 + 78 + if (priv->segment_gpios->ndescs < 7 || priv->segment_gpios->ndescs > 8) 79 + return -EINVAL; 80 + 81 + return linedisp_register(&priv->linedisp, dev, 1, &seg_led_linedisp_ops); 82 + } 83 + 84 + static int seg_led_remove(struct platform_device *pdev) 85 + { 86 + struct seg_led_priv *priv = platform_get_drvdata(pdev); 87 + 88 + cancel_delayed_work_sync(&priv->work); 89 + linedisp_unregister(&priv->linedisp); 90 + 91 + return 0; 92 + } 93 + 94 + static const struct of_device_id seg_led_of_match[] = { 95 + { .compatible = "gpio-7-segment"}, 96 + {} 97 + }; 98 + MODULE_DEVICE_TABLE(of, seg_led_of_match); 99 + 100 + static struct platform_driver seg_led_driver = { 101 + .probe = seg_led_probe, 102 + .remove = seg_led_remove, 103 + .driver = { 104 + .name = "seg-led-gpio", 105 + .of_match_table = seg_led_of_match, 106 + }, 107 + }; 108 + module_platform_driver(seg_led_driver); 109 + 110 + MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>"); 111 + MODULE_DESCRIPTION("7 segment LED driver"); 112 + MODULE_LICENSE("GPL"); 113 + MODULE_IMPORT_NS(LINEDISP);