2018-02-17 20:33:13 +01:00
// SPDX-License-Identifier: GPL-2.0
2016-11-07 10:56:35 +01:00
/*
* HT16K33 driver
*
* Author : Robin van der Gracht < robin @ protonic . nl >
*
* Copyright : ( C ) 2016 Protonic Holland .
2021-10-19 16:45:17 +02:00
* Copyright ( C ) 2021 Glider bv
2016-11-07 10:56:35 +01:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
2021-10-19 16:45:20 +02:00
# include <linux/property.h>
2016-11-07 10:56:35 +01:00
# include <linux/fb.h>
# include <linux/backlight.h>
2024-02-19 18:58:04 +02:00
# include <linux/container_of.h>
2016-11-07 10:56:35 +01:00
# include <linux/input.h>
# include <linux/input/matrix_keypad.h>
2021-10-19 16:45:19 +02:00
# include <linux/leds.h>
2016-11-07 10:56:35 +01:00
# include <linux/workqueue.h>
# include <linux/mm.h>
2021-10-19 16:45:17 +02:00
# include <linux/map_to_7segment.h>
# include <linux/map_to_14segment.h>
# include <asm/unaligned.h>
# include "line-display.h"
2016-11-07 10:56:35 +01:00
/* Registers */
# define REG_SYSTEM_SETUP 0x20
# define REG_SYSTEM_SETUP_OSC_ON BIT(0)
# define REG_DISPLAY_SETUP 0x80
# define REG_DISPLAY_SETUP_ON BIT(0)
2021-10-19 16:45:19 +02:00
# define REG_DISPLAY_SETUP_BLINK_OFF (0 << 1)
# define REG_DISPLAY_SETUP_BLINK_2HZ (1 << 1)
# define REG_DISPLAY_SETUP_BLINK_1HZ (2 << 1)
# define REG_DISPLAY_SETUP_BLINK_0HZ5 (3 << 1)
2016-11-07 10:56:35 +01:00
# define REG_ROWINT_SET 0xA0
# define REG_ROWINT_SET_INT_EN BIT(0)
# define REG_ROWINT_SET_INT_ACT_HIGH BIT(1)
# define REG_BRIGHTNESS 0xE0
/* Defines */
# define DRIVER_NAME "ht16k33"
# define MIN_BRIGHTNESS 0x1
# define MAX_BRIGHTNESS 0x10
# define HT16K33_MATRIX_LED_MAX_COLS 8
# define HT16K33_MATRIX_LED_MAX_ROWS 16
# define HT16K33_MATRIX_KEYPAD_MAX_COLS 3
# define HT16K33_MATRIX_KEYPAD_MAX_ROWS 12
# define BYTES_PER_ROW (HT16K33_MATRIX_LED_MAX_ROWS / 8)
# define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
2021-10-19 16:45:17 +02:00
enum display_type {
DISP_MATRIX = 0 ,
DISP_QUAD_7SEG ,
DISP_QUAD_14SEG ,
} ;
2016-11-07 10:56:35 +01:00
struct ht16k33_keypad {
2017-02-09 10:15:52 -08:00
struct i2c_client * client ;
2016-11-07 10:56:35 +01:00
struct input_dev * dev ;
uint32_t cols ;
uint32_t rows ;
uint32_t row_shift ;
uint32_t debounce_ms ;
uint16_t last_key_state [ HT16K33_MATRIX_KEYPAD_MAX_COLS ] ;
2017-02-09 10:15:52 -08:00
wait_queue_head_t wait ;
bool stopped ;
2016-11-07 10:56:35 +01:00
} ;
struct ht16k33_fbdev {
struct fb_info * info ;
uint32_t refresh_rate ;
uint8_t * buffer ;
uint8_t * cache ;
} ;
struct ht16k33_priv {
struct i2c_client * client ;
2021-10-19 16:45:14 +02:00
struct delayed_work work ;
2021-10-19 16:45:19 +02:00
struct led_classdev led ;
2016-11-07 10:56:35 +01:00
struct ht16k33_keypad keypad ;
2021-10-19 16:45:17 +02:00
union {
struct ht16k33_fbdev fbdev ;
2024-02-19 18:58:06 +02:00
struct linedisp linedisp ;
2021-10-19 16:45:17 +02:00
} ;
enum display_type type ;
2021-10-19 16:45:19 +02:00
uint8_t blink ;
2016-11-07 10:56:35 +01:00
} ;
2024-02-19 18:58:04 +02:00
# define ht16k33_work_to_priv(p) \
container_of ( p , struct ht16k33_priv , work . work )
# define ht16k33_led_to_priv(p) \
container_of ( p , struct ht16k33_priv , led )
# define ht16k33_linedisp_to_priv(p) \
2024-02-19 18:58:06 +02:00
container_of ( p , struct ht16k33_priv , linedisp )
2024-02-19 18:58:04 +02:00
2019-08-19 13:21:26 +05:30
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
2016-11-07 10:56:35 +01:00
. id = DRIVER_NAME ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_MONO10 ,
. xpanstep = 0 ,
. ypanstep = 0 ,
. ywrapstep = 0 ,
. line_length = HT16K33_MATRIX_LED_MAX_ROWS ,
. accel = FB_ACCEL_NONE ,
} ;
2019-08-19 13:21:26 +05:30
static const struct fb_var_screeninfo ht16k33_fb_var = {
2016-11-07 10:56:35 +01:00
. xres = HT16K33_MATRIX_LED_MAX_ROWS ,
. yres = HT16K33_MATRIX_LED_MAX_COLS ,
. xres_virtual = HT16K33_MATRIX_LED_MAX_ROWS ,
. yres_virtual = HT16K33_MATRIX_LED_MAX_COLS ,
. bits_per_pixel = 1 ,
. red = { 0 , 1 , 0 } ,
. green = { 0 , 1 , 0 } ,
. blue = { 0 , 1 , 0 } ,
. left_margin = 0 ,
. right_margin = 0 ,
. upper_margin = 0 ,
. lower_margin = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ;
static int ht16k33_display_on ( struct ht16k33_priv * priv )
{
2021-10-19 16:45:19 +02:00
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv - > blink ;
2016-11-07 10:56:35 +01:00
return i2c_smbus_write_byte ( priv - > client , data ) ;
}
static int ht16k33_display_off ( struct ht16k33_priv * priv )
{
return i2c_smbus_write_byte ( priv - > client , REG_DISPLAY_SETUP ) ;
}
2021-10-19 16:45:15 +02:00
static int ht16k33_brightness_set ( struct ht16k33_priv * priv ,
unsigned int brightness )
{
int err ;
2021-10-19 16:45:19 +02:00
if ( brightness = = 0 ) {
priv - > blink = REG_DISPLAY_SETUP_BLINK_OFF ;
2021-10-19 16:45:15 +02:00
return ht16k33_display_off ( priv ) ;
2021-10-19 16:45:19 +02:00
}
2021-10-19 16:45:15 +02:00
err = ht16k33_display_on ( priv ) ;
if ( err )
return err ;
return i2c_smbus_write_byte ( priv - > client ,
REG_BRIGHTNESS | ( brightness - 1 ) ) ;
}
2021-10-19 16:45:19 +02:00
static int ht16k33_brightness_set_blocking ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_led_to_priv ( led_cdev ) ;
2021-10-19 16:45:19 +02:00
return ht16k33_brightness_set ( priv , brightness ) ;
}
static int ht16k33_blink_set ( struct led_classdev * led_cdev ,
unsigned long * delay_on , unsigned long * delay_off )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_led_to_priv ( led_cdev ) ;
2021-10-19 16:45:19 +02:00
unsigned int delay ;
uint8_t blink ;
int err ;
if ( ! * delay_on & & ! * delay_off ) {
blink = REG_DISPLAY_SETUP_BLINK_1HZ ;
delay = 1000 ;
} else if ( * delay_on < = 750 ) {
blink = REG_DISPLAY_SETUP_BLINK_2HZ ;
delay = 500 ;
} else if ( * delay_on < = 1500 ) {
blink = REG_DISPLAY_SETUP_BLINK_1HZ ;
delay = 1000 ;
} else {
blink = REG_DISPLAY_SETUP_BLINK_0HZ5 ;
delay = 2000 ;
}
err = i2c_smbus_write_byte ( priv - > client ,
REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON |
blink ) ;
if ( err )
return err ;
priv - > blink = blink ;
* delay_on = * delay_off = delay ;
return 0 ;
}
2016-11-07 10:56:35 +01:00
static void ht16k33_fb_queue ( struct ht16k33_priv * priv )
{
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 16:45:14 +02:00
schedule_delayed_work ( & priv - > work , HZ / fbdev - > refresh_rate ) ;
2016-11-07 10:56:35 +01:00
}
/*
* This gets the fb data from cache and copies it to ht16k33 display RAM
*/
static void ht16k33_fb_update ( struct work_struct * work )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_work_to_priv ( work ) ;
2021-10-19 16:45:14 +02:00
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2016-11-07 10:56:35 +01:00
uint8_t * p1 , * p2 ;
int len , pos = 0 , first = - 1 ;
p1 = fbdev - > cache ;
p2 = fbdev - > buffer ;
/* Search for the first byte with changes */
while ( pos < HT16K33_FB_SIZE & & first < 0 ) {
if ( * ( p1 + + ) - * ( p2 + + ) )
first = pos ;
pos + + ;
}
/* No changes found */
if ( first < 0 )
goto requeue ;
len = HT16K33_FB_SIZE - first ;
p1 = fbdev - > cache + HT16K33_FB_SIZE - 1 ;
p2 = fbdev - > buffer + HT16K33_FB_SIZE - 1 ;
/* Determine i2c transfer length */
while ( len > 1 ) {
if ( * ( p1 - - ) - * ( p2 - - ) )
break ;
len - - ;
}
p1 = fbdev - > cache + first ;
p2 = fbdev - > buffer + first ;
if ( ! i2c_smbus_write_i2c_block_data ( priv - > client , first , len , p2 ) )
memcpy ( p1 , p2 , len ) ;
requeue :
ht16k33_fb_queue ( priv ) ;
}
static int ht16k33_initialize ( struct ht16k33_priv * priv )
{
2021-10-19 16:45:10 +02:00
uint8_t data [ HT16K33_FB_SIZE ] ;
2016-11-07 10:56:35 +01:00
uint8_t byte ;
int err ;
/* Clear RAM (8 * 16 bits) */
memset ( data , 0 , sizeof ( data ) ) ;
err = i2c_smbus_write_block_data ( priv - > client , 0 , sizeof ( data ) , data ) ;
if ( err )
return err ;
/* Turn on internal oscillator */
byte = REG_SYSTEM_SETUP_OSC_ON | REG_SYSTEM_SETUP ;
err = i2c_smbus_write_byte ( priv - > client , byte ) ;
if ( err )
return err ;
/* Configure INT pin */
byte = REG_ROWINT_SET | REG_ROWINT_SET_INT_ACT_HIGH ;
if ( priv - > client - > irq > 0 )
byte | = REG_ROWINT_SET_INT_EN ;
return i2c_smbus_write_byte ( priv - > client , byte ) ;
}
static int ht16k33_bl_update_status ( struct backlight_device * bl )
{
int brightness = bl - > props . brightness ;
struct ht16k33_priv * priv = bl_get_data ( bl ) ;
if ( bl - > props . power ! = FB_BLANK_UNBLANK | |
bl - > props . fb_blank ! = FB_BLANK_UNBLANK | |
2021-10-19 16:45:15 +02:00
bl - > props . state & BL_CORE_FBBLANK )
brightness = 0 ;
2016-11-07 10:56:35 +01:00
2021-10-19 16:45:15 +02:00
return ht16k33_brightness_set ( priv , brightness ) ;
2016-11-07 10:56:35 +01:00
}
static int ht16k33_bl_check_fb ( struct backlight_device * bl , struct fb_info * fi )
{
struct ht16k33_priv * priv = bl_get_data ( bl ) ;
return ( fi = = NULL ) | | ( fi - > par = = priv ) ;
}
static const struct backlight_ops ht16k33_bl_ops = {
. update_status = ht16k33_bl_update_status ,
. check_fb = ht16k33_bl_check_fb ,
} ;
2021-10-19 16:45:09 +02:00
/*
* Blank events will be passed to the actual device handling the backlight when
* we return zero here .
*/
static int ht16k33_blank ( int blank , struct fb_info * info )
{
return 0 ;
}
2016-11-07 10:56:35 +01:00
static int ht16k33_mmap ( struct fb_info * info , struct vm_area_struct * vma )
{
struct ht16k33_priv * priv = info - > par ;
2019-05-26 21:41:10 +05:30
struct page * pages = virt_to_page ( priv - > fbdev . buffer ) ;
2016-11-07 10:56:35 +01:00
2023-11-27 14:15:58 +01:00
vma - > vm_page_prot = pgprot_decrypted ( vma - > vm_page_prot ) ;
2019-05-26 21:41:10 +05:30
return vm_map_pages_zero ( vma , & pages , 1 ) ;
2016-11-07 10:56:35 +01:00
}
2019-12-03 18:38:54 +02:00
static const struct fb_ops ht16k33_fb_ops = {
2016-11-07 10:56:35 +01:00
. owner = THIS_MODULE ,
2023-11-27 14:15:39 +01:00
__FB_DEFAULT_SYSMEM_OPS_RDWR ,
2021-10-19 16:45:09 +02:00
. fb_blank = ht16k33_blank ,
2023-11-27 14:15:39 +01:00
__FB_DEFAULT_SYSMEM_OPS_DRAW ,
2016-11-07 10:56:35 +01:00
. fb_mmap = ht16k33_mmap ,
} ;
2017-02-09 10:15:52 -08:00
/*
* This gets the keys from keypad and reports it to input subsystem .
* Returns true if a key is pressed .
*/
static bool ht16k33_keypad_scan ( struct ht16k33_keypad * keypad )
{
const unsigned short * keycodes = keypad - > dev - > keycode ;
u16 new_state [ HT16K33_MATRIX_KEYPAD_MAX_COLS ] ;
2017-04-01 10:27:14 -07:00
__le16 data [ HT16K33_MATRIX_KEYPAD_MAX_COLS ] ;
2017-02-09 10:15:52 -08:00
unsigned long bits_changed ;
int row , col , code ;
2017-04-01 10:27:14 -07:00
int rc ;
2017-02-09 10:15:52 -08:00
bool pressed = false ;
2017-04-01 10:27:14 -07:00
rc = i2c_smbus_read_i2c_block_data ( keypad - > client , 0x40 ,
sizeof ( data ) , ( u8 * ) data ) ;
if ( rc ! = sizeof ( data ) ) {
dev_err ( & keypad - > client - > dev ,
" Failed to read key data, rc=%d \n " , rc ) ;
2017-02-09 10:15:52 -08:00
return false ;
}
for ( col = 0 ; col < keypad - > cols ; col + + ) {
2017-04-01 10:27:14 -07:00
new_state [ col ] = le16_to_cpu ( data [ col ] ) ;
2017-02-09 10:15:52 -08:00
if ( new_state [ col ] )
pressed = true ;
bits_changed = keypad - > last_key_state [ col ] ^ new_state [ col ] ;
for_each_set_bit ( row , & bits_changed , BITS_PER_LONG ) {
code = MATRIX_SCAN_CODE ( row , col , keypad - > row_shift ) ;
input_event ( keypad - > dev , EV_MSC , MSC_SCAN , code ) ;
input_report_key ( keypad - > dev , keycodes [ code ] ,
new_state [ col ] & BIT ( row ) ) ;
}
}
input_sync ( keypad - > dev ) ;
2017-03-28 12:11:49 +02:00
memcpy ( keypad - > last_key_state , new_state , sizeof ( u16 ) * keypad - > cols ) ;
2017-02-09 10:15:52 -08:00
return pressed ;
}
static irqreturn_t ht16k33_keypad_irq_thread ( int irq , void * dev )
{
struct ht16k33_keypad * keypad = dev ;
do {
wait_event_timeout ( keypad - > wait , keypad - > stopped ,
msecs_to_jiffies ( keypad - > debounce_ms ) ) ;
if ( keypad - > stopped )
break ;
} while ( ht16k33_keypad_scan ( keypad ) ) ;
return IRQ_HANDLED ;
}
static int ht16k33_keypad_start ( struct input_dev * dev )
{
struct ht16k33_keypad * keypad = input_get_drvdata ( dev ) ;
keypad - > stopped = false ;
mb ( ) ;
enable_irq ( keypad - > client - > irq ) ;
return 0 ;
}
static void ht16k33_keypad_stop ( struct input_dev * dev )
{
struct ht16k33_keypad * keypad = input_get_drvdata ( dev ) ;
keypad - > stopped = true ;
mb ( ) ;
wake_up ( & keypad - > wait ) ;
disable_irq ( keypad - > client - > irq ) ;
}
2021-10-19 16:45:17 +02:00
static void ht16k33_seg7_update ( struct work_struct * work )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_work_to_priv ( work ) ;
2024-02-19 18:58:06 +02:00
struct linedisp_map * map = priv - > linedisp . map ;
char * s = priv - > linedisp . buf ;
2021-10-19 16:45:17 +02:00
uint8_t buf [ 9 ] ;
2024-02-19 18:58:05 +02:00
buf [ 0 ] = map_to_seg7 ( & map - > map . seg7 , * s + + ) ;
2021-10-19 16:45:17 +02:00
buf [ 1 ] = 0 ;
2024-02-19 18:58:05 +02:00
buf [ 2 ] = map_to_seg7 ( & map - > map . seg7 , * s + + ) ;
2021-10-19 16:45:17 +02:00
buf [ 3 ] = 0 ;
buf [ 4 ] = 0 ;
buf [ 5 ] = 0 ;
2024-02-19 18:58:05 +02:00
buf [ 6 ] = map_to_seg7 ( & map - > map . seg7 , * s + + ) ;
2021-10-19 16:45:17 +02:00
buf [ 7 ] = 0 ;
2024-02-19 18:58:05 +02:00
buf [ 8 ] = map_to_seg7 ( & map - > map . seg7 , * s + + ) ;
2021-10-19 16:45:17 +02:00
i2c_smbus_write_i2c_block_data ( priv - > client , 0 , ARRAY_SIZE ( buf ) , buf ) ;
}
static void ht16k33_seg14_update ( struct work_struct * work )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_work_to_priv ( work ) ;
2024-02-19 18:58:06 +02:00
struct linedisp_map * map = priv - > linedisp . map ;
char * s = priv - > linedisp . buf ;
2021-10-19 16:45:17 +02:00
uint8_t buf [ 8 ] ;
2024-02-19 18:58:05 +02:00
put_unaligned_le16 ( map_to_seg14 ( & map - > map . seg14 , * s + + ) , buf + 0 ) ;
put_unaligned_le16 ( map_to_seg14 ( & map - > map . seg14 , * s + + ) , buf + 2 ) ;
put_unaligned_le16 ( map_to_seg14 ( & map - > map . seg14 , * s + + ) , buf + 4 ) ;
put_unaligned_le16 ( map_to_seg14 ( & map - > map . seg14 , * s + + ) , buf + 6 ) ;
2021-10-19 16:45:17 +02:00
i2c_smbus_write_i2c_block_data ( priv - > client , 0 , ARRAY_SIZE ( buf ) , buf ) ;
}
2024-02-19 18:58:05 +02:00
static int ht16k33_linedisp_get_map_type ( struct linedisp * linedisp )
{
struct ht16k33_priv * priv = ht16k33_linedisp_to_priv ( linedisp ) ;
switch ( priv - > type ) {
case DISP_QUAD_7SEG :
INIT_DELAYED_WORK ( & priv - > work , ht16k33_seg7_update ) ;
return LINEDISP_MAP_SEG7 ;
case DISP_QUAD_14SEG :
INIT_DELAYED_WORK ( & priv - > work , ht16k33_seg14_update ) ;
return LINEDISP_MAP_SEG14 ;
default :
return - EINVAL ;
}
}
2024-02-19 18:58:03 +02:00
static void ht16k33_linedisp_update ( struct linedisp * linedisp )
{
2024-02-19 18:58:04 +02:00
struct ht16k33_priv * priv = ht16k33_linedisp_to_priv ( linedisp ) ;
2024-02-19 18:58:03 +02:00
schedule_delayed_work ( & priv - > work , 0 ) ;
}
static const struct linedisp_ops ht16k33_linedisp_ops = {
2024-02-19 18:58:05 +02:00
. get_map_type = ht16k33_linedisp_get_map_type ,
2024-02-19 18:58:03 +02:00
. update = ht16k33_linedisp_update ,
} ;
2021-10-19 16:45:19 +02:00
static int ht16k33_led_probe ( struct device * dev , struct led_classdev * led ,
unsigned int brightness )
{
struct led_init_data init_data = { } ;
int err ;
/* The LED is optional */
2021-10-19 16:45:20 +02:00
init_data . fwnode = device_get_named_child_node ( dev , " led " ) ;
if ( ! init_data . fwnode )
2021-10-19 16:45:19 +02:00
return 0 ;
init_data . devicename = " auxdisplay " ;
init_data . devname_mandatory = true ;
led - > brightness_set_blocking = ht16k33_brightness_set_blocking ;
led - > blink_set = ht16k33_blink_set ;
led - > flags = LED_CORE_SUSPENDRESUME ;
led - > brightness = brightness ;
led - > max_brightness = MAX_BRIGHTNESS ;
err = devm_led_classdev_register_ext ( dev , led , & init_data ) ;
if ( err )
dev_err ( dev , " Failed to register LED \n " ) ;
return err ;
}
2017-02-09 10:15:52 -08:00
static int ht16k33_keypad_probe ( struct i2c_client * client ,
struct ht16k33_keypad * keypad )
{
2021-10-19 16:45:13 +02:00
struct device * dev = & client - > dev ;
2017-02-09 10:15:52 -08:00
u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS ;
u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS ;
int err ;
keypad - > client = client ;
init_waitqueue_head ( & keypad - > wait ) ;
2021-10-19 16:45:13 +02:00
keypad - > dev = devm_input_allocate_device ( dev ) ;
2017-02-09 10:15:52 -08:00
if ( ! keypad - > dev )
return - ENOMEM ;
input_set_drvdata ( keypad - > dev , keypad ) ;
keypad - > dev - > name = DRIVER_NAME " -keypad " ;
keypad - > dev - > id . bustype = BUS_I2C ;
keypad - > dev - > open = ht16k33_keypad_start ;
keypad - > dev - > close = ht16k33_keypad_stop ;
2021-10-19 16:45:20 +02:00
if ( ! device_property_read_bool ( dev , " linux,no-autorepeat " ) )
2017-02-09 10:15:52 -08:00
__set_bit ( EV_REP , keypad - > dev - > evbit ) ;
2021-10-19 16:45:20 +02:00
err = device_property_read_u32 ( dev , " debounce-delay-ms " ,
& keypad - > debounce_ms ) ;
2017-02-09 10:15:52 -08:00
if ( err ) {
2021-10-19 16:45:13 +02:00
dev_err ( dev , " key debounce delay not specified \n " ) ;
2017-02-09 10:15:52 -08:00
return err ;
}
2021-10-19 16:45:20 +02:00
err = matrix_keypad_parse_properties ( dev , & rows , & cols ) ;
2017-02-09 10:15:52 -08:00
if ( err )
return err ;
2017-03-28 12:11:49 +02:00
if ( rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS | |
cols > HT16K33_MATRIX_KEYPAD_MAX_COLS ) {
2021-10-19 16:45:13 +02:00
dev_err ( dev , " %u rows or %u cols out of range in DT \n " , rows ,
cols ) ;
2017-03-28 12:11:49 +02:00
return - ERANGE ;
}
2017-02-09 10:15:52 -08:00
keypad - > rows = rows ;
keypad - > cols = cols ;
keypad - > row_shift = get_count_order ( cols ) ;
err = matrix_keypad_build_keymap ( NULL , NULL , rows , cols , NULL ,
keypad - > dev ) ;
if ( err ) {
2021-10-19 16:45:13 +02:00
dev_err ( dev , " failed to build keymap \n " ) ;
2017-02-09 10:15:52 -08:00
return err ;
}
2021-10-19 16:45:13 +02:00
err = devm_request_threaded_irq ( dev , client - > irq , NULL ,
ht16k33_keypad_irq_thread ,
2017-02-09 10:15:52 -08:00
IRQF_TRIGGER_HIGH | IRQF_ONESHOT ,
DRIVER_NAME , keypad ) ;
if ( err ) {
2021-10-19 16:45:13 +02:00
dev_err ( dev , " irq request failed %d, error %d \n " , client - > irq ,
err ) ;
2017-02-09 10:15:52 -08:00
return err ;
}
ht16k33_keypad_stop ( keypad - > dev ) ;
2021-10-19 16:45:11 +02:00
return input_register_device ( keypad - > dev ) ;
2017-02-09 10:15:52 -08:00
}
2021-10-19 16:45:16 +02:00
static int ht16k33_fbdev_probe ( struct device * dev , struct ht16k33_priv * priv ,
uint32_t brightness )
2016-11-07 10:56:35 +01:00
{
2021-10-19 16:45:16 +02:00
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 16:45:19 +02:00
struct backlight_device * bl = NULL ;
2021-10-19 16:45:16 +02:00
int err ;
2016-11-07 10:56:35 +01:00
2021-10-19 16:45:19 +02:00
if ( priv - > led . dev ) {
err = ht16k33_brightness_set ( priv , brightness ) ;
if ( err )
return err ;
} else {
/* backwards compatibility with DT lacking an led subnode */
struct backlight_properties bl_props ;
memset ( & bl_props , 0 , sizeof ( struct backlight_properties ) ) ;
bl_props . type = BACKLIGHT_RAW ;
bl_props . max_brightness = MAX_BRIGHTNESS ;
bl = devm_backlight_device_register ( dev , DRIVER_NAME " -bl " , dev ,
priv , & ht16k33_bl_ops ,
& bl_props ) ;
if ( IS_ERR ( bl ) ) {
dev_err ( dev , " failed to register backlight \n " ) ;
return PTR_ERR ( bl ) ;
}
2021-10-19 16:45:08 +02:00
2021-10-19 16:45:19 +02:00
bl - > props . brightness = brightness ;
ht16k33_bl_update_status ( bl ) ;
2021-10-19 16:45:08 +02:00
}
2016-11-07 10:56:35 +01:00
/* Framebuffer (2 bytes per column) */
BUILD_BUG_ON ( PAGE_SIZE < HT16K33_FB_SIZE ) ;
fbdev - > buffer = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
2017-02-09 10:15:53 -08:00
if ( ! fbdev - > buffer )
return - ENOMEM ;
2016-11-07 10:56:35 +01:00
2021-10-19 16:45:13 +02:00
fbdev - > cache = devm_kmalloc ( dev , HT16K33_FB_SIZE , GFP_KERNEL ) ;
2016-11-07 10:56:35 +01:00
if ( ! fbdev - > cache ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
2021-10-19 16:45:13 +02:00
fbdev - > info = framebuffer_alloc ( 0 , dev ) ;
2016-11-07 10:56:35 +01:00
if ( ! fbdev - > info ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
2021-10-19 16:45:20 +02:00
err = device_property_read_u32 ( dev , " refresh-rate-hz " ,
& fbdev - > refresh_rate ) ;
2016-11-07 10:56:35 +01:00
if ( err ) {
2021-10-19 16:45:13 +02:00
dev_err ( dev , " refresh rate not specified \n " ) ;
2016-11-07 10:56:35 +01:00
goto err_fbdev_info ;
}
fb_bl_default_curve ( fbdev - > info , 0 , MIN_BRIGHTNESS , MAX_BRIGHTNESS ) ;
2021-10-19 16:45:14 +02:00
INIT_DELAYED_WORK ( & priv - > work , ht16k33_fb_update ) ;
2016-11-07 10:56:35 +01:00
fbdev - > info - > fbops = & ht16k33_fb_ops ;
2023-11-27 14:15:38 +01:00
fbdev - > info - > flags | = FBINFO_VIRTFB ;
2023-04-28 14:24:35 +02:00
fbdev - > info - > screen_buffer = fbdev - > buffer ;
2016-11-07 10:56:35 +01:00
fbdev - > info - > screen_size = HT16K33_FB_SIZE ;
fbdev - > info - > fix = ht16k33_fb_fix ;
fbdev - > info - > var = ht16k33_fb_var ;
2021-10-19 16:45:08 +02:00
fbdev - > info - > bl_dev = bl ;
2016-11-07 10:56:35 +01:00
fbdev - > info - > pseudo_palette = NULL ;
fbdev - > info - > par = priv ;
err = register_framebuffer ( fbdev - > info ) ;
if ( err )
goto err_fbdev_info ;
ht16k33_fb_queue ( priv ) ;
return 0 ;
err_fbdev_info :
framebuffer_release ( fbdev - > info ) ;
err_fbdev_buffer :
free_page ( ( unsigned long ) fbdev - > buffer ) ;
return err ;
}
2021-10-19 16:45:17 +02:00
static int ht16k33_seg_probe ( struct device * dev , struct ht16k33_priv * priv ,
uint32_t brightness )
{
2024-02-19 18:58:06 +02:00
struct linedisp * linedisp = & priv - > linedisp ;
2021-10-19 16:45:17 +02:00
int err ;
err = ht16k33_brightness_set ( priv , brightness ) ;
if ( err )
return err ;
2024-02-19 18:58:06 +02:00
return linedisp_register ( linedisp , dev , 4 , & ht16k33_linedisp_ops ) ;
2021-10-19 16:45:17 +02:00
}
2021-10-19 16:45:16 +02:00
static int ht16k33_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
2021-10-19 16:45:17 +02:00
const struct of_device_id * id ;
2021-10-19 16:45:16 +02:00
struct ht16k33_priv * priv ;
uint32_t dft_brightness ;
int err ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( dev , " i2c_check_functionality error \n " ) ;
return - EIO ;
}
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > client = client ;
2021-10-19 16:45:17 +02:00
id = i2c_of_match_device ( dev - > driver - > of_match_table , client ) ;
if ( id )
priv - > type = ( uintptr_t ) id - > data ;
2021-10-19 16:45:16 +02:00
i2c_set_clientdata ( client , priv ) ;
err = ht16k33_initialize ( priv ) ;
if ( err )
return err ;
2021-10-19 16:45:20 +02:00
err = device_property_read_u32 ( dev , " default-brightness-level " ,
& dft_brightness ) ;
2021-10-19 16:45:16 +02:00
if ( err ) {
dft_brightness = MAX_BRIGHTNESS ;
} else if ( dft_brightness > MAX_BRIGHTNESS ) {
dev_warn ( dev ,
" invalid default brightness level: %u, using %u \n " ,
dft_brightness , MAX_BRIGHTNESS ) ;
dft_brightness = MAX_BRIGHTNESS ;
}
2021-10-19 16:45:19 +02:00
/* LED */
err = ht16k33_led_probe ( dev , & priv - > led , dft_brightness ) ;
if ( err )
return err ;
2021-10-19 16:45:16 +02:00
/* Keypad */
if ( client - > irq > 0 ) {
err = ht16k33_keypad_probe ( client , & priv - > keypad ) ;
if ( err )
return err ;
}
2021-10-19 16:45:17 +02:00
switch ( priv - > type ) {
case DISP_MATRIX :
/* Frame Buffer Display */
err = ht16k33_fbdev_probe ( dev , priv , dft_brightness ) ;
break ;
case DISP_QUAD_7SEG :
case DISP_QUAD_14SEG :
/* Segment Display */
err = ht16k33_seg_probe ( dev , priv , dft_brightness ) ;
break ;
2024-02-19 18:58:02 +02:00
default :
return - EINVAL ;
2021-10-19 16:45:17 +02:00
}
return err ;
2021-10-19 16:45:16 +02:00
}
2022-08-15 10:02:30 +02:00
static void ht16k33_remove ( struct i2c_client * client )
2016-11-07 10:56:35 +01:00
{
struct ht16k33_priv * priv = i2c_get_clientdata ( client ) ;
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 16:45:14 +02:00
cancel_delayed_work_sync ( & priv - > work ) ;
2021-10-19 16:45:17 +02:00
switch ( priv - > type ) {
case DISP_MATRIX :
unregister_framebuffer ( fbdev - > info ) ;
framebuffer_release ( fbdev - > info ) ;
free_page ( ( unsigned long ) fbdev - > buffer ) ;
break ;
case DISP_QUAD_7SEG :
case DISP_QUAD_14SEG :
2024-02-19 18:58:06 +02:00
linedisp_unregister ( & priv - > linedisp ) ;
2021-10-19 16:45:17 +02:00
break ;
2024-02-19 18:58:02 +02:00
default :
break ;
2021-10-19 16:45:17 +02:00
}
2016-11-07 10:56:35 +01:00
}
static const struct i2c_device_id ht16k33_i2c_match [ ] = {
{ " ht16k33 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ht16k33_i2c_match ) ;
static const struct of_device_id ht16k33_of_match [ ] = {
2021-10-19 16:45:17 +02:00
{
/* 0.56" 4-Digit 7-Segment FeatherWing Display (Red) */
. compatible = " adafruit,3108 " , . data = ( void * ) DISP_QUAD_7SEG ,
} , {
/* 0.54" Quad Alphanumeric FeatherWing Display (Red) */
. compatible = " adafruit,3130 " , . data = ( void * ) DISP_QUAD_14SEG ,
} , {
/* Generic, assumed Dot-Matrix Display */
. compatible = " holtek,ht16k33 " , . data = ( void * ) DISP_MATRIX ,
} ,
2016-11-07 10:56:35 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , ht16k33_of_match ) ;
static struct i2c_driver ht16k33_driver = {
2023-05-25 22:58:40 +02:00
. probe = ht16k33_probe ,
2016-11-07 10:56:35 +01:00
. remove = ht16k33_remove ,
. driver = {
. name = DRIVER_NAME ,
2021-10-19 16:45:20 +02:00
. of_match_table = ht16k33_of_match ,
2016-11-07 10:56:35 +01:00
} ,
. id_table = ht16k33_i2c_match ,
} ;
module_i2c_driver ( ht16k33_driver ) ;
MODULE_DESCRIPTION ( " Holtek HT16K33 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2024-02-12 19:01:39 +02:00
MODULE_IMPORT_NS ( LINEDISP ) ;
2016-11-07 10:56:35 +01:00
MODULE_AUTHOR ( " Robin van der Gracht <robin@protonic.nl> " ) ;