2019-06-03 08:44:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-02-27 09:42:22 +03:00
/*
* sky81452 - backlight . c SKY81452 backlight driver
*
* Copyright 2014 Skyworks Solutions Inc .
* Author : Gyungoh Yoo < jack . yoo @ skyworksinc . com >
*/
# include <linux/backlight.h>
# include <linux/err.h>
2020-06-26 23:37:41 +03:00
# include <linux/gpio/consumer.h>
2015-02-27 09:42:22 +03:00
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
/* registers */
# define SKY81452_REG0 0x00
# define SKY81452_REG1 0x01
# define SKY81452_REG2 0x02
# define SKY81452_REG4 0x04
# define SKY81452_REG5 0x05
/* bit mask */
# define SKY81452_CS 0xFF
# define SKY81452_EN 0x3F
# define SKY81452_IGPW 0x20
# define SKY81452_PWMMD 0x10
# define SKY81452_PHASE 0x08
# define SKY81452_ILIM 0x04
# define SKY81452_VSHRT 0x03
# define SKY81452_OCP 0x80
# define SKY81452_OTMP 0x40
# define SKY81452_SHRT 0x3F
# define SKY81452_OPN 0x3F
# define SKY81452_DEFAULT_NAME "lcd-backlight"
# define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1)
2020-06-26 23:37:42 +03:00
/**
* struct sky81452_platform_data
* @ name : backlight driver name .
2020-07-06 10:44:21 +03:00
* If it is not defined , default name is lcd - backlight .
* @ gpiod_enable : GPIO descriptor which control EN pin
2020-06-26 23:37:42 +03:00
* @ enable : Enable mask for current sink channel 1 , 2 , 3 , 4 , 5 and 6.
* @ ignore_pwm : true if DPWMI should be ignored .
* @ dpwm_mode : true is DPWM dimming mode , otherwise Analog dimming mode .
* @ phase_shift : true is phase shift mode .
2020-07-06 10:44:21 +03:00
* @ short_detection_threshold : It should be one of 4 , 5 , 6 and 7 V .
2020-06-26 23:37:42 +03:00
* @ boost_current_limit : It should be one of 2300 , 2750 mA .
*/
struct sky81452_bl_platform_data {
const char * name ;
struct gpio_desc * gpiod_enable ;
unsigned int enable ;
bool ignore_pwm ;
bool dpwm_mode ;
bool phase_shift ;
unsigned int short_detection_threshold ;
unsigned int boost_current_limit ;
} ;
2015-02-27 09:42:22 +03:00
# define CTZ(b) __builtin_ctz(b)
static int sky81452_bl_update_status ( struct backlight_device * bd )
{
const struct sky81452_bl_platform_data * pdata =
dev_get_platdata ( bd - > dev . parent ) ;
const unsigned int brightness = ( unsigned int ) bd - > props . brightness ;
struct regmap * regmap = bl_get_data ( bd ) ;
int ret ;
if ( brightness > 0 ) {
ret = regmap_write ( regmap , SKY81452_REG0 , brightness - 1 ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
return ret ;
return regmap_update_bits ( regmap , SKY81452_REG1 , SKY81452_EN ,
pdata - > enable < < CTZ ( SKY81452_EN ) ) ;
}
return regmap_update_bits ( regmap , SKY81452_REG1 , SKY81452_EN , 0 ) ;
}
static const struct backlight_ops sky81452_bl_ops = {
. update_status = sky81452_bl_update_status ,
} ;
static ssize_t sky81452_bl_store_enable ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
{
struct regmap * regmap = bl_get_data ( to_backlight_device ( dev ) ) ;
unsigned long value ;
int ret ;
ret = kstrtoul ( buf , 16 , & value ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
return ret ;
ret = regmap_update_bits ( regmap , SKY81452_REG1 , SKY81452_EN ,
value < < CTZ ( SKY81452_EN ) ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
return ret ;
return count ;
}
static ssize_t sky81452_bl_show_open_short ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct regmap * regmap = bl_get_data ( to_backlight_device ( dev ) ) ;
unsigned int reg , value = 0 ;
char tmp [ 3 ] ;
int i , ret ;
reg = ! strcmp ( attr - > attr . name , " open " ) ? SKY81452_REG5 : SKY81452_REG4 ;
ret = regmap_read ( regmap , reg , & value ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
return ret ;
if ( value & SKY81452_SHRT ) {
* buf = 0 ;
for ( i = 0 ; i < 6 ; i + + ) {
if ( value & 0x01 ) {
sprintf ( tmp , " %d " , i + 1 ) ;
strcat ( buf , tmp ) ;
}
value > > = 1 ;
}
strcat ( buf , " \n " ) ;
} else {
strcpy ( buf , " none \n " ) ;
}
return strlen ( buf ) ;
}
static ssize_t sky81452_bl_show_fault ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct regmap * regmap = bl_get_data ( to_backlight_device ( dev ) ) ;
unsigned int value = 0 ;
int ret ;
ret = regmap_read ( regmap , SKY81452_REG4 , & value ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
return ret ;
* buf = 0 ;
if ( value & SKY81452_OCP )
strcat ( buf , " over-current " ) ;
if ( value & SKY81452_OTMP )
strcat ( buf , " over-temperature " ) ;
strcat ( buf , " \n " ) ;
return strlen ( buf ) ;
}
static DEVICE_ATTR ( enable , S_IWGRP | S_IWUSR , NULL , sky81452_bl_store_enable ) ;
static DEVICE_ATTR ( open , S_IRUGO , sky81452_bl_show_open_short , NULL ) ;
static DEVICE_ATTR ( short , S_IRUGO , sky81452_bl_show_open_short , NULL ) ;
static DEVICE_ATTR ( fault , S_IRUGO , sky81452_bl_show_fault , NULL ) ;
static struct attribute * sky81452_bl_attribute [ ] = {
& dev_attr_enable . attr ,
& dev_attr_open . attr ,
& dev_attr_short . attr ,
& dev_attr_fault . attr ,
NULL
} ;
static const struct attribute_group sky81452_bl_attr_group = {
. attrs = sky81452_bl_attribute ,
} ;
# ifdef CONFIG_OF
static struct sky81452_bl_platform_data * sky81452_bl_parse_dt (
struct device * dev )
{
struct device_node * np = of_node_get ( dev - > of_node ) ;
struct sky81452_bl_platform_data * pdata ;
int num_entry ;
unsigned int sources [ 6 ] ;
int ret ;
if ( ! np ) {
dev_err ( dev , " backlight node not found. \n " ) ;
return ERR_PTR ( - ENODATA ) ;
}
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata ) {
of_node_put ( np ) ;
return ERR_PTR ( - ENOMEM ) ;
}
of_property_read_string ( np , " name " , & pdata - > name ) ;
pdata - > ignore_pwm = of_property_read_bool ( np , " skyworks,ignore-pwm " ) ;
pdata - > dpwm_mode = of_property_read_bool ( np , " skyworks,dpwm-mode " ) ;
pdata - > phase_shift = of_property_read_bool ( np , " skyworks,phase-shift " ) ;
2020-06-26 23:37:41 +03:00
pdata - > gpiod_enable = devm_gpiod_get_optional ( dev , NULL , GPIOD_OUT_HIGH ) ;
2015-02-27 09:42:22 +03:00
ret = of_property_count_u32_elems ( np , " led-sources " ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 ) {
2015-02-27 09:42:22 +03:00
pdata - > enable = SKY81452_EN > > CTZ ( SKY81452_EN ) ;
} else {
num_entry = ret ;
if ( num_entry > 6 )
num_entry = 6 ;
ret = of_property_read_u32_array ( np , " led-sources " , sources ,
num_entry ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 ) {
2015-02-27 09:42:22 +03:00
dev_err ( dev , " led-sources node is invalid. \n " ) ;
2020-08-20 09:38:17 +03:00
of_node_put ( np ) ;
2015-02-27 09:42:22 +03:00
return ERR_PTR ( - EINVAL ) ;
}
pdata - > enable = 0 ;
while ( - - num_entry )
pdata - > enable | = ( 1 < < sources [ num_entry ] ) ;
}
ret = of_property_read_u32 ( np ,
" skyworks,short-detection-threshold-volt " ,
& pdata - > short_detection_threshold ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
pdata - > short_detection_threshold = 7 ;
ret = of_property_read_u32 ( np , " skyworks,current-limit-mA " ,
& pdata - > boost_current_limit ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 )
2015-02-27 09:42:22 +03:00
pdata - > boost_current_limit = 2750 ;
of_node_put ( np ) ;
return pdata ;
}
# else
static struct sky81452_bl_platform_data * sky81452_bl_parse_dt (
struct device * dev )
{
return ERR_PTR ( - EINVAL ) ;
}
# endif
static int sky81452_bl_init_device ( struct regmap * regmap ,
struct sky81452_bl_platform_data * pdata )
{
unsigned int value ;
value = pdata - > ignore_pwm ? SKY81452_IGPW : 0 ;
value | = pdata - > dpwm_mode ? SKY81452_PWMMD : 0 ;
value | = pdata - > phase_shift ? 0 : SKY81452_PHASE ;
if ( pdata - > boost_current_limit = = 2300 )
value | = SKY81452_ILIM ;
else if ( pdata - > boost_current_limit ! = 2750 )
return - EINVAL ;
if ( pdata - > short_detection_threshold < 4 | |
pdata - > short_detection_threshold > 7 )
return - EINVAL ;
value | = ( 7 - pdata - > short_detection_threshold ) < < CTZ ( SKY81452_VSHRT ) ;
return regmap_write ( regmap , SKY81452_REG2 , value ) ;
}
static int sky81452_bl_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct regmap * regmap = dev_get_drvdata ( dev - > parent ) ;
2020-06-26 23:37:42 +03:00
struct sky81452_bl_platform_data * pdata ;
2015-02-27 09:42:22 +03:00
struct backlight_device * bd ;
struct backlight_properties props ;
const char * name ;
int ret ;
2020-06-26 23:37:42 +03:00
pdata = sky81452_bl_parse_dt ( dev ) ;
if ( IS_ERR ( pdata ) )
return PTR_ERR ( pdata ) ;
2015-02-27 09:42:22 +03:00
ret = sky81452_bl_init_device ( regmap , pdata ) ;
2015-08-17 10:59:17 +03:00
if ( ret < 0 ) {
2015-02-27 09:42:22 +03:00
dev_err ( dev , " failed to initialize. err=%d \n " , ret ) ;
return ret ;
}
memset ( & props , 0 , sizeof ( props ) ) ;
2020-12-14 16:34:58 +03:00
props . max_brightness = SKY81452_MAX_BRIGHTNESS ;
2015-02-27 09:42:22 +03:00
name = pdata - > name ? pdata - > name : SKY81452_DEFAULT_NAME ;
bd = devm_backlight_device_register ( dev , name , dev , regmap ,
& sky81452_bl_ops , & props ) ;
if ( IS_ERR ( bd ) ) {
dev_err ( dev , " failed to register. err=%ld \n " , PTR_ERR ( bd ) ) ;
return PTR_ERR ( bd ) ;
}
platform_set_drvdata ( pdev , bd ) ;
2015-08-17 10:59:17 +03:00
ret = sysfs_create_group ( & bd - > dev . kobj , & sky81452_bl_attr_group ) ;
if ( ret < 0 ) {
2015-02-27 09:42:22 +03:00
dev_err ( dev , " failed to create attribute. err=%d \n " , ret ) ;
return ret ;
}
return ret ;
}
static int sky81452_bl_remove ( struct platform_device * pdev )
{
const struct sky81452_bl_platform_data * pdata =
dev_get_platdata ( & pdev - > dev ) ;
struct backlight_device * bd = platform_get_drvdata ( pdev ) ;
sysfs_remove_group ( & bd - > dev . kobj , & sky81452_bl_attr_group ) ;
bd - > props . power = FB_BLANK_UNBLANK ;
bd - > props . brightness = 0 ;
backlight_update_status ( bd ) ;
2020-06-26 23:37:41 +03:00
if ( pdata - > gpiod_enable )
gpiod_set_value_cansleep ( pdata - > gpiod_enable , 0 ) ;
2015-02-27 09:42:22 +03:00
return 0 ;
}
# ifdef CONFIG_OF
static const struct of_device_id sky81452_bl_of_match [ ] = {
{ . compatible = " skyworks,sky81452-backlight " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sky81452_bl_of_match ) ;
# endif
static struct platform_driver sky81452_bl_driver = {
. driver = {
. name = " sky81452-backlight " ,
. of_match_table = of_match_ptr ( sky81452_bl_of_match ) ,
} ,
. probe = sky81452_bl_probe ,
. remove = sky81452_bl_remove ,
} ;
module_platform_driver ( sky81452_bl_driver ) ;
MODULE_DESCRIPTION ( " Skyworks SKY81452 backlight driver " ) ;
MODULE_AUTHOR ( " Gyungoh Yoo <jack.yoo@skyworksinc.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;