785 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * HT16K33 driver4 *5 * Author: Robin van der Gracht <robin@protonic.nl>6 *7 * Copyright: (C) 2016 Protonic Holland.8 * Copyright (C) 2021 Glider bv9 */10 11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/interrupt.h>14#include <linux/i2c.h>15#include <linux/property.h>16#include <linux/fb.h>17#include <linux/backlight.h>18#include <linux/container_of.h>19#include <linux/input.h>20#include <linux/input/matrix_keypad.h>21#include <linux/leds.h>22#include <linux/workqueue.h>23#include <linux/mm.h>24 25#include <linux/map_to_7segment.h>26#include <linux/map_to_14segment.h>27 28#include <linux/unaligned.h>29 30#include "line-display.h"31 32/* Registers */33#define REG_SYSTEM_SETUP 0x2034#define REG_SYSTEM_SETUP_OSC_ON BIT(0)35 36#define REG_DISPLAY_SETUP 0x8037#define REG_DISPLAY_SETUP_ON BIT(0)38#define REG_DISPLAY_SETUP_BLINK_OFF (0 << 1)39#define REG_DISPLAY_SETUP_BLINK_2HZ (1 << 1)40#define REG_DISPLAY_SETUP_BLINK_1HZ (2 << 1)41#define REG_DISPLAY_SETUP_BLINK_0HZ5 (3 << 1)42 43#define REG_ROWINT_SET 0xA044#define REG_ROWINT_SET_INT_EN BIT(0)45#define REG_ROWINT_SET_INT_ACT_HIGH BIT(1)46 47#define REG_BRIGHTNESS 0xE048 49/* Defines */50#define DRIVER_NAME "ht16k33"51 52#define MIN_BRIGHTNESS 0x153#define MAX_BRIGHTNESS 0x1054 55#define HT16K33_MATRIX_LED_MAX_COLS 856#define HT16K33_MATRIX_LED_MAX_ROWS 1657#define HT16K33_MATRIX_KEYPAD_MAX_COLS 358#define HT16K33_MATRIX_KEYPAD_MAX_ROWS 1259 60#define BYTES_PER_ROW (HT16K33_MATRIX_LED_MAX_ROWS / 8)61#define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)62 63enum display_type {64 DISP_MATRIX = 0,65 DISP_QUAD_7SEG,66 DISP_QUAD_14SEG,67};68 69struct ht16k33_keypad {70 struct i2c_client *client;71 struct input_dev *dev;72 uint32_t cols;73 uint32_t rows;74 uint32_t row_shift;75 uint32_t debounce_ms;76 uint16_t last_key_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];77 78 wait_queue_head_t wait;79 bool stopped;80};81 82struct ht16k33_fbdev {83 struct fb_info *info;84 uint32_t refresh_rate;85 uint8_t *buffer;86 uint8_t *cache;87};88 89struct ht16k33_priv {90 struct i2c_client *client;91 struct delayed_work work;92 struct led_classdev led;93 struct ht16k33_keypad keypad;94 union {95 struct ht16k33_fbdev fbdev;96 struct linedisp linedisp;97 };98 enum display_type type;99 uint8_t blink;100};101 102#define ht16k33_work_to_priv(p) \103 container_of(p, struct ht16k33_priv, work.work)104 105#define ht16k33_led_to_priv(p) \106 container_of(p, struct ht16k33_priv, led)107 108#define ht16k33_linedisp_to_priv(p) \109 container_of(p, struct ht16k33_priv, linedisp)110 111static const struct fb_fix_screeninfo ht16k33_fb_fix = {112 .id = DRIVER_NAME,113 .type = FB_TYPE_PACKED_PIXELS,114 .visual = FB_VISUAL_MONO10,115 .xpanstep = 0,116 .ypanstep = 0,117 .ywrapstep = 0,118 .line_length = HT16K33_MATRIX_LED_MAX_ROWS,119 .accel = FB_ACCEL_NONE,120};121 122static const struct fb_var_screeninfo ht16k33_fb_var = {123 .xres = HT16K33_MATRIX_LED_MAX_ROWS,124 .yres = HT16K33_MATRIX_LED_MAX_COLS,125 .xres_virtual = HT16K33_MATRIX_LED_MAX_ROWS,126 .yres_virtual = HT16K33_MATRIX_LED_MAX_COLS,127 .bits_per_pixel = 1,128 .red = { 0, 1, 0 },129 .green = { 0, 1, 0 },130 .blue = { 0, 1, 0 },131 .left_margin = 0,132 .right_margin = 0,133 .upper_margin = 0,134 .lower_margin = 0,135 .vmode = FB_VMODE_NONINTERLACED,136};137 138static int ht16k33_display_on(struct ht16k33_priv *priv)139{140 uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv->blink;141 142 return i2c_smbus_write_byte(priv->client, data);143}144 145static int ht16k33_display_off(struct ht16k33_priv *priv)146{147 return i2c_smbus_write_byte(priv->client, REG_DISPLAY_SETUP);148}149 150static int ht16k33_brightness_set(struct ht16k33_priv *priv,151 unsigned int brightness)152{153 int err;154 155 if (brightness == 0) {156 priv->blink = REG_DISPLAY_SETUP_BLINK_OFF;157 return ht16k33_display_off(priv);158 }159 160 err = ht16k33_display_on(priv);161 if (err)162 return err;163 164 return i2c_smbus_write_byte(priv->client,165 REG_BRIGHTNESS | (brightness - 1));166}167 168static int ht16k33_brightness_set_blocking(struct led_classdev *led_cdev,169 enum led_brightness brightness)170{171 struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);172 173 return ht16k33_brightness_set(priv, brightness);174}175 176static int ht16k33_blink_set(struct led_classdev *led_cdev,177 unsigned long *delay_on, unsigned long *delay_off)178{179 struct ht16k33_priv *priv = ht16k33_led_to_priv(led_cdev);180 unsigned int delay;181 uint8_t blink;182 int err;183 184 if (!*delay_on && !*delay_off) {185 blink = REG_DISPLAY_SETUP_BLINK_1HZ;186 delay = 1000;187 } else if (*delay_on <= 750) {188 blink = REG_DISPLAY_SETUP_BLINK_2HZ;189 delay = 500;190 } else if (*delay_on <= 1500) {191 blink = REG_DISPLAY_SETUP_BLINK_1HZ;192 delay = 1000;193 } else {194 blink = REG_DISPLAY_SETUP_BLINK_0HZ5;195 delay = 2000;196 }197 198 err = i2c_smbus_write_byte(priv->client,199 REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON |200 blink);201 if (err)202 return err;203 204 priv->blink = blink;205 *delay_on = *delay_off = delay;206 return 0;207}208 209static void ht16k33_fb_queue(struct ht16k33_priv *priv)210{211 struct ht16k33_fbdev *fbdev = &priv->fbdev;212 213 schedule_delayed_work(&priv->work, HZ / fbdev->refresh_rate);214}215 216/*217 * This gets the fb data from cache and copies it to ht16k33 display RAM218 */219static void ht16k33_fb_update(struct work_struct *work)220{221 struct ht16k33_priv *priv = ht16k33_work_to_priv(work);222 struct ht16k33_fbdev *fbdev = &priv->fbdev;223 224 uint8_t *p1, *p2;225 int len, pos = 0, first = -1;226 227 p1 = fbdev->cache;228 p2 = fbdev->buffer;229 230 /* Search for the first byte with changes */231 while (pos < HT16K33_FB_SIZE && first < 0) {232 if (*(p1++) - *(p2++))233 first = pos;234 pos++;235 }236 237 /* No changes found */238 if (first < 0)239 goto requeue;240 241 len = HT16K33_FB_SIZE - first;242 p1 = fbdev->cache + HT16K33_FB_SIZE - 1;243 p2 = fbdev->buffer + HT16K33_FB_SIZE - 1;244 245 /* Determine i2c transfer length */246 while (len > 1) {247 if (*(p1--) - *(p2--))248 break;249 len--;250 }251 252 p1 = fbdev->cache + first;253 p2 = fbdev->buffer + first;254 if (!i2c_smbus_write_i2c_block_data(priv->client, first, len, p2))255 memcpy(p1, p2, len);256requeue:257 ht16k33_fb_queue(priv);258}259 260static int ht16k33_initialize(struct ht16k33_priv *priv)261{262 uint8_t data[HT16K33_FB_SIZE];263 uint8_t byte;264 int err;265 266 /* Clear RAM (8 * 16 bits) */267 memset(data, 0, sizeof(data));268 err = i2c_smbus_write_block_data(priv->client, 0, sizeof(data), data);269 if (err)270 return err;271 272 /* Turn on internal oscillator */273 byte = REG_SYSTEM_SETUP_OSC_ON | REG_SYSTEM_SETUP;274 err = i2c_smbus_write_byte(priv->client, byte);275 if (err)276 return err;277 278 /* Configure INT pin */279 byte = REG_ROWINT_SET | REG_ROWINT_SET_INT_ACT_HIGH;280 if (priv->client->irq > 0)281 byte |= REG_ROWINT_SET_INT_EN;282 return i2c_smbus_write_byte(priv->client, byte);283}284 285static int ht16k33_bl_update_status(struct backlight_device *bl)286{287 const int brightness = backlight_get_brightness(bl);288 struct ht16k33_priv *priv = bl_get_data(bl);289 290 return ht16k33_brightness_set(priv, brightness);291}292 293static const struct backlight_ops ht16k33_bl_ops = {294 .update_status = ht16k33_bl_update_status,295};296 297/*298 * Blank events will be passed to the actual device handling the backlight when299 * we return zero here.300 */301static int ht16k33_blank(int blank, struct fb_info *info)302{303 return 0;304}305 306static int ht16k33_mmap(struct fb_info *info, struct vm_area_struct *vma)307{308 struct ht16k33_priv *priv = info->par;309 struct page *pages = virt_to_page(priv->fbdev.buffer);310 311 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);312 313 return vm_map_pages_zero(vma, &pages, 1);314}315 316static const struct fb_ops ht16k33_fb_ops = {317 .owner = THIS_MODULE,318 __FB_DEFAULT_SYSMEM_OPS_RDWR,319 .fb_blank = ht16k33_blank,320 __FB_DEFAULT_SYSMEM_OPS_DRAW,321 .fb_mmap = ht16k33_mmap,322};323 324/*325 * This gets the keys from keypad and reports it to input subsystem.326 * Returns true if a key is pressed.327 */328static bool ht16k33_keypad_scan(struct ht16k33_keypad *keypad)329{330 const unsigned short *keycodes = keypad->dev->keycode;331 u16 new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];332 __le16 data[HT16K33_MATRIX_KEYPAD_MAX_COLS];333 unsigned long bits_changed;334 int row, col, code;335 int rc;336 bool pressed = false;337 338 rc = i2c_smbus_read_i2c_block_data(keypad->client, 0x40,339 sizeof(data), (u8 *)data);340 if (rc != sizeof(data)) {341 dev_err(&keypad->client->dev,342 "Failed to read key data, rc=%d\n", rc);343 return false;344 }345 346 for (col = 0; col < keypad->cols; col++) {347 new_state[col] = le16_to_cpu(data[col]);348 if (new_state[col])349 pressed = true;350 bits_changed = keypad->last_key_state[col] ^ new_state[col];351 352 for_each_set_bit(row, &bits_changed, BITS_PER_LONG) {353 code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);354 input_event(keypad->dev, EV_MSC, MSC_SCAN, code);355 input_report_key(keypad->dev, keycodes[code],356 new_state[col] & BIT(row));357 }358 }359 input_sync(keypad->dev);360 memcpy(keypad->last_key_state, new_state, sizeof(u16) * keypad->cols);361 362 return pressed;363}364 365static irqreturn_t ht16k33_keypad_irq_thread(int irq, void *dev)366{367 struct ht16k33_keypad *keypad = dev;368 369 do {370 wait_event_timeout(keypad->wait, keypad->stopped,371 msecs_to_jiffies(keypad->debounce_ms));372 if (keypad->stopped)373 break;374 } while (ht16k33_keypad_scan(keypad));375 376 return IRQ_HANDLED;377}378 379static int ht16k33_keypad_start(struct input_dev *dev)380{381 struct ht16k33_keypad *keypad = input_get_drvdata(dev);382 383 keypad->stopped = false;384 mb();385 enable_irq(keypad->client->irq);386 387 return 0;388}389 390static void ht16k33_keypad_stop(struct input_dev *dev)391{392 struct ht16k33_keypad *keypad = input_get_drvdata(dev);393 394 keypad->stopped = true;395 mb();396 wake_up(&keypad->wait);397 disable_irq(keypad->client->irq);398}399 400static void ht16k33_seg7_update(struct work_struct *work)401{402 struct ht16k33_priv *priv = ht16k33_work_to_priv(work);403 struct linedisp_map *map = priv->linedisp.map;404 char *s = priv->linedisp.buf;405 uint8_t buf[9];406 407 buf[0] = map_to_seg7(&map->map.seg7, *s++);408 buf[1] = 0;409 buf[2] = map_to_seg7(&map->map.seg7, *s++);410 buf[3] = 0;411 buf[4] = 0;412 buf[5] = 0;413 buf[6] = map_to_seg7(&map->map.seg7, *s++);414 buf[7] = 0;415 buf[8] = map_to_seg7(&map->map.seg7, *s++);416 417 i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);418}419 420static void ht16k33_seg14_update(struct work_struct *work)421{422 struct ht16k33_priv *priv = ht16k33_work_to_priv(work);423 struct linedisp_map *map = priv->linedisp.map;424 char *s = priv->linedisp.buf;425 uint8_t buf[8];426 427 put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 0);428 put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 2);429 put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 4);430 put_unaligned_le16(map_to_seg14(&map->map.seg14, *s++), buf + 6);431 432 i2c_smbus_write_i2c_block_data(priv->client, 0, ARRAY_SIZE(buf), buf);433}434 435static int ht16k33_linedisp_get_map_type(struct linedisp *linedisp)436{437 struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);438 439 switch (priv->type) {440 case DISP_QUAD_7SEG:441 INIT_DELAYED_WORK(&priv->work, ht16k33_seg7_update);442 return LINEDISP_MAP_SEG7;443 444 case DISP_QUAD_14SEG:445 INIT_DELAYED_WORK(&priv->work, ht16k33_seg14_update);446 return LINEDISP_MAP_SEG14;447 448 default:449 return -EINVAL;450 }451}452 453static void ht16k33_linedisp_update(struct linedisp *linedisp)454{455 struct ht16k33_priv *priv = ht16k33_linedisp_to_priv(linedisp);456 457 schedule_delayed_work(&priv->work, 0);458}459 460static const struct linedisp_ops ht16k33_linedisp_ops = {461 .get_map_type = ht16k33_linedisp_get_map_type,462 .update = ht16k33_linedisp_update,463};464 465static int ht16k33_led_probe(struct device *dev, struct led_classdev *led,466 unsigned int brightness)467{468 struct led_init_data init_data = {};469 int err;470 471 /* The LED is optional */472 init_data.fwnode = device_get_named_child_node(dev, "led");473 if (!init_data.fwnode)474 return 0;475 476 init_data.devicename = "auxdisplay";477 init_data.devname_mandatory = true;478 479 led->brightness_set_blocking = ht16k33_brightness_set_blocking;480 led->blink_set = ht16k33_blink_set;481 led->flags = LED_CORE_SUSPENDRESUME;482 led->brightness = brightness;483 led->max_brightness = MAX_BRIGHTNESS;484 485 err = devm_led_classdev_register_ext(dev, led, &init_data);486 fwnode_handle_put(init_data.fwnode);487 if (err)488 dev_err(dev, "Failed to register LED\n");489 490 return err;491}492 493static int ht16k33_keypad_probe(struct i2c_client *client,494 struct ht16k33_keypad *keypad)495{496 struct device *dev = &client->dev;497 u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;498 u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;499 int err;500 501 keypad->client = client;502 init_waitqueue_head(&keypad->wait);503 504 keypad->dev = devm_input_allocate_device(dev);505 if (!keypad->dev)506 return -ENOMEM;507 508 input_set_drvdata(keypad->dev, keypad);509 510 keypad->dev->name = DRIVER_NAME"-keypad";511 keypad->dev->id.bustype = BUS_I2C;512 keypad->dev->open = ht16k33_keypad_start;513 keypad->dev->close = ht16k33_keypad_stop;514 515 if (!device_property_read_bool(dev, "linux,no-autorepeat"))516 __set_bit(EV_REP, keypad->dev->evbit);517 518 err = device_property_read_u32(dev, "debounce-delay-ms",519 &keypad->debounce_ms);520 if (err) {521 dev_err(dev, "key debounce delay not specified\n");522 return err;523 }524 525 err = matrix_keypad_parse_properties(dev, &rows, &cols);526 if (err)527 return err;528 if (rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS ||529 cols > HT16K33_MATRIX_KEYPAD_MAX_COLS) {530 dev_err(dev, "%u rows or %u cols out of range in DT\n", rows,531 cols);532 return -ERANGE;533 }534 535 keypad->rows = rows;536 keypad->cols = cols;537 keypad->row_shift = get_count_order(cols);538 539 err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,540 keypad->dev);541 if (err) {542 dev_err(dev, "failed to build keymap\n");543 return err;544 }545 546 err = devm_request_threaded_irq(dev, client->irq, NULL,547 ht16k33_keypad_irq_thread,548 IRQF_TRIGGER_HIGH | IRQF_ONESHOT,549 DRIVER_NAME, keypad);550 if (err) {551 dev_err(dev, "irq request failed %d, error %d\n", client->irq,552 err);553 return err;554 }555 556 ht16k33_keypad_stop(keypad->dev);557 558 return input_register_device(keypad->dev);559}560 561static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,562 uint32_t brightness)563{564 struct ht16k33_fbdev *fbdev = &priv->fbdev;565 struct backlight_device *bl = NULL;566 int err;567 568 if (priv->led.dev) {569 err = ht16k33_brightness_set(priv, brightness);570 if (err)571 return err;572 } else {573 /* backwards compatibility with DT lacking an led subnode */574 struct backlight_properties bl_props;575 576 memset(&bl_props, 0, sizeof(struct backlight_properties));577 bl_props.type = BACKLIGHT_RAW;578 bl_props.max_brightness = MAX_BRIGHTNESS;579 580 bl = devm_backlight_device_register(dev, DRIVER_NAME"-bl", dev,581 priv, &ht16k33_bl_ops,582 &bl_props);583 if (IS_ERR(bl)) {584 dev_err(dev, "failed to register backlight\n");585 return PTR_ERR(bl);586 }587 588 bl->props.brightness = brightness;589 ht16k33_bl_update_status(bl);590 }591 592 /* Framebuffer (2 bytes per column) */593 BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);594 fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);595 if (!fbdev->buffer)596 return -ENOMEM;597 598 fbdev->cache = devm_kmalloc(dev, HT16K33_FB_SIZE, GFP_KERNEL);599 if (!fbdev->cache) {600 err = -ENOMEM;601 goto err_fbdev_buffer;602 }603 604 fbdev->info = framebuffer_alloc(0, dev);605 if (!fbdev->info) {606 err = -ENOMEM;607 goto err_fbdev_buffer;608 }609 610 err = device_property_read_u32(dev, "refresh-rate-hz",611 &fbdev->refresh_rate);612 if (err) {613 dev_err(dev, "refresh rate not specified\n");614 goto err_fbdev_info;615 }616 fb_bl_default_curve(fbdev->info, 0, MIN_BRIGHTNESS, MAX_BRIGHTNESS);617 618 INIT_DELAYED_WORK(&priv->work, ht16k33_fb_update);619 fbdev->info->fbops = &ht16k33_fb_ops;620 fbdev->info->flags |= FBINFO_VIRTFB;621 fbdev->info->screen_buffer = fbdev->buffer;622 fbdev->info->screen_size = HT16K33_FB_SIZE;623 fbdev->info->fix = ht16k33_fb_fix;624 fbdev->info->var = ht16k33_fb_var;625 fbdev->info->bl_dev = bl;626 fbdev->info->pseudo_palette = NULL;627 fbdev->info->par = priv;628 629 err = register_framebuffer(fbdev->info);630 if (err)631 goto err_fbdev_info;632 633 ht16k33_fb_queue(priv);634 return 0;635 636err_fbdev_info:637 framebuffer_release(fbdev->info);638err_fbdev_buffer:639 free_page((unsigned long) fbdev->buffer);640 641 return err;642}643 644static int ht16k33_seg_probe(struct device *dev, struct ht16k33_priv *priv,645 uint32_t brightness)646{647 struct linedisp *linedisp = &priv->linedisp;648 int err;649 650 err = ht16k33_brightness_set(priv, brightness);651 if (err)652 return err;653 654 return linedisp_register(linedisp, dev, 4, &ht16k33_linedisp_ops);655}656 657static int ht16k33_probe(struct i2c_client *client)658{659 struct device *dev = &client->dev;660 const struct of_device_id *id;661 struct ht16k33_priv *priv;662 uint32_t dft_brightness;663 int err;664 665 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {666 dev_err(dev, "i2c_check_functionality error\n");667 return -EIO;668 }669 670 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);671 if (!priv)672 return -ENOMEM;673 674 priv->client = client;675 id = i2c_of_match_device(dev->driver->of_match_table, client);676 if (id)677 priv->type = (uintptr_t)id->data;678 i2c_set_clientdata(client, priv);679 680 err = ht16k33_initialize(priv);681 if (err)682 return err;683 684 err = device_property_read_u32(dev, "default-brightness-level",685 &dft_brightness);686 if (err) {687 dft_brightness = MAX_BRIGHTNESS;688 } else if (dft_brightness > MAX_BRIGHTNESS) {689 dev_warn(dev,690 "invalid default brightness level: %u, using %u\n",691 dft_brightness, MAX_BRIGHTNESS);692 dft_brightness = MAX_BRIGHTNESS;693 }694 695 /* LED */696 err = ht16k33_led_probe(dev, &priv->led, dft_brightness);697 if (err)698 return err;699 700 /* Keypad */701 if (client->irq > 0) {702 err = ht16k33_keypad_probe(client, &priv->keypad);703 if (err)704 return err;705 }706 707 switch (priv->type) {708 case DISP_MATRIX:709 /* Frame Buffer Display */710 err = ht16k33_fbdev_probe(dev, priv, dft_brightness);711 break;712 713 case DISP_QUAD_7SEG:714 case DISP_QUAD_14SEG:715 /* Segment Display */716 err = ht16k33_seg_probe(dev, priv, dft_brightness);717 break;718 719 default:720 return -EINVAL;721 }722 return err;723}724 725static void ht16k33_remove(struct i2c_client *client)726{727 struct ht16k33_priv *priv = i2c_get_clientdata(client);728 struct ht16k33_fbdev *fbdev = &priv->fbdev;729 730 cancel_delayed_work_sync(&priv->work);731 732 switch (priv->type) {733 case DISP_MATRIX:734 unregister_framebuffer(fbdev->info);735 framebuffer_release(fbdev->info);736 free_page((unsigned long)fbdev->buffer);737 break;738 739 case DISP_QUAD_7SEG:740 case DISP_QUAD_14SEG:741 linedisp_unregister(&priv->linedisp);742 break;743 744 default:745 break;746 }747}748 749static const struct i2c_device_id ht16k33_i2c_match[] = {750 { "ht16k33", 0 },751 { }752};753MODULE_DEVICE_TABLE(i2c, ht16k33_i2c_match);754 755static const struct of_device_id ht16k33_of_match[] = {756 {757 /* 0.56" 4-Digit 7-Segment FeatherWing Display (Red) */758 .compatible = "adafruit,3108", .data = (void *)DISP_QUAD_7SEG,759 }, {760 /* 0.54" Quad Alphanumeric FeatherWing Display (Red) */761 .compatible = "adafruit,3130", .data = (void *)DISP_QUAD_14SEG,762 }, {763 /* Generic, assumed Dot-Matrix Display */764 .compatible = "holtek,ht16k33", .data = (void *)DISP_MATRIX,765 },766 { }767};768MODULE_DEVICE_TABLE(of, ht16k33_of_match);769 770static struct i2c_driver ht16k33_driver = {771 .probe = ht16k33_probe,772 .remove = ht16k33_remove,773 .driver = {774 .name = DRIVER_NAME,775 .of_match_table = ht16k33_of_match,776 },777 .id_table = ht16k33_i2c_match,778};779module_i2c_driver(ht16k33_driver);780 781MODULE_DESCRIPTION("Holtek HT16K33 driver");782MODULE_LICENSE("GPL");783MODULE_IMPORT_NS(LINEDISP);784MODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");785