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