18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * HT16K33 driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Robin van der Gracht <robin@protonic.nl>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright: (C) 2016 Protonic Holland.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/fb.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/backlight.h>
188c2ecf20Sopenharmony_ci#include <linux/input.h>
198c2ecf20Sopenharmony_ci#include <linux/input/matrix_keypad.h>
208c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Registers */
248c2ecf20Sopenharmony_ci#define REG_SYSTEM_SETUP		0x20
258c2ecf20Sopenharmony_ci#define REG_SYSTEM_SETUP_OSC_ON		BIT(0)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define REG_DISPLAY_SETUP		0x80
288c2ecf20Sopenharmony_ci#define REG_DISPLAY_SETUP_ON		BIT(0)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define REG_ROWINT_SET			0xA0
318c2ecf20Sopenharmony_ci#define REG_ROWINT_SET_INT_EN		BIT(0)
328c2ecf20Sopenharmony_ci#define REG_ROWINT_SET_INT_ACT_HIGH	BIT(1)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define REG_BRIGHTNESS			0xE0
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Defines */
378c2ecf20Sopenharmony_ci#define DRIVER_NAME			"ht16k33"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define MIN_BRIGHTNESS			0x1
408c2ecf20Sopenharmony_ci#define MAX_BRIGHTNESS			0x10
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define HT16K33_MATRIX_LED_MAX_COLS	8
438c2ecf20Sopenharmony_ci#define HT16K33_MATRIX_LED_MAX_ROWS	16
448c2ecf20Sopenharmony_ci#define HT16K33_MATRIX_KEYPAD_MAX_COLS	3
458c2ecf20Sopenharmony_ci#define HT16K33_MATRIX_KEYPAD_MAX_ROWS	12
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define BYTES_PER_ROW		(HT16K33_MATRIX_LED_MAX_ROWS / 8)
488c2ecf20Sopenharmony_ci#define HT16K33_FB_SIZE		(HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct ht16k33_keypad {
518c2ecf20Sopenharmony_ci	struct i2c_client *client;
528c2ecf20Sopenharmony_ci	struct input_dev *dev;
538c2ecf20Sopenharmony_ci	uint32_t cols;
548c2ecf20Sopenharmony_ci	uint32_t rows;
558c2ecf20Sopenharmony_ci	uint32_t row_shift;
568c2ecf20Sopenharmony_ci	uint32_t debounce_ms;
578c2ecf20Sopenharmony_ci	uint16_t last_key_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
608c2ecf20Sopenharmony_ci	bool stopped;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct ht16k33_fbdev {
648c2ecf20Sopenharmony_ci	struct fb_info *info;
658c2ecf20Sopenharmony_ci	uint32_t refresh_rate;
668c2ecf20Sopenharmony_ci	uint8_t *buffer;
678c2ecf20Sopenharmony_ci	uint8_t *cache;
688c2ecf20Sopenharmony_ci	struct delayed_work work;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct ht16k33_priv {
728c2ecf20Sopenharmony_ci	struct i2c_client *client;
738c2ecf20Sopenharmony_ci	struct ht16k33_keypad keypad;
748c2ecf20Sopenharmony_ci	struct ht16k33_fbdev fbdev;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo ht16k33_fb_fix = {
788c2ecf20Sopenharmony_ci	.id		= DRIVER_NAME,
798c2ecf20Sopenharmony_ci	.type		= FB_TYPE_PACKED_PIXELS,
808c2ecf20Sopenharmony_ci	.visual		= FB_VISUAL_MONO10,
818c2ecf20Sopenharmony_ci	.xpanstep	= 0,
828c2ecf20Sopenharmony_ci	.ypanstep	= 0,
838c2ecf20Sopenharmony_ci	.ywrapstep	= 0,
848c2ecf20Sopenharmony_ci	.line_length	= HT16K33_MATRIX_LED_MAX_ROWS,
858c2ecf20Sopenharmony_ci	.accel		= FB_ACCEL_NONE,
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo ht16k33_fb_var = {
898c2ecf20Sopenharmony_ci	.xres = HT16K33_MATRIX_LED_MAX_ROWS,
908c2ecf20Sopenharmony_ci	.yres = HT16K33_MATRIX_LED_MAX_COLS,
918c2ecf20Sopenharmony_ci	.xres_virtual = HT16K33_MATRIX_LED_MAX_ROWS,
928c2ecf20Sopenharmony_ci	.yres_virtual = HT16K33_MATRIX_LED_MAX_COLS,
938c2ecf20Sopenharmony_ci	.bits_per_pixel = 1,
948c2ecf20Sopenharmony_ci	.red = { 0, 1, 0 },
958c2ecf20Sopenharmony_ci	.green = { 0, 1, 0 },
968c2ecf20Sopenharmony_ci	.blue = { 0, 1, 0 },
978c2ecf20Sopenharmony_ci	.left_margin = 0,
988c2ecf20Sopenharmony_ci	.right_margin = 0,
998c2ecf20Sopenharmony_ci	.upper_margin = 0,
1008c2ecf20Sopenharmony_ci	.lower_margin = 0,
1018c2ecf20Sopenharmony_ci	.vmode = FB_VMODE_NONINTERLACED,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int ht16k33_display_on(struct ht16k33_priv *priv)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(priv->client, data);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int ht16k33_display_off(struct ht16k33_priv *priv)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(priv->client, REG_DISPLAY_SETUP);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void ht16k33_fb_queue(struct ht16k33_priv *priv)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct ht16k33_fbdev *fbdev = &priv->fbdev;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	schedule_delayed_work(&fbdev->work, HZ / fbdev->refresh_rate);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * This gets the fb data from cache and copies it to ht16k33 display RAM
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistatic void ht16k33_fb_update(struct work_struct *work)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct ht16k33_fbdev *fbdev =
1298c2ecf20Sopenharmony_ci		container_of(work, struct ht16k33_fbdev, work.work);
1308c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv =
1318c2ecf20Sopenharmony_ci		container_of(fbdev, struct ht16k33_priv, fbdev);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	uint8_t *p1, *p2;
1348c2ecf20Sopenharmony_ci	int len, pos = 0, first = -1;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	p1 = fbdev->cache;
1378c2ecf20Sopenharmony_ci	p2 = fbdev->buffer;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Search for the first byte with changes */
1408c2ecf20Sopenharmony_ci	while (pos < HT16K33_FB_SIZE && first < 0) {
1418c2ecf20Sopenharmony_ci		if (*(p1++) - *(p2++))
1428c2ecf20Sopenharmony_ci			first = pos;
1438c2ecf20Sopenharmony_ci		pos++;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* No changes found */
1478c2ecf20Sopenharmony_ci	if (first < 0)
1488c2ecf20Sopenharmony_ci		goto requeue;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	len = HT16K33_FB_SIZE - first;
1518c2ecf20Sopenharmony_ci	p1 = fbdev->cache + HT16K33_FB_SIZE - 1;
1528c2ecf20Sopenharmony_ci	p2 = fbdev->buffer + HT16K33_FB_SIZE - 1;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	/* Determine i2c transfer length */
1558c2ecf20Sopenharmony_ci	while (len > 1) {
1568c2ecf20Sopenharmony_ci		if (*(p1--) - *(p2--))
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		len--;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	p1 = fbdev->cache + first;
1628c2ecf20Sopenharmony_ci	p2 = fbdev->buffer + first;
1638c2ecf20Sopenharmony_ci	if (!i2c_smbus_write_i2c_block_data(priv->client, first, len, p2))
1648c2ecf20Sopenharmony_ci		memcpy(p1, p2, len);
1658c2ecf20Sopenharmony_cirequeue:
1668c2ecf20Sopenharmony_ci	ht16k33_fb_queue(priv);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int ht16k33_initialize(struct ht16k33_priv *priv)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	uint8_t byte;
1728c2ecf20Sopenharmony_ci	int err;
1738c2ecf20Sopenharmony_ci	uint8_t data[HT16K33_MATRIX_LED_MAX_COLS * 2];
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* Clear RAM (8 * 16 bits) */
1768c2ecf20Sopenharmony_ci	memset(data, 0, sizeof(data));
1778c2ecf20Sopenharmony_ci	err = i2c_smbus_write_block_data(priv->client, 0, sizeof(data), data);
1788c2ecf20Sopenharmony_ci	if (err)
1798c2ecf20Sopenharmony_ci		return err;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	/* Turn on internal oscillator */
1828c2ecf20Sopenharmony_ci	byte = REG_SYSTEM_SETUP_OSC_ON | REG_SYSTEM_SETUP;
1838c2ecf20Sopenharmony_ci	err = i2c_smbus_write_byte(priv->client, byte);
1848c2ecf20Sopenharmony_ci	if (err)
1858c2ecf20Sopenharmony_ci		return err;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* Configure INT pin */
1888c2ecf20Sopenharmony_ci	byte = REG_ROWINT_SET | REG_ROWINT_SET_INT_ACT_HIGH;
1898c2ecf20Sopenharmony_ci	if (priv->client->irq > 0)
1908c2ecf20Sopenharmony_ci		byte |= REG_ROWINT_SET_INT_EN;
1918c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(priv->client, byte);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int ht16k33_bl_update_status(struct backlight_device *bl)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int brightness = bl->props.brightness;
1978c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv = bl_get_data(bl);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (bl->props.power != FB_BLANK_UNBLANK ||
2008c2ecf20Sopenharmony_ci	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
2018c2ecf20Sopenharmony_ci	    bl->props.state & BL_CORE_FBBLANK || brightness == 0) {
2028c2ecf20Sopenharmony_ci		return ht16k33_display_off(priv);
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ht16k33_display_on(priv);
2068c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte(priv->client,
2078c2ecf20Sopenharmony_ci				    REG_BRIGHTNESS | (brightness - 1));
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int ht16k33_bl_check_fb(struct backlight_device *bl, struct fb_info *fi)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv = bl_get_data(bl);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return (fi == NULL) || (fi->par == priv);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic const struct backlight_ops ht16k33_bl_ops = {
2188c2ecf20Sopenharmony_ci	.update_status	= ht16k33_bl_update_status,
2198c2ecf20Sopenharmony_ci	.check_fb	= ht16k33_bl_check_fb,
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * Blank events will be passed to the actual device handling the backlight when
2248c2ecf20Sopenharmony_ci * we return zero here.
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_cistatic int ht16k33_blank(int blank, struct fb_info *info)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	return 0;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int ht16k33_mmap(struct fb_info *info, struct vm_area_struct *vma)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv = info->par;
2348c2ecf20Sopenharmony_ci	struct page *pages = virt_to_page(priv->fbdev.buffer);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return vm_map_pages_zero(vma, &pages, 1);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic const struct fb_ops ht16k33_fb_ops = {
2408c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2418c2ecf20Sopenharmony_ci	.fb_read = fb_sys_read,
2428c2ecf20Sopenharmony_ci	.fb_write = fb_sys_write,
2438c2ecf20Sopenharmony_ci	.fb_blank = ht16k33_blank,
2448c2ecf20Sopenharmony_ci	.fb_fillrect = sys_fillrect,
2458c2ecf20Sopenharmony_ci	.fb_copyarea = sys_copyarea,
2468c2ecf20Sopenharmony_ci	.fb_imageblit = sys_imageblit,
2478c2ecf20Sopenharmony_ci	.fb_mmap = ht16k33_mmap,
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * This gets the keys from keypad and reports it to input subsystem.
2528c2ecf20Sopenharmony_ci * Returns true if a key is pressed.
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_cistatic bool ht16k33_keypad_scan(struct ht16k33_keypad *keypad)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	const unsigned short *keycodes = keypad->dev->keycode;
2578c2ecf20Sopenharmony_ci	u16 new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
2588c2ecf20Sopenharmony_ci	__le16 data[HT16K33_MATRIX_KEYPAD_MAX_COLS];
2598c2ecf20Sopenharmony_ci	unsigned long bits_changed;
2608c2ecf20Sopenharmony_ci	int row, col, code;
2618c2ecf20Sopenharmony_ci	int rc;
2628c2ecf20Sopenharmony_ci	bool pressed = false;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	rc = i2c_smbus_read_i2c_block_data(keypad->client, 0x40,
2658c2ecf20Sopenharmony_ci					   sizeof(data), (u8 *)data);
2668c2ecf20Sopenharmony_ci	if (rc != sizeof(data)) {
2678c2ecf20Sopenharmony_ci		dev_err(&keypad->client->dev,
2688c2ecf20Sopenharmony_ci			"Failed to read key data, rc=%d\n", rc);
2698c2ecf20Sopenharmony_ci		return false;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	for (col = 0; col < keypad->cols; col++) {
2738c2ecf20Sopenharmony_ci		new_state[col] = le16_to_cpu(data[col]);
2748c2ecf20Sopenharmony_ci		if (new_state[col])
2758c2ecf20Sopenharmony_ci			pressed = true;
2768c2ecf20Sopenharmony_ci		bits_changed = keypad->last_key_state[col] ^ new_state[col];
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		for_each_set_bit(row, &bits_changed, BITS_PER_LONG) {
2798c2ecf20Sopenharmony_ci			code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
2808c2ecf20Sopenharmony_ci			input_event(keypad->dev, EV_MSC, MSC_SCAN, code);
2818c2ecf20Sopenharmony_ci			input_report_key(keypad->dev, keycodes[code],
2828c2ecf20Sopenharmony_ci					 new_state[col] & BIT(row));
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci	input_sync(keypad->dev);
2868c2ecf20Sopenharmony_ci	memcpy(keypad->last_key_state, new_state, sizeof(u16) * keypad->cols);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return pressed;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic irqreturn_t ht16k33_keypad_irq_thread(int irq, void *dev)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct ht16k33_keypad *keypad = dev;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	do {
2968c2ecf20Sopenharmony_ci		wait_event_timeout(keypad->wait, keypad->stopped,
2978c2ecf20Sopenharmony_ci				    msecs_to_jiffies(keypad->debounce_ms));
2988c2ecf20Sopenharmony_ci		if (keypad->stopped)
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci	} while (ht16k33_keypad_scan(keypad));
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int ht16k33_keypad_start(struct input_dev *dev)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct ht16k33_keypad *keypad = input_get_drvdata(dev);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	keypad->stopped = false;
3108c2ecf20Sopenharmony_ci	mb();
3118c2ecf20Sopenharmony_ci	enable_irq(keypad->client->irq);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void ht16k33_keypad_stop(struct input_dev *dev)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct ht16k33_keypad *keypad = input_get_drvdata(dev);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	keypad->stopped = true;
3218c2ecf20Sopenharmony_ci	mb();
3228c2ecf20Sopenharmony_ci	wake_up(&keypad->wait);
3238c2ecf20Sopenharmony_ci	disable_irq(keypad->client->irq);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int ht16k33_keypad_probe(struct i2c_client *client,
3278c2ecf20Sopenharmony_ci				struct ht16k33_keypad *keypad)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct device_node *node = client->dev.of_node;
3308c2ecf20Sopenharmony_ci	u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;
3318c2ecf20Sopenharmony_ci	u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;
3328c2ecf20Sopenharmony_ci	int err;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	keypad->client = client;
3358c2ecf20Sopenharmony_ci	init_waitqueue_head(&keypad->wait);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	keypad->dev = devm_input_allocate_device(&client->dev);
3388c2ecf20Sopenharmony_ci	if (!keypad->dev)
3398c2ecf20Sopenharmony_ci		return -ENOMEM;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	input_set_drvdata(keypad->dev, keypad);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	keypad->dev->name = DRIVER_NAME"-keypad";
3448c2ecf20Sopenharmony_ci	keypad->dev->id.bustype = BUS_I2C;
3458c2ecf20Sopenharmony_ci	keypad->dev->open = ht16k33_keypad_start;
3468c2ecf20Sopenharmony_ci	keypad->dev->close = ht16k33_keypad_stop;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (!of_get_property(node, "linux,no-autorepeat", NULL))
3498c2ecf20Sopenharmony_ci		__set_bit(EV_REP, keypad->dev->evbit);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	err = of_property_read_u32(node, "debounce-delay-ms",
3528c2ecf20Sopenharmony_ci				   &keypad->debounce_ms);
3538c2ecf20Sopenharmony_ci	if (err) {
3548c2ecf20Sopenharmony_ci		dev_err(&client->dev, "key debounce delay not specified\n");
3558c2ecf20Sopenharmony_ci		return err;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
3598c2ecf20Sopenharmony_ci	if (err)
3608c2ecf20Sopenharmony_ci		return err;
3618c2ecf20Sopenharmony_ci	if (rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS ||
3628c2ecf20Sopenharmony_ci	    cols > HT16K33_MATRIX_KEYPAD_MAX_COLS) {
3638c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%u rows or %u cols out of range in DT\n",
3648c2ecf20Sopenharmony_ci			rows, cols);
3658c2ecf20Sopenharmony_ci		return -ERANGE;
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	keypad->rows = rows;
3698c2ecf20Sopenharmony_ci	keypad->cols = cols;
3708c2ecf20Sopenharmony_ci	keypad->row_shift = get_count_order(cols);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,
3738c2ecf20Sopenharmony_ci					 keypad->dev);
3748c2ecf20Sopenharmony_ci	if (err) {
3758c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to build keymap\n");
3768c2ecf20Sopenharmony_ci		return err;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	err = devm_request_threaded_irq(&client->dev, client->irq,
3808c2ecf20Sopenharmony_ci					NULL, ht16k33_keypad_irq_thread,
3818c2ecf20Sopenharmony_ci					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
3828c2ecf20Sopenharmony_ci					DRIVER_NAME, keypad);
3838c2ecf20Sopenharmony_ci	if (err) {
3848c2ecf20Sopenharmony_ci		dev_err(&client->dev, "irq request failed %d, error %d\n",
3858c2ecf20Sopenharmony_ci			client->irq, err);
3868c2ecf20Sopenharmony_ci		return err;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	ht16k33_keypad_stop(keypad->dev);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	err = input_register_device(keypad->dev);
3928c2ecf20Sopenharmony_ci	if (err)
3938c2ecf20Sopenharmony_ci		return err;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int ht16k33_probe(struct i2c_client *client,
3998c2ecf20Sopenharmony_ci				  const struct i2c_device_id *id)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	int err;
4028c2ecf20Sopenharmony_ci	uint32_t dft_brightness;
4038c2ecf20Sopenharmony_ci	struct backlight_device *bl;
4048c2ecf20Sopenharmony_ci	struct backlight_properties bl_props;
4058c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv;
4068c2ecf20Sopenharmony_ci	struct ht16k33_fbdev *fbdev;
4078c2ecf20Sopenharmony_ci	struct device_node *node = client->dev.of_node;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
4108c2ecf20Sopenharmony_ci		dev_err(&client->dev, "i2c_check_functionality error\n");
4118c2ecf20Sopenharmony_ci		return -EIO;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (client->irq <= 0) {
4158c2ecf20Sopenharmony_ci		dev_err(&client->dev, "No IRQ specified\n");
4168c2ecf20Sopenharmony_ci		return -EINVAL;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
4208c2ecf20Sopenharmony_ci	if (!priv)
4218c2ecf20Sopenharmony_ci		return -ENOMEM;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	priv->client = client;
4248c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, priv);
4258c2ecf20Sopenharmony_ci	fbdev = &priv->fbdev;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	err = ht16k33_initialize(priv);
4288c2ecf20Sopenharmony_ci	if (err)
4298c2ecf20Sopenharmony_ci		return err;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* Backlight */
4328c2ecf20Sopenharmony_ci	memset(&bl_props, 0, sizeof(struct backlight_properties));
4338c2ecf20Sopenharmony_ci	bl_props.type = BACKLIGHT_RAW;
4348c2ecf20Sopenharmony_ci	bl_props.max_brightness = MAX_BRIGHTNESS;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	bl = devm_backlight_device_register(&client->dev, DRIVER_NAME"-bl",
4378c2ecf20Sopenharmony_ci					    &client->dev, priv,
4388c2ecf20Sopenharmony_ci					    &ht16k33_bl_ops, &bl_props);
4398c2ecf20Sopenharmony_ci	if (IS_ERR(bl)) {
4408c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to register backlight\n");
4418c2ecf20Sopenharmony_ci		return PTR_ERR(bl);
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	err = of_property_read_u32(node, "default-brightness-level",
4458c2ecf20Sopenharmony_ci				   &dft_brightness);
4468c2ecf20Sopenharmony_ci	if (err) {
4478c2ecf20Sopenharmony_ci		dft_brightness = MAX_BRIGHTNESS;
4488c2ecf20Sopenharmony_ci	} else if (dft_brightness > MAX_BRIGHTNESS) {
4498c2ecf20Sopenharmony_ci		dev_warn(&client->dev,
4508c2ecf20Sopenharmony_ci			 "invalid default brightness level: %u, using %u\n",
4518c2ecf20Sopenharmony_ci			 dft_brightness, MAX_BRIGHTNESS);
4528c2ecf20Sopenharmony_ci		dft_brightness = MAX_BRIGHTNESS;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	bl->props.brightness = dft_brightness;
4568c2ecf20Sopenharmony_ci	ht16k33_bl_update_status(bl);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* Framebuffer (2 bytes per column) */
4598c2ecf20Sopenharmony_ci	BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);
4608c2ecf20Sopenharmony_ci	fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
4618c2ecf20Sopenharmony_ci	if (!fbdev->buffer)
4628c2ecf20Sopenharmony_ci		return -ENOMEM;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	fbdev->cache = devm_kmalloc(&client->dev, HT16K33_FB_SIZE, GFP_KERNEL);
4658c2ecf20Sopenharmony_ci	if (!fbdev->cache) {
4668c2ecf20Sopenharmony_ci		err = -ENOMEM;
4678c2ecf20Sopenharmony_ci		goto err_fbdev_buffer;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	fbdev->info = framebuffer_alloc(0, &client->dev);
4718c2ecf20Sopenharmony_ci	if (!fbdev->info) {
4728c2ecf20Sopenharmony_ci		err = -ENOMEM;
4738c2ecf20Sopenharmony_ci		goto err_fbdev_buffer;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	err = of_property_read_u32(node, "refresh-rate-hz",
4778c2ecf20Sopenharmony_ci		&fbdev->refresh_rate);
4788c2ecf20Sopenharmony_ci	if (err) {
4798c2ecf20Sopenharmony_ci		dev_err(&client->dev, "refresh rate not specified\n");
4808c2ecf20Sopenharmony_ci		goto err_fbdev_info;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	fb_bl_default_curve(fbdev->info, 0, MIN_BRIGHTNESS, MAX_BRIGHTNESS);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&fbdev->work, ht16k33_fb_update);
4858c2ecf20Sopenharmony_ci	fbdev->info->fbops = &ht16k33_fb_ops;
4868c2ecf20Sopenharmony_ci	fbdev->info->screen_base = (char __iomem *) fbdev->buffer;
4878c2ecf20Sopenharmony_ci	fbdev->info->screen_size = HT16K33_FB_SIZE;
4888c2ecf20Sopenharmony_ci	fbdev->info->fix = ht16k33_fb_fix;
4898c2ecf20Sopenharmony_ci	fbdev->info->var = ht16k33_fb_var;
4908c2ecf20Sopenharmony_ci	fbdev->info->bl_dev = bl;
4918c2ecf20Sopenharmony_ci	fbdev->info->pseudo_palette = NULL;
4928c2ecf20Sopenharmony_ci	fbdev->info->flags = FBINFO_FLAG_DEFAULT;
4938c2ecf20Sopenharmony_ci	fbdev->info->par = priv;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	err = register_framebuffer(fbdev->info);
4968c2ecf20Sopenharmony_ci	if (err)
4978c2ecf20Sopenharmony_ci		goto err_fbdev_info;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	err = ht16k33_keypad_probe(client, &priv->keypad);
5008c2ecf20Sopenharmony_ci	if (err)
5018c2ecf20Sopenharmony_ci		goto err_fbdev_unregister;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ht16k33_fb_queue(priv);
5048c2ecf20Sopenharmony_ci	return 0;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cierr_fbdev_unregister:
5078c2ecf20Sopenharmony_ci	unregister_framebuffer(fbdev->info);
5088c2ecf20Sopenharmony_cierr_fbdev_info:
5098c2ecf20Sopenharmony_ci	framebuffer_release(fbdev->info);
5108c2ecf20Sopenharmony_cierr_fbdev_buffer:
5118c2ecf20Sopenharmony_ci	free_page((unsigned long) fbdev->buffer);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return err;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int ht16k33_remove(struct i2c_client *client)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct ht16k33_priv *priv = i2c_get_clientdata(client);
5198c2ecf20Sopenharmony_ci	struct ht16k33_fbdev *fbdev = &priv->fbdev;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&fbdev->work);
5228c2ecf20Sopenharmony_ci	unregister_framebuffer(fbdev->info);
5238c2ecf20Sopenharmony_ci	framebuffer_release(fbdev->info);
5248c2ecf20Sopenharmony_ci	free_page((unsigned long) fbdev->buffer);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	return 0;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic const struct i2c_device_id ht16k33_i2c_match[] = {
5308c2ecf20Sopenharmony_ci	{ "ht16k33", 0 },
5318c2ecf20Sopenharmony_ci	{ }
5328c2ecf20Sopenharmony_ci};
5338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ht16k33_i2c_match);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic const struct of_device_id ht16k33_of_match[] = {
5368c2ecf20Sopenharmony_ci	{ .compatible = "holtek,ht16k33", },
5378c2ecf20Sopenharmony_ci	{ }
5388c2ecf20Sopenharmony_ci};
5398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ht16k33_of_match);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic struct i2c_driver ht16k33_driver = {
5428c2ecf20Sopenharmony_ci	.probe		= ht16k33_probe,
5438c2ecf20Sopenharmony_ci	.remove		= ht16k33_remove,
5448c2ecf20Sopenharmony_ci	.driver		= {
5458c2ecf20Sopenharmony_ci		.name		= DRIVER_NAME,
5468c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(ht16k33_of_match),
5478c2ecf20Sopenharmony_ci	},
5488c2ecf20Sopenharmony_ci	.id_table = ht16k33_i2c_match,
5498c2ecf20Sopenharmony_ci};
5508c2ecf20Sopenharmony_cimodule_i2c_driver(ht16k33_driver);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Holtek HT16K33 driver");
5538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robin van der Gracht <robin@protonic.nl>");
555