2018-02-17 22:33:13 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-11-07 12:56:35 +03:00
/*
* HT16K33 driver
*
* Author : Robin van der Gracht < robin @ protonic . nl >
*
* Copyright : ( C ) 2016 Protonic Holland .
2021-10-19 17:45:17 +03:00
* Copyright ( C ) 2021 Glider bv
2016-11-07 12:56:35 +03:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
2021-10-19 17:45:20 +03:00
# include <linux/property.h>
2016-11-07 12:56:35 +03:00
# include <linux/fb.h>
# include <linux/backlight.h>
# include <linux/input.h>
# include <linux/input/matrix_keypad.h>
2021-10-19 17:45:19 +03:00
# include <linux/leds.h>
2016-11-07 12:56:35 +03:00
# include <linux/workqueue.h>
# include <linux/mm.h>
2021-10-19 17:45:17 +03:00
# include <linux/map_to_7segment.h>
# include <linux/map_to_14segment.h>
# include <asm/unaligned.h>
# include "line-display.h"
2016-11-07 12:56:35 +03: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 17:45:19 +03: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 12:56:35 +03: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 17:45:17 +03:00
enum display_type {
DISP_MATRIX = 0 ,
DISP_QUAD_7SEG ,
DISP_QUAD_14SEG ,
} ;
2016-11-07 12:56:35 +03:00
struct ht16k33_keypad {
2017-02-09 21:15:52 +03:00
struct i2c_client * client ;
2016-11-07 12:56:35 +03: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 21:15:52 +03:00
wait_queue_head_t wait ;
bool stopped ;
2016-11-07 12:56:35 +03:00
} ;
struct ht16k33_fbdev {
struct fb_info * info ;
uint32_t refresh_rate ;
uint8_t * buffer ;
uint8_t * cache ;
} ;
2021-10-19 17:45:17 +03:00
struct ht16k33_seg {
struct linedisp linedisp ;
union {
struct seg7_conversion_map seg7 ;
struct seg14_conversion_map seg14 ;
} map ;
unsigned int map_size ;
char curr [ 4 ] ;
} ;
2016-11-07 12:56:35 +03:00
struct ht16k33_priv {
struct i2c_client * client ;
2021-10-19 17:45:14 +03:00
struct delayed_work work ;
2021-10-19 17:45:19 +03:00
struct led_classdev led ;
2016-11-07 12:56:35 +03:00
struct ht16k33_keypad keypad ;
2021-10-19 17:45:17 +03:00
union {
struct ht16k33_fbdev fbdev ;
struct ht16k33_seg seg ;
} ;
enum display_type type ;
2021-10-19 17:45:19 +03:00
uint8_t blink ;
2016-11-07 12:56:35 +03:00
} ;
2019-08-19 10:51:26 +03:00
static const struct fb_fix_screeninfo ht16k33_fb_fix = {
2016-11-07 12:56:35 +03: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 10:51:26 +03:00
static const struct fb_var_screeninfo ht16k33_fb_var = {
2016-11-07 12:56:35 +03: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 ,
} ;
2021-10-19 17:45:17 +03:00
static const SEG7_DEFAULT_MAP ( initial_map_seg7 ) ;
static const SEG14_DEFAULT_MAP ( initial_map_seg14 ) ;
static ssize_t map_seg_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct ht16k33_priv * priv = dev_get_drvdata ( dev ) ;
memcpy ( buf , & priv - > seg . map , priv - > seg . map_size ) ;
return priv - > seg . map_size ;
}
static ssize_t map_seg_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t cnt )
{
struct ht16k33_priv * priv = dev_get_drvdata ( dev ) ;
if ( cnt ! = priv - > seg . map_size )
return - EINVAL ;
memcpy ( & priv - > seg . map , buf , cnt ) ;
return cnt ;
}
static DEVICE_ATTR ( map_seg7 , 0644 , map_seg_show , map_seg_store ) ;
static DEVICE_ATTR ( map_seg14 , 0644 , map_seg_show , map_seg_store ) ;
2016-11-07 12:56:35 +03:00
static int ht16k33_display_on ( struct ht16k33_priv * priv )
{
2021-10-19 17:45:19 +03:00
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON | priv - > blink ;
2016-11-07 12:56:35 +03: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 17:45:15 +03:00
static int ht16k33_brightness_set ( struct ht16k33_priv * priv ,
unsigned int brightness )
{
int err ;
2021-10-19 17:45:19 +03:00
if ( brightness = = 0 ) {
priv - > blink = REG_DISPLAY_SETUP_BLINK_OFF ;
2021-10-19 17:45:15 +03:00
return ht16k33_display_off ( priv ) ;
2021-10-19 17:45:19 +03:00
}
2021-10-19 17:45:15 +03:00
err = ht16k33_display_on ( priv ) ;
if ( err )
return err ;
return i2c_smbus_write_byte ( priv - > client ,
REG_BRIGHTNESS | ( brightness - 1 ) ) ;
}
2021-10-19 17:45:19 +03:00
static int ht16k33_brightness_set_blocking ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct ht16k33_priv * priv = container_of ( led_cdev , struct ht16k33_priv ,
led ) ;
return ht16k33_brightness_set ( priv , brightness ) ;
}
static int ht16k33_blink_set ( struct led_classdev * led_cdev ,
unsigned long * delay_on , unsigned long * delay_off )
{
struct ht16k33_priv * priv = container_of ( led_cdev , struct ht16k33_priv ,
led ) ;
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 12:56:35 +03:00
static void ht16k33_fb_queue ( struct ht16k33_priv * priv )
{
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 17:45:14 +03:00
schedule_delayed_work ( & priv - > work , HZ / fbdev - > refresh_rate ) ;
2016-11-07 12:56:35 +03:00
}
/*
* This gets the fb data from cache and copies it to ht16k33 display RAM
*/
static void ht16k33_fb_update ( struct work_struct * work )
{
2021-10-19 17:45:14 +03:00
struct ht16k33_priv * priv = container_of ( work , struct ht16k33_priv ,
work . work ) ;
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2016-11-07 12:56:35 +03: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 17:45:10 +03:00
uint8_t data [ HT16K33_FB_SIZE ] ;
2016-11-07 12:56:35 +03: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 17:45:15 +03:00
bl - > props . state & BL_CORE_FBBLANK )
brightness = 0 ;
2016-11-07 12:56:35 +03:00
2021-10-19 17:45:15 +03:00
return ht16k33_brightness_set ( priv , brightness ) ;
2016-11-07 12:56:35 +03: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 17:45:09 +03: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 12:56:35 +03:00
static int ht16k33_mmap ( struct fb_info * info , struct vm_area_struct * vma )
{
struct ht16k33_priv * priv = info - > par ;
2019-05-26 19:11:10 +03:00
struct page * pages = virt_to_page ( priv - > fbdev . buffer ) ;
2016-11-07 12:56:35 +03:00
2019-05-26 19:11:10 +03:00
return vm_map_pages_zero ( vma , & pages , 1 ) ;
2016-11-07 12:56:35 +03:00
}
2019-12-03 19:38:54 +03:00
static const struct fb_ops ht16k33_fb_ops = {
2016-11-07 12:56:35 +03:00
. owner = THIS_MODULE ,
. fb_read = fb_sys_read ,
. fb_write = fb_sys_write ,
2021-10-19 17:45:09 +03:00
. fb_blank = ht16k33_blank ,
2016-11-07 12:56:35 +03:00
. fb_fillrect = sys_fillrect ,
. fb_copyarea = sys_copyarea ,
. fb_imageblit = sys_imageblit ,
. fb_mmap = ht16k33_mmap ,
} ;
2017-02-09 21:15:52 +03: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 20:27:14 +03:00
__le16 data [ HT16K33_MATRIX_KEYPAD_MAX_COLS ] ;
2017-02-09 21:15:52 +03:00
unsigned long bits_changed ;
int row , col , code ;
2017-04-01 20:27:14 +03:00
int rc ;
2017-02-09 21:15:52 +03:00
bool pressed = false ;
2017-04-01 20:27:14 +03: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 21:15:52 +03:00
return false ;
}
for ( col = 0 ; col < keypad - > cols ; col + + ) {
2017-04-01 20:27:14 +03:00
new_state [ col ] = le16_to_cpu ( data [ col ] ) ;
2017-02-09 21:15:52 +03: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 13:11:49 +03:00
memcpy ( keypad - > last_key_state , new_state , sizeof ( u16 ) * keypad - > cols ) ;
2017-02-09 21:15:52 +03: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 17:45:17 +03:00
static void ht16k33_linedisp_update ( struct linedisp * linedisp )
{
struct ht16k33_priv * priv = container_of ( linedisp , struct ht16k33_priv ,
seg . linedisp ) ;
schedule_delayed_work ( & priv - > work , 0 ) ;
}
static void ht16k33_seg7_update ( struct work_struct * work )
{
struct ht16k33_priv * priv = container_of ( work , struct ht16k33_priv ,
work . work ) ;
struct ht16k33_seg * seg = & priv - > seg ;
char * s = seg - > curr ;
uint8_t buf [ 9 ] ;
buf [ 0 ] = map_to_seg7 ( & seg - > map . seg7 , * s + + ) ;
buf [ 1 ] = 0 ;
buf [ 2 ] = map_to_seg7 ( & seg - > map . seg7 , * s + + ) ;
buf [ 3 ] = 0 ;
buf [ 4 ] = 0 ;
buf [ 5 ] = 0 ;
buf [ 6 ] = map_to_seg7 ( & seg - > map . seg7 , * s + + ) ;
buf [ 7 ] = 0 ;
buf [ 8 ] = map_to_seg7 ( & seg - > map . seg7 , * s + + ) ;
i2c_smbus_write_i2c_block_data ( priv - > client , 0 , ARRAY_SIZE ( buf ) , buf ) ;
}
static void ht16k33_seg14_update ( struct work_struct * work )
{
struct ht16k33_priv * priv = container_of ( work , struct ht16k33_priv ,
work . work ) ;
struct ht16k33_seg * seg = & priv - > seg ;
char * s = seg - > curr ;
uint8_t buf [ 8 ] ;
put_unaligned_le16 ( map_to_seg14 ( & seg - > map . seg14 , * s + + ) , buf ) ;
put_unaligned_le16 ( map_to_seg14 ( & seg - > map . seg14 , * s + + ) , buf + 2 ) ;
put_unaligned_le16 ( map_to_seg14 ( & seg - > map . seg14 , * s + + ) , buf + 4 ) ;
put_unaligned_le16 ( map_to_seg14 ( & seg - > map . seg14 , * s + + ) , buf + 6 ) ;
i2c_smbus_write_i2c_block_data ( priv - > client , 0 , ARRAY_SIZE ( buf ) , buf ) ;
}
2021-10-19 17:45:19 +03: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 17:45:20 +03:00
init_data . fwnode = device_get_named_child_node ( dev , " led " ) ;
if ( ! init_data . fwnode )
2021-10-19 17:45:19 +03: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 21:15:52 +03:00
static int ht16k33_keypad_probe ( struct i2c_client * client ,
struct ht16k33_keypad * keypad )
{
2021-10-19 17:45:13 +03:00
struct device * dev = & client - > dev ;
2017-02-09 21:15:52 +03: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 17:45:13 +03:00
keypad - > dev = devm_input_allocate_device ( dev ) ;
2017-02-09 21:15:52 +03: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 17:45:20 +03:00
if ( ! device_property_read_bool ( dev , " linux,no-autorepeat " ) )
2017-02-09 21:15:52 +03:00
__set_bit ( EV_REP , keypad - > dev - > evbit ) ;
2021-10-19 17:45:20 +03:00
err = device_property_read_u32 ( dev , " debounce-delay-ms " ,
& keypad - > debounce_ms ) ;
2017-02-09 21:15:52 +03:00
if ( err ) {
2021-10-19 17:45:13 +03:00
dev_err ( dev , " key debounce delay not specified \n " ) ;
2017-02-09 21:15:52 +03:00
return err ;
}
2021-10-19 17:45:20 +03:00
err = matrix_keypad_parse_properties ( dev , & rows , & cols ) ;
2017-02-09 21:15:52 +03:00
if ( err )
return err ;
2017-03-28 13:11:49 +03:00
if ( rows > HT16K33_MATRIX_KEYPAD_MAX_ROWS | |
cols > HT16K33_MATRIX_KEYPAD_MAX_COLS ) {
2021-10-19 17:45:13 +03:00
dev_err ( dev , " %u rows or %u cols out of range in DT \n " , rows ,
cols ) ;
2017-03-28 13:11:49 +03:00
return - ERANGE ;
}
2017-02-09 21:15:52 +03: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 17:45:13 +03:00
dev_err ( dev , " failed to build keymap \n " ) ;
2017-02-09 21:15:52 +03:00
return err ;
}
2021-10-19 17:45:13 +03:00
err = devm_request_threaded_irq ( dev , client - > irq , NULL ,
ht16k33_keypad_irq_thread ,
2017-02-09 21:15:52 +03:00
IRQF_TRIGGER_HIGH | IRQF_ONESHOT ,
DRIVER_NAME , keypad ) ;
if ( err ) {
2021-10-19 17:45:13 +03:00
dev_err ( dev , " irq request failed %d, error %d \n " , client - > irq ,
err ) ;
2017-02-09 21:15:52 +03:00
return err ;
}
ht16k33_keypad_stop ( keypad - > dev ) ;
2021-10-19 17:45:11 +03:00
return input_register_device ( keypad - > dev ) ;
2017-02-09 21:15:52 +03:00
}
2021-10-19 17:45:16 +03:00
static int ht16k33_fbdev_probe ( struct device * dev , struct ht16k33_priv * priv ,
uint32_t brightness )
2016-11-07 12:56:35 +03:00
{
2021-10-19 17:45:16 +03:00
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 17:45:19 +03:00
struct backlight_device * bl = NULL ;
2021-10-19 17:45:16 +03:00
int err ;
2016-11-07 12:56:35 +03:00
2021-10-19 17:45:19 +03: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 17:45:08 +03:00
2021-10-19 17:45:19 +03:00
bl - > props . brightness = brightness ;
ht16k33_bl_update_status ( bl ) ;
2021-10-19 17:45:08 +03:00
}
2016-11-07 12:56:35 +03: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 21:15:53 +03:00
if ( ! fbdev - > buffer )
return - ENOMEM ;
2016-11-07 12:56:35 +03:00
2021-10-19 17:45:13 +03:00
fbdev - > cache = devm_kmalloc ( dev , HT16K33_FB_SIZE , GFP_KERNEL ) ;
2016-11-07 12:56:35 +03:00
if ( ! fbdev - > cache ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
2021-10-19 17:45:13 +03:00
fbdev - > info = framebuffer_alloc ( 0 , dev ) ;
2016-11-07 12:56:35 +03:00
if ( ! fbdev - > info ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
2021-10-19 17:45:20 +03:00
err = device_property_read_u32 ( dev , " refresh-rate-hz " ,
& fbdev - > refresh_rate ) ;
2016-11-07 12:56:35 +03:00
if ( err ) {
2021-10-19 17:45:13 +03:00
dev_err ( dev , " refresh rate not specified \n " ) ;
2016-11-07 12:56:35 +03:00
goto err_fbdev_info ;
}
fb_bl_default_curve ( fbdev - > info , 0 , MIN_BRIGHTNESS , MAX_BRIGHTNESS ) ;
2021-10-19 17:45:14 +03:00
INIT_DELAYED_WORK ( & priv - > work , ht16k33_fb_update ) ;
2016-11-07 12:56:35 +03:00
fbdev - > info - > fbops = & ht16k33_fb_ops ;
fbdev - > info - > screen_base = ( char __iomem * ) fbdev - > buffer ;
fbdev - > info - > screen_size = HT16K33_FB_SIZE ;
fbdev - > info - > fix = ht16k33_fb_fix ;
fbdev - > info - > var = ht16k33_fb_var ;
2021-10-19 17:45:08 +03:00
fbdev - > info - > bl_dev = bl ;
2016-11-07 12:56:35 +03:00
fbdev - > info - > pseudo_palette = NULL ;
fbdev - > info - > flags = FBINFO_FLAG_DEFAULT ;
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 17:45:17 +03:00
static int ht16k33_seg_probe ( struct device * dev , struct ht16k33_priv * priv ,
uint32_t brightness )
{
struct ht16k33_seg * seg = & priv - > seg ;
int err ;
err = ht16k33_brightness_set ( priv , brightness ) ;
if ( err )
return err ;
switch ( priv - > type ) {
case DISP_MATRIX :
/* not handled here */
err = - EINVAL ;
break ;
case DISP_QUAD_7SEG :
INIT_DELAYED_WORK ( & priv - > work , ht16k33_seg7_update ) ;
seg - > map . seg7 = initial_map_seg7 ;
seg - > map_size = sizeof ( seg - > map . seg7 ) ;
err = device_create_file ( dev , & dev_attr_map_seg7 ) ;
break ;
case DISP_QUAD_14SEG :
INIT_DELAYED_WORK ( & priv - > work , ht16k33_seg14_update ) ;
seg - > map . seg14 = initial_map_seg14 ;
seg - > map_size = sizeof ( seg - > map . seg14 ) ;
err = device_create_file ( dev , & dev_attr_map_seg14 ) ;
break ;
}
if ( err )
return err ;
err = linedisp_register ( & seg - > linedisp , dev , 4 , seg - > curr ,
ht16k33_linedisp_update ) ;
if ( err )
goto err_remove_map_file ;
return 0 ;
err_remove_map_file :
device_remove_file ( dev , & dev_attr_map_seg7 ) ;
device_remove_file ( dev , & dev_attr_map_seg14 ) ;
return err ;
}
2021-10-19 17:45:16 +03:00
static int ht16k33_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
2021-10-19 17:45:17 +03:00
const struct of_device_id * id ;
2021-10-19 17:45:16 +03: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 17:45:17 +03:00
id = i2c_of_match_device ( dev - > driver - > of_match_table , client ) ;
if ( id )
priv - > type = ( uintptr_t ) id - > data ;
2021-10-19 17:45:16 +03:00
i2c_set_clientdata ( client , priv ) ;
err = ht16k33_initialize ( priv ) ;
if ( err )
return err ;
2021-10-19 17:45:20 +03:00
err = device_property_read_u32 ( dev , " default-brightness-level " ,
& dft_brightness ) ;
2021-10-19 17:45:16 +03: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 17:45:19 +03:00
/* LED */
err = ht16k33_led_probe ( dev , & priv - > led , dft_brightness ) ;
if ( err )
return err ;
2021-10-19 17:45:16 +03:00
/* Keypad */
if ( client - > irq > 0 ) {
err = ht16k33_keypad_probe ( client , & priv - > keypad ) ;
if ( err )
return err ;
}
2021-10-19 17:45:17 +03: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 ;
}
return err ;
2021-10-19 17:45:16 +03:00
}
2022-08-15 11:02:30 +03:00
static void ht16k33_remove ( struct i2c_client * client )
2016-11-07 12:56:35 +03:00
{
struct ht16k33_priv * priv = i2c_get_clientdata ( client ) ;
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2021-10-19 17:45:14 +03:00
cancel_delayed_work_sync ( & priv - > work ) ;
2021-10-19 17:45:17 +03: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 :
linedisp_unregister ( & priv - > seg . linedisp ) ;
device_remove_file ( & client - > dev , & dev_attr_map_seg7 ) ;
device_remove_file ( & client - > dev , & dev_attr_map_seg14 ) ;
break ;
}
2016-11-07 12:56:35 +03: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 17:45:17 +03: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 12:56:35 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , ht16k33_of_match ) ;
static struct i2c_driver ht16k33_driver = {
2021-10-19 17:45:12 +03:00
. probe_new = ht16k33_probe ,
2016-11-07 12:56:35 +03:00
. remove = ht16k33_remove ,
. driver = {
. name = DRIVER_NAME ,
2021-10-19 17:45:20 +03:00
. of_match_table = ht16k33_of_match ,
2016-11-07 12:56:35 +03:00
} ,
. id_table = ht16k33_i2c_match ,
} ;
module_i2c_driver ( ht16k33_driver ) ;
MODULE_DESCRIPTION ( " Holtek HT16K33 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Robin van der Gracht <robin@protonic.nl> " ) ;