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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/i2c.h>
# include <linux/of.h>
# include <linux/fb.h>
# include <linux/slab.h>
# include <linux/backlight.h>
# include <linux/input.h>
# include <linux/input/matrix_keypad.h>
# include <linux/workqueue.h>
# include <linux/mm.h>
/* 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)
# 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)
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 delayed_work work ;
} ;
struct ht16k33_priv {
struct i2c_client * client ;
struct ht16k33_keypad keypad ;
struct ht16k33_fbdev fbdev ;
} ;
static struct fb_fix_screeninfo ht16k33_fb_fix = {
. 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 ,
} ;
static struct fb_var_screeninfo ht16k33_fb_var = {
. 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 )
{
uint8_t data = REG_DISPLAY_SETUP | REG_DISPLAY_SETUP_ON ;
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 ) ;
}
static void ht16k33_fb_queue ( struct ht16k33_priv * priv )
{
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
2017-02-09 10:15:53 -08:00
schedule_delayed_work ( & fbdev - > work ,
msecs_to_jiffies ( 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 )
{
struct ht16k33_fbdev * fbdev =
container_of ( work , struct ht16k33_fbdev , work . work ) ;
struct ht16k33_priv * priv =
container_of ( fbdev , struct ht16k33_priv , fbdev ) ;
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 )
{
uint8_t byte ;
int err ;
uint8_t data [ HT16K33_MATRIX_LED_MAX_COLS * 2 ] ;
/* 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 | |
bl - > props . state & BL_CORE_FBBLANK | | brightness = = 0 ) {
return ht16k33_display_off ( priv ) ;
}
ht16k33_display_on ( priv ) ;
return i2c_smbus_write_byte ( priv - > client ,
REG_BRIGHTNESS | ( brightness - 1 ) ) ;
}
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 ,
} ;
static int ht16k33_mmap ( struct fb_info * info , struct vm_area_struct * vma )
{
struct ht16k33_priv * priv = info - > par ;
return vm_insert_page ( vma , vma - > vm_start ,
virt_to_page ( priv - > fbdev . buffer ) ) ;
}
static struct fb_ops ht16k33_fb_ops = {
. owner = THIS_MODULE ,
. fb_read = fb_sys_read ,
. fb_write = fb_sys_write ,
. fb_fillrect = sys_fillrect ,
. fb_copyarea = sys_copyarea ,
. fb_imageblit = sys_imageblit ,
. 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 ) ;
}
static int ht16k33_keypad_probe ( struct i2c_client * client ,
struct ht16k33_keypad * keypad )
{
struct device_node * node = client - > dev . of_node ;
u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS ;
u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS ;
int err ;
keypad - > client = client ;
init_waitqueue_head ( & keypad - > wait ) ;
keypad - > dev = devm_input_allocate_device ( & client - > dev ) ;
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 ;
if ( ! of_get_property ( node , " linux,no-autorepeat " , NULL ) )
__set_bit ( EV_REP , keypad - > dev - > evbit ) ;
err = of_property_read_u32 ( node , " debounce-delay-ms " ,
& keypad - > debounce_ms ) ;
if ( err ) {
dev_err ( & client - > dev , " key debounce delay not specified \n " ) ;
return err ;
}
err = matrix_keypad_parse_of_params ( & client - > dev , & rows , & cols ) ;
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 ) {
dev_err ( & client - > dev , " %u rows or %u cols out of range in DT \n " ,
rows , cols ) ;
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 ) {
dev_err ( & client - > dev , " failed to build keymap \n " ) ;
return err ;
}
err = devm_request_threaded_irq ( & client - > dev , client - > irq ,
NULL , ht16k33_keypad_irq_thread ,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT ,
DRIVER_NAME , keypad ) ;
if ( err ) {
dev_err ( & client - > dev , " irq request failed %d, error %d \n " ,
client - > irq , err ) ;
return err ;
}
ht16k33_keypad_stop ( keypad - > dev ) ;
err = input_register_device ( keypad - > dev ) ;
if ( err )
return err ;
return 0 ;
}
2016-11-07 10:56:35 +01:00
static int ht16k33_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int err ;
2017-02-09 10:15:52 -08:00
uint32_t dft_brightness ;
2016-11-07 10:56:35 +01:00
struct backlight_device * bl ;
struct backlight_properties bl_props ;
struct ht16k33_priv * priv ;
struct ht16k33_fbdev * fbdev ;
struct device_node * node = client - > dev . of_node ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( & client - > dev , " i2c_check_functionality error \n " ) ;
return - EIO ;
}
if ( client - > irq < = 0 ) {
dev_err ( & client - > dev , " No IRQ specified \n " ) ;
return - EINVAL ;
}
priv = devm_kzalloc ( & client - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > client = client ;
i2c_set_clientdata ( client , priv ) ;
fbdev = & priv - > fbdev ;
err = ht16k33_initialize ( priv ) ;
if ( err )
2017-02-09 10:15:53 -08:00
return err ;
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
fbdev - > cache = devm_kmalloc ( & client - > dev , HT16K33_FB_SIZE , GFP_KERNEL ) ;
if ( ! fbdev - > cache ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
fbdev - > info = framebuffer_alloc ( 0 , & client - > dev ) ;
if ( ! fbdev - > info ) {
err = - ENOMEM ;
goto err_fbdev_buffer ;
}
err = of_property_read_u32 ( node , " refresh-rate-hz " ,
& fbdev - > refresh_rate ) ;
if ( err ) {
dev_err ( & client - > dev , " refresh rate not specified \n " ) ;
goto err_fbdev_info ;
}
fb_bl_default_curve ( fbdev - > info , 0 , MIN_BRIGHTNESS , MAX_BRIGHTNESS ) ;
INIT_DELAYED_WORK ( & fbdev - > work , ht16k33_fb_update ) ;
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 ;
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 ;
2017-02-09 10:15:52 -08:00
err = ht16k33_keypad_probe ( client , & priv - > keypad ) ;
2016-11-07 10:56:35 +01:00
if ( err )
goto err_fbdev_unregister ;
/* Backlight */
memset ( & bl_props , 0 , sizeof ( struct backlight_properties ) ) ;
bl_props . type = BACKLIGHT_RAW ;
bl_props . max_brightness = MAX_BRIGHTNESS ;
bl = devm_backlight_device_register ( & client - > dev , DRIVER_NAME " -bl " ,
& client - > dev , priv ,
& ht16k33_bl_ops , & bl_props ) ;
if ( IS_ERR ( bl ) ) {
dev_err ( & client - > dev , " failed to register backlight \n " ) ;
err = PTR_ERR ( bl ) ;
2017-02-09 10:15:52 -08:00
goto err_fbdev_unregister ;
2016-11-07 10:56:35 +01:00
}
err = of_property_read_u32 ( node , " default-brightness-level " ,
& dft_brightness ) ;
if ( err ) {
dft_brightness = MAX_BRIGHTNESS ;
} else if ( dft_brightness > MAX_BRIGHTNESS ) {
dev_warn ( & client - > dev ,
" invalid default brightness level: %u, using %u \n " ,
dft_brightness , MAX_BRIGHTNESS ) ;
dft_brightness = MAX_BRIGHTNESS ;
}
bl - > props . brightness = dft_brightness ;
ht16k33_bl_update_status ( bl ) ;
ht16k33_fb_queue ( priv ) ;
return 0 ;
err_fbdev_unregister :
unregister_framebuffer ( fbdev - > info ) ;
err_fbdev_info :
framebuffer_release ( fbdev - > info ) ;
err_fbdev_buffer :
free_page ( ( unsigned long ) fbdev - > buffer ) ;
return err ;
}
static int ht16k33_remove ( struct i2c_client * client )
{
struct ht16k33_priv * priv = i2c_get_clientdata ( client ) ;
struct ht16k33_fbdev * fbdev = & priv - > fbdev ;
cancel_delayed_work ( & fbdev - > work ) ;
unregister_framebuffer ( fbdev - > info ) ;
framebuffer_release ( fbdev - > info ) ;
free_page ( ( unsigned long ) fbdev - > buffer ) ;
return 0 ;
}
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 [ ] = {
{ . compatible = " holtek,ht16k33 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ht16k33_of_match ) ;
static struct i2c_driver ht16k33_driver = {
. probe = ht16k33_probe ,
. remove = ht16k33_remove ,
. driver = {
. name = DRIVER_NAME ,
. of_match_table = of_match_ptr ( ht16k33_of_match ) ,
} ,
. 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> " ) ;