5
5
* Author: Robin van der Gracht <[email protected] >
6
6
*
7
7
* Copyright: (C) 2016 Protonic Holland.
8
+ * Copyright (C) 2021 Glider bv
8
9
*/
9
10
10
11
#include <linux/kernel.h>
20
21
#include <linux/workqueue.h>
21
22
#include <linux/mm.h>
22
23
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
+
23
31
/* Registers */
24
32
#define REG_SYSTEM_SETUP 0x20
25
33
#define REG_SYSTEM_SETUP_OSC_ON BIT(0)
47
55
#define BYTES_PER_ROW (HT16K33_MATRIX_LED_MAX_ROWS / 8)
48
56
#define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
49
57
58
+ enum display_type {
59
+ DISP_MATRIX = 0 ,
60
+ DISP_QUAD_7SEG ,
61
+ DISP_QUAD_14SEG ,
62
+ };
63
+
50
64
struct ht16k33_keypad {
51
65
struct i2c_client * client ;
52
66
struct input_dev * dev ;
@@ -67,11 +81,25 @@ struct ht16k33_fbdev {
67
81
uint8_t * cache ;
68
82
};
69
83
84
+ struct ht16k33_seg {
85
+ struct linedisp linedisp ;
86
+ union {
87
+ struct seg7_conversion_map seg7 ;
88
+ struct seg14_conversion_map seg14 ;
89
+ } map ;
90
+ unsigned int map_size ;
91
+ char curr [4 ];
92
+ };
93
+
70
94
struct ht16k33_priv {
71
95
struct i2c_client * client ;
72
96
struct delayed_work work ;
73
97
struct ht16k33_keypad keypad ;
74
- struct ht16k33_fbdev fbdev ;
98
+ union {
99
+ struct ht16k33_fbdev fbdev ;
100
+ struct ht16k33_seg seg ;
101
+ };
102
+ enum display_type type ;
75
103
};
76
104
77
105
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
@@ -101,6 +129,33 @@ static const struct fb_var_screeninfo ht16k33_fb_var = {
101
129
.vmode = FB_VMODE_NONINTERLACED ,
102
130
};
103
131
132
+ static const SEG7_DEFAULT_MAP (initial_map_seg7 );
133
+ static const SEG14_DEFAULT_MAP (initial_map_seg14 );
134
+
135
+ static ssize_t map_seg_show (struct device * dev , struct device_attribute * attr ,
136
+ char * buf )
137
+ {
138
+ struct ht16k33_priv * priv = dev_get_drvdata (dev );
139
+
140
+ memcpy (buf , & priv -> seg .map , priv -> seg .map_size );
141
+ return priv -> seg .map_size ;
142
+ }
143
+
144
+ static ssize_t map_seg_store (struct device * dev , struct device_attribute * attr ,
145
+ const char * buf , size_t cnt )
146
+ {
147
+ struct ht16k33_priv * priv = dev_get_drvdata (dev );
148
+
149
+ if (cnt != priv -> seg .map_size )
150
+ return - EINVAL ;
151
+
152
+ memcpy (& priv -> seg .map , buf , cnt );
153
+ return cnt ;
154
+ }
155
+
156
+ static DEVICE_ATTR (map_seg7 , 0644 , map_seg_show , map_seg_store ) ;
157
+ static DEVICE_ATTR (map_seg14 , 0644 , map_seg_show , map_seg_store ) ;
158
+
104
159
static int ht16k33_display_on (struct ht16k33_priv * priv )
105
160
{
106
161
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON ;
@@ -335,6 +390,51 @@ static void ht16k33_keypad_stop(struct input_dev *dev)
335
390
disable_irq (keypad -> client -> irq );
336
391
}
337
392
393
+ static void ht16k33_linedisp_update (struct linedisp * linedisp )
394
+ {
395
+ struct ht16k33_priv * priv = container_of (linedisp , struct ht16k33_priv ,
396
+ seg .linedisp );
397
+
398
+ schedule_delayed_work (& priv -> work , 0 );
399
+ }
400
+
401
+ static void ht16k33_seg7_update (struct work_struct * work )
402
+ {
403
+ struct ht16k33_priv * priv = container_of (work , struct ht16k33_priv ,
404
+ work .work );
405
+ struct ht16k33_seg * seg = & priv -> seg ;
406
+ char * s = seg -> curr ;
407
+ uint8_t buf [9 ];
408
+
409
+ buf [0 ] = map_to_seg7 (& seg -> map .seg7 , * s ++ );
410
+ buf [1 ] = 0 ;
411
+ buf [2 ] = map_to_seg7 (& seg -> map .seg7 , * s ++ );
412
+ buf [3 ] = 0 ;
413
+ buf [4 ] = 0 ;
414
+ buf [5 ] = 0 ;
415
+ buf [6 ] = map_to_seg7 (& seg -> map .seg7 , * s ++ );
416
+ buf [7 ] = 0 ;
417
+ buf [8 ] = map_to_seg7 (& seg -> map .seg7 , * s ++ );
418
+
419
+ i2c_smbus_write_i2c_block_data (priv -> client , 0 , ARRAY_SIZE (buf ), buf );
420
+ }
421
+
422
+ static void ht16k33_seg14_update (struct work_struct * work )
423
+ {
424
+ struct ht16k33_priv * priv = container_of (work , struct ht16k33_priv ,
425
+ work .work );
426
+ struct ht16k33_seg * seg = & priv -> seg ;
427
+ char * s = seg -> curr ;
428
+ uint8_t buf [8 ];
429
+
430
+ put_unaligned_le16 (map_to_seg14 (& seg -> map .seg14 , * s ++ ), buf );
431
+ put_unaligned_le16 (map_to_seg14 (& seg -> map .seg14 , * s ++ ), buf + 2 );
432
+ put_unaligned_le16 (map_to_seg14 (& seg -> map .seg14 , * s ++ ), buf + 4 );
433
+ put_unaligned_le16 (map_to_seg14 (& seg -> map .seg14 , * s ++ ), buf + 6 );
434
+
435
+ i2c_smbus_write_i2c_block_data (priv -> client , 0 , ARRAY_SIZE (buf ), buf );
436
+ }
437
+
338
438
static int ht16k33_keypad_probe (struct i2c_client * client ,
339
439
struct ht16k33_keypad * keypad )
340
440
{
@@ -479,9 +579,56 @@ static int ht16k33_fbdev_probe(struct device *dev, struct ht16k33_priv *priv,
479
579
return err ;
480
580
}
481
581
582
+ static int ht16k33_seg_probe (struct device * dev , struct ht16k33_priv * priv ,
583
+ uint32_t brightness )
584
+ {
585
+ struct ht16k33_seg * seg = & priv -> seg ;
586
+ int err ;
587
+
588
+ err = ht16k33_brightness_set (priv , brightness );
589
+ if (err )
590
+ return err ;
591
+
592
+ switch (priv -> type ) {
593
+ case DISP_MATRIX :
594
+ /* not handled here */
595
+ err = - EINVAL ;
596
+ break ;
597
+
598
+ case DISP_QUAD_7SEG :
599
+ INIT_DELAYED_WORK (& priv -> work , ht16k33_seg7_update );
600
+ seg -> map .seg7 = initial_map_seg7 ;
601
+ seg -> map_size = sizeof (seg -> map .seg7 );
602
+ err = device_create_file (dev , & dev_attr_map_seg7 );
603
+ break ;
604
+
605
+ case DISP_QUAD_14SEG :
606
+ INIT_DELAYED_WORK (& priv -> work , ht16k33_seg14_update );
607
+ seg -> map .seg14 = initial_map_seg14 ;
608
+ seg -> map_size = sizeof (seg -> map .seg14 );
609
+ err = device_create_file (dev , & dev_attr_map_seg14 );
610
+ break ;
611
+ }
612
+ if (err )
613
+ return err ;
614
+
615
+ err = linedisp_register (& seg -> linedisp , dev , 4 , seg -> curr ,
616
+ ht16k33_linedisp_update );
617
+ if (err )
618
+ goto err_remove_map_file ;
619
+
620
+ return 0 ;
621
+
622
+ err_remove_map_file :
623
+ device_remove_file (dev , & dev_attr_map_seg7 );
624
+ device_remove_file (dev , & dev_attr_map_seg14 );
625
+ return err ;
626
+ }
627
+
482
628
static int ht16k33_probe (struct i2c_client * client )
483
629
{
484
630
struct device * dev = & client -> dev ;
631
+ const struct of_device_id * id ;
485
632
struct ht16k33_priv * priv ;
486
633
uint32_t dft_brightness ;
487
634
int err ;
@@ -496,6 +643,9 @@ static int ht16k33_probe(struct i2c_client *client)
496
643
return - ENOMEM ;
497
644
498
645
priv -> client = client ;
646
+ id = i2c_of_match_device (dev -> driver -> of_match_table , client );
647
+ if (id )
648
+ priv -> type = (uintptr_t )id -> data ;
499
649
i2c_set_clientdata (client , priv );
500
650
501
651
err = ht16k33_initialize (priv );
@@ -520,8 +670,19 @@ static int ht16k33_probe(struct i2c_client *client)
520
670
return err ;
521
671
}
522
672
523
- /* Frame Buffer Display */
524
- return ht16k33_fbdev_probe (dev , priv , dft_brightness );
673
+ switch (priv -> type ) {
674
+ case DISP_MATRIX :
675
+ /* Frame Buffer Display */
676
+ err = ht16k33_fbdev_probe (dev , priv , dft_brightness );
677
+ break ;
678
+
679
+ case DISP_QUAD_7SEG :
680
+ case DISP_QUAD_14SEG :
681
+ /* Segment Display */
682
+ err = ht16k33_seg_probe (dev , priv , dft_brightness );
683
+ break ;
684
+ }
685
+ return err ;
525
686
}
526
687
527
688
static int ht16k33_remove (struct i2c_client * client )
@@ -530,9 +691,21 @@ static int ht16k33_remove(struct i2c_client *client)
530
691
struct ht16k33_fbdev * fbdev = & priv -> fbdev ;
531
692
532
693
cancel_delayed_work_sync (& priv -> work );
533
- unregister_framebuffer (fbdev -> info );
534
- framebuffer_release (fbdev -> info );
535
- free_page ((unsigned long ) fbdev -> buffer );
694
+
695
+ switch (priv -> type ) {
696
+ case DISP_MATRIX :
697
+ unregister_framebuffer (fbdev -> info );
698
+ framebuffer_release (fbdev -> info );
699
+ free_page ((unsigned long )fbdev -> buffer );
700
+ break ;
701
+
702
+ case DISP_QUAD_7SEG :
703
+ case DISP_QUAD_14SEG :
704
+ linedisp_unregister (& priv -> seg .linedisp );
705
+ device_remove_file (& client -> dev , & dev_attr_map_seg7 );
706
+ device_remove_file (& client -> dev , & dev_attr_map_seg14 );
707
+ break ;
708
+ }
536
709
537
710
return 0 ;
538
711
}
@@ -544,7 +717,16 @@ static const struct i2c_device_id ht16k33_i2c_match[] = {
544
717
MODULE_DEVICE_TABLE (i2c , ht16k33_i2c_match );
545
718
546
719
static const struct of_device_id ht16k33_of_match [] = {
547
- { .compatible = "holtek,ht16k33" , },
720
+ {
721
+ /* 0.56" 4-Digit 7-Segment FeatherWing Display (Red) */
722
+ .compatible = "adafruit,3108" , .data = (void * )DISP_QUAD_7SEG ,
723
+ }, {
724
+ /* 0.54" Quad Alphanumeric FeatherWing Display (Red) */
725
+ .compatible = "adafruit,3130" , .data = (void * )DISP_QUAD_14SEG ,
726
+ }, {
727
+ /* Generic, assumed Dot-Matrix Display */
728
+ .compatible = "holtek,ht16k33" , .data = (void * )DISP_MATRIX ,
729
+ },
548
730
{ }
549
731
};
550
732
MODULE_DEVICE_TABLE (of , ht16k33_of_match );
0 commit comments