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