brintos

brintos / linux-shallow public Read only

0
0
Text · 18.8 KiB · a816f9e Raw
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