1*7a8685acSDan Murphy /* 2*7a8685acSDan Murphy * TI LP8860 4-Channel LED Driver 3*7a8685acSDan Murphy * 4*7a8685acSDan Murphy * Copyright (C) 2014 Texas Instruments 5*7a8685acSDan Murphy * 6*7a8685acSDan Murphy * Author: Dan Murphy <dmurphy@ti.com> 7*7a8685acSDan Murphy * 8*7a8685acSDan Murphy * This program is free software; you can redistribute it and/or 9*7a8685acSDan Murphy * modify it under the terms of the GNU General Public License 10*7a8685acSDan Murphy * version 2 as published by the Free Software Foundation. 11*7a8685acSDan Murphy * 12*7a8685acSDan Murphy */ 13*7a8685acSDan Murphy 14*7a8685acSDan Murphy #include <linux/i2c.h> 15*7a8685acSDan Murphy #include <linux/init.h> 16*7a8685acSDan Murphy #include <linux/leds.h> 17*7a8685acSDan Murphy #include <linux/regmap.h> 18*7a8685acSDan Murphy #include <linux/regulator/consumer.h> 19*7a8685acSDan Murphy #include <linux/module.h> 20*7a8685acSDan Murphy #include <linux/mutex.h> 21*7a8685acSDan Murphy #include <linux/of.h> 22*7a8685acSDan Murphy #include <linux/of_gpio.h> 23*7a8685acSDan Murphy #include <linux/gpio/consumer.h> 24*7a8685acSDan Murphy #include <linux/slab.h> 25*7a8685acSDan Murphy 26*7a8685acSDan Murphy #define LP8860_DISP_CL1_BRT_MSB 0x00 27*7a8685acSDan Murphy #define LP8860_DISP_CL1_BRT_LSB 0x01 28*7a8685acSDan Murphy #define LP8860_DISP_CL1_CURR_MSB 0x02 29*7a8685acSDan Murphy #define LP8860_DISP_CL1_CURR_LSB 0x03 30*7a8685acSDan Murphy #define LP8860_CL2_BRT_MSB 0x04 31*7a8685acSDan Murphy #define LP8860_CL2_BRT_LSB 0x05 32*7a8685acSDan Murphy #define LP8860_CL2_CURRENT 0x06 33*7a8685acSDan Murphy #define LP8860_CL3_BRT_MSB 0x07 34*7a8685acSDan Murphy #define LP8860_CL3_BRT_LSB 0x08 35*7a8685acSDan Murphy #define LP8860_CL3_CURRENT 0x09 36*7a8685acSDan Murphy #define LP8860_CL4_BRT_MSB 0x0a 37*7a8685acSDan Murphy #define LP8860_CL4_BRT_LSB 0x0b 38*7a8685acSDan Murphy #define LP8860_CL4_CURRENT 0x0c 39*7a8685acSDan Murphy #define LP8860_CONFIG 0x0d 40*7a8685acSDan Murphy #define LP8860_STATUS 0x0e 41*7a8685acSDan Murphy #define LP8860_FAULT 0x0f 42*7a8685acSDan Murphy #define LP8860_LED_FAULT 0x10 43*7a8685acSDan Murphy #define LP8860_FAULT_CLEAR 0x11 44*7a8685acSDan Murphy #define LP8860_ID 0x12 45*7a8685acSDan Murphy #define LP8860_TEMP_MSB 0x13 46*7a8685acSDan Murphy #define LP8860_TEMP_LSB 0x14 47*7a8685acSDan Murphy #define LP8860_DISP_LED_CURR_MSB 0x15 48*7a8685acSDan Murphy #define LP8860_DISP_LED_CURR_LSB 0x16 49*7a8685acSDan Murphy #define LP8860_DISP_LED_PWM_MSB 0x17 50*7a8685acSDan Murphy #define LP8860_DISP_LED_PWM_LSB 0x18 51*7a8685acSDan Murphy #define LP8860_EEPROM_CNTRL 0x19 52*7a8685acSDan Murphy #define LP8860_EEPROM_UNLOCK 0x1a 53*7a8685acSDan Murphy 54*7a8685acSDan Murphy #define LP8860_EEPROM_REG_0 0x60 55*7a8685acSDan Murphy #define LP8860_EEPROM_REG_1 0x61 56*7a8685acSDan Murphy #define LP8860_EEPROM_REG_2 0x62 57*7a8685acSDan Murphy #define LP8860_EEPROM_REG_3 0x63 58*7a8685acSDan Murphy #define LP8860_EEPROM_REG_4 0x64 59*7a8685acSDan Murphy #define LP8860_EEPROM_REG_5 0x65 60*7a8685acSDan Murphy #define LP8860_EEPROM_REG_6 0x66 61*7a8685acSDan Murphy #define LP8860_EEPROM_REG_7 0x67 62*7a8685acSDan Murphy #define LP8860_EEPROM_REG_8 0x68 63*7a8685acSDan Murphy #define LP8860_EEPROM_REG_9 0x69 64*7a8685acSDan Murphy #define LP8860_EEPROM_REG_10 0x6a 65*7a8685acSDan Murphy #define LP8860_EEPROM_REG_11 0x6b 66*7a8685acSDan Murphy #define LP8860_EEPROM_REG_12 0x6c 67*7a8685acSDan Murphy #define LP8860_EEPROM_REG_13 0x6d 68*7a8685acSDan Murphy #define LP8860_EEPROM_REG_14 0x6e 69*7a8685acSDan Murphy #define LP8860_EEPROM_REG_15 0x6f 70*7a8685acSDan Murphy #define LP8860_EEPROM_REG_16 0x70 71*7a8685acSDan Murphy #define LP8860_EEPROM_REG_17 0x71 72*7a8685acSDan Murphy #define LP8860_EEPROM_REG_18 0x72 73*7a8685acSDan Murphy #define LP8860_EEPROM_REG_19 0x73 74*7a8685acSDan Murphy #define LP8860_EEPROM_REG_20 0x74 75*7a8685acSDan Murphy #define LP8860_EEPROM_REG_21 0x75 76*7a8685acSDan Murphy #define LP8860_EEPROM_REG_22 0x76 77*7a8685acSDan Murphy #define LP8860_EEPROM_REG_23 0x77 78*7a8685acSDan Murphy #define LP8860_EEPROM_REG_24 0x78 79*7a8685acSDan Murphy 80*7a8685acSDan Murphy #define LP8860_LOCK_EEPROM 0x00 81*7a8685acSDan Murphy #define LP8860_UNLOCK_EEPROM 0x01 82*7a8685acSDan Murphy #define LP8860_PROGRAM_EEPROM 0x02 83*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_1 0x08 84*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_2 0xba 85*7a8685acSDan Murphy #define LP8860_EEPROM_CODE_3 0xef 86*7a8685acSDan Murphy 87*7a8685acSDan Murphy #define LP8860_CLEAR_FAULTS 0x01 88*7a8685acSDan Murphy 89*7a8685acSDan Murphy #define LP8860_DISP_LED_NAME "display_cluster" 90*7a8685acSDan Murphy 91*7a8685acSDan Murphy /** 92*7a8685acSDan Murphy * struct lp8860_led - 93*7a8685acSDan Murphy * @lock - Lock for reading/writing the device 94*7a8685acSDan Murphy * @work - Work item used to off load the brightness register writes 95*7a8685acSDan Murphy * @client - Pointer to the I2C client 96*7a8685acSDan Murphy * @led_dev - led class device pointer 97*7a8685acSDan Murphy * @regmap - Devices register map 98*7a8685acSDan Murphy * @eeprom_regmap - EEPROM register map 99*7a8685acSDan Murphy * @enable_gpio - VDDIO/EN gpio to enable communication interface 100*7a8685acSDan Murphy * @regulator - LED supply regulator pointer 101*7a8685acSDan Murphy * @brightness - Current brightness value requested 102*7a8685acSDan Murphy * @label - LED label 103*7a8685acSDan Murphy **/ 104*7a8685acSDan Murphy struct lp8860_led { 105*7a8685acSDan Murphy struct mutex lock; 106*7a8685acSDan Murphy struct work_struct work; 107*7a8685acSDan Murphy struct i2c_client *client; 108*7a8685acSDan Murphy struct led_classdev led_dev; 109*7a8685acSDan Murphy struct regmap *regmap; 110*7a8685acSDan Murphy struct regmap *eeprom_regmap; 111*7a8685acSDan Murphy struct gpio_desc *enable_gpio; 112*7a8685acSDan Murphy struct regulator *regulator; 113*7a8685acSDan Murphy enum led_brightness brightness; 114*7a8685acSDan Murphy const char *label; 115*7a8685acSDan Murphy }; 116*7a8685acSDan Murphy 117*7a8685acSDan Murphy struct lp8860_eeprom_reg { 118*7a8685acSDan Murphy uint8_t reg; 119*7a8685acSDan Murphy uint8_t value; 120*7a8685acSDan Murphy }; 121*7a8685acSDan Murphy 122*7a8685acSDan Murphy static struct lp8860_eeprom_reg lp8860_eeprom_disp_regs[] = { 123*7a8685acSDan Murphy { LP8860_EEPROM_REG_0, 0xed }, 124*7a8685acSDan Murphy { LP8860_EEPROM_REG_1, 0xdf }, 125*7a8685acSDan Murphy { LP8860_EEPROM_REG_2, 0xdc }, 126*7a8685acSDan Murphy { LP8860_EEPROM_REG_3, 0xf0 }, 127*7a8685acSDan Murphy { LP8860_EEPROM_REG_4, 0xdf }, 128*7a8685acSDan Murphy { LP8860_EEPROM_REG_5, 0xe5 }, 129*7a8685acSDan Murphy { LP8860_EEPROM_REG_6, 0xf2 }, 130*7a8685acSDan Murphy { LP8860_EEPROM_REG_7, 0x77 }, 131*7a8685acSDan Murphy { LP8860_EEPROM_REG_8, 0x77 }, 132*7a8685acSDan Murphy { LP8860_EEPROM_REG_9, 0x71 }, 133*7a8685acSDan Murphy { LP8860_EEPROM_REG_10, 0x3f }, 134*7a8685acSDan Murphy { LP8860_EEPROM_REG_11, 0xb7 }, 135*7a8685acSDan Murphy { LP8860_EEPROM_REG_12, 0x17 }, 136*7a8685acSDan Murphy { LP8860_EEPROM_REG_13, 0xef }, 137*7a8685acSDan Murphy { LP8860_EEPROM_REG_14, 0xb0 }, 138*7a8685acSDan Murphy { LP8860_EEPROM_REG_15, 0x87 }, 139*7a8685acSDan Murphy { LP8860_EEPROM_REG_16, 0xce }, 140*7a8685acSDan Murphy { LP8860_EEPROM_REG_17, 0x72 }, 141*7a8685acSDan Murphy { LP8860_EEPROM_REG_18, 0xe5 }, 142*7a8685acSDan Murphy { LP8860_EEPROM_REG_19, 0xdf }, 143*7a8685acSDan Murphy { LP8860_EEPROM_REG_20, 0x35 }, 144*7a8685acSDan Murphy { LP8860_EEPROM_REG_21, 0x06 }, 145*7a8685acSDan Murphy { LP8860_EEPROM_REG_22, 0xdc }, 146*7a8685acSDan Murphy { LP8860_EEPROM_REG_23, 0x88 }, 147*7a8685acSDan Murphy { LP8860_EEPROM_REG_24, 0x3E }, 148*7a8685acSDan Murphy }; 149*7a8685acSDan Murphy 150*7a8685acSDan Murphy static int lp8860_unlock_eeprom(struct lp8860_led *led, int lock) 151*7a8685acSDan Murphy { 152*7a8685acSDan Murphy int ret; 153*7a8685acSDan Murphy 154*7a8685acSDan Murphy mutex_lock(&led->lock); 155*7a8685acSDan Murphy 156*7a8685acSDan Murphy if (lock == LP8860_UNLOCK_EEPROM) { 157*7a8685acSDan Murphy ret = regmap_write(led->regmap, 158*7a8685acSDan Murphy LP8860_EEPROM_UNLOCK, 159*7a8685acSDan Murphy LP8860_EEPROM_CODE_1); 160*7a8685acSDan Murphy if (ret) { 161*7a8685acSDan Murphy dev_err(&led->client->dev, "EEPROM Unlock failed\n"); 162*7a8685acSDan Murphy goto out; 163*7a8685acSDan Murphy } 164*7a8685acSDan Murphy 165*7a8685acSDan Murphy ret = regmap_write(led->regmap, 166*7a8685acSDan Murphy LP8860_EEPROM_UNLOCK, 167*7a8685acSDan Murphy LP8860_EEPROM_CODE_2); 168*7a8685acSDan Murphy if (ret) { 169*7a8685acSDan Murphy dev_err(&led->client->dev, "EEPROM Unlock failed\n"); 170*7a8685acSDan Murphy goto out; 171*7a8685acSDan Murphy } 172*7a8685acSDan Murphy ret = regmap_write(led->regmap, 173*7a8685acSDan Murphy LP8860_EEPROM_UNLOCK, 174*7a8685acSDan Murphy LP8860_EEPROM_CODE_3); 175*7a8685acSDan Murphy if (ret) { 176*7a8685acSDan Murphy dev_err(&led->client->dev, "EEPROM Unlock failed\n"); 177*7a8685acSDan Murphy goto out; 178*7a8685acSDan Murphy } 179*7a8685acSDan Murphy } else { 180*7a8685acSDan Murphy ret = regmap_write(led->regmap, 181*7a8685acSDan Murphy LP8860_EEPROM_UNLOCK, 182*7a8685acSDan Murphy LP8860_LOCK_EEPROM); 183*7a8685acSDan Murphy } 184*7a8685acSDan Murphy 185*7a8685acSDan Murphy out: 186*7a8685acSDan Murphy mutex_unlock(&led->lock); 187*7a8685acSDan Murphy return ret; 188*7a8685acSDan Murphy } 189*7a8685acSDan Murphy 190*7a8685acSDan Murphy static int lp8860_fault_check(struct lp8860_led *led) 191*7a8685acSDan Murphy { 192*7a8685acSDan Murphy int ret, fault; 193*7a8685acSDan Murphy unsigned int read_buf; 194*7a8685acSDan Murphy 195*7a8685acSDan Murphy ret = regmap_read(led->regmap, LP8860_LED_FAULT, &read_buf); 196*7a8685acSDan Murphy if (ret) 197*7a8685acSDan Murphy goto out; 198*7a8685acSDan Murphy 199*7a8685acSDan Murphy fault = read_buf; 200*7a8685acSDan Murphy 201*7a8685acSDan Murphy ret = regmap_read(led->regmap, LP8860_FAULT, &read_buf); 202*7a8685acSDan Murphy if (ret) 203*7a8685acSDan Murphy goto out; 204*7a8685acSDan Murphy 205*7a8685acSDan Murphy fault |= read_buf; 206*7a8685acSDan Murphy 207*7a8685acSDan Murphy /* Attempt to clear any faults */ 208*7a8685acSDan Murphy if (fault) 209*7a8685acSDan Murphy ret = regmap_write(led->regmap, LP8860_FAULT_CLEAR, 210*7a8685acSDan Murphy LP8860_CLEAR_FAULTS); 211*7a8685acSDan Murphy out: 212*7a8685acSDan Murphy return ret; 213*7a8685acSDan Murphy } 214*7a8685acSDan Murphy 215*7a8685acSDan Murphy static void lp8860_led_brightness_work(struct work_struct *work) 216*7a8685acSDan Murphy { 217*7a8685acSDan Murphy struct lp8860_led *led = container_of(work, struct lp8860_led, work); 218*7a8685acSDan Murphy int ret; 219*7a8685acSDan Murphy int disp_brightness = led->brightness * 255; 220*7a8685acSDan Murphy 221*7a8685acSDan Murphy mutex_lock(&led->lock); 222*7a8685acSDan Murphy 223*7a8685acSDan Murphy ret = lp8860_fault_check(led); 224*7a8685acSDan Murphy if (ret) { 225*7a8685acSDan Murphy dev_err(&led->client->dev, "Cannot read/clear faults\n"); 226*7a8685acSDan Murphy goto out; 227*7a8685acSDan Murphy } 228*7a8685acSDan Murphy 229*7a8685acSDan Murphy ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_MSB, 230*7a8685acSDan Murphy (disp_brightness & 0xff00) >> 8); 231*7a8685acSDan Murphy if (ret) { 232*7a8685acSDan Murphy dev_err(&led->client->dev, "Cannot write CL1 MSB\n"); 233*7a8685acSDan Murphy goto out; 234*7a8685acSDan Murphy } 235*7a8685acSDan Murphy 236*7a8685acSDan Murphy ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_LSB, 237*7a8685acSDan Murphy disp_brightness & 0xff); 238*7a8685acSDan Murphy if (ret) { 239*7a8685acSDan Murphy dev_err(&led->client->dev, "Cannot write CL1 LSB\n"); 240*7a8685acSDan Murphy goto out; 241*7a8685acSDan Murphy } 242*7a8685acSDan Murphy out: 243*7a8685acSDan Murphy mutex_unlock(&led->lock); 244*7a8685acSDan Murphy } 245*7a8685acSDan Murphy 246*7a8685acSDan Murphy static void lp8860_brightness_set(struct led_classdev *led_cdev, 247*7a8685acSDan Murphy enum led_brightness brt_val) 248*7a8685acSDan Murphy { 249*7a8685acSDan Murphy struct lp8860_led *led = 250*7a8685acSDan Murphy container_of(led_cdev, struct lp8860_led, led_dev); 251*7a8685acSDan Murphy 252*7a8685acSDan Murphy led->brightness = brt_val; 253*7a8685acSDan Murphy schedule_work(&led->work); 254*7a8685acSDan Murphy } 255*7a8685acSDan Murphy 256*7a8685acSDan Murphy static int lp8860_init(struct lp8860_led *led) 257*7a8685acSDan Murphy { 258*7a8685acSDan Murphy unsigned int read_buf; 259*7a8685acSDan Murphy int ret, i, reg_count; 260*7a8685acSDan Murphy 261*7a8685acSDan Murphy if (led->enable_gpio) 262*7a8685acSDan Murphy gpiod_direction_output(led->enable_gpio, 1); 263*7a8685acSDan Murphy 264*7a8685acSDan Murphy ret = lp8860_fault_check(led); 265*7a8685acSDan Murphy if (ret) 266*7a8685acSDan Murphy goto out; 267*7a8685acSDan Murphy 268*7a8685acSDan Murphy ret = regmap_read(led->regmap, LP8860_STATUS, &read_buf); 269*7a8685acSDan Murphy if (ret) 270*7a8685acSDan Murphy goto out; 271*7a8685acSDan Murphy 272*7a8685acSDan Murphy ret = lp8860_unlock_eeprom(led, LP8860_UNLOCK_EEPROM); 273*7a8685acSDan Murphy if (ret) { 274*7a8685acSDan Murphy dev_err(&led->client->dev, "Failed unlocking EEPROM\n"); 275*7a8685acSDan Murphy goto out; 276*7a8685acSDan Murphy } 277*7a8685acSDan Murphy 278*7a8685acSDan Murphy reg_count = ARRAY_SIZE(lp8860_eeprom_disp_regs) / sizeof(lp8860_eeprom_disp_regs[0]); 279*7a8685acSDan Murphy for (i = 0; i < reg_count; i++) { 280*7a8685acSDan Murphy ret = regmap_write(led->eeprom_regmap, 281*7a8685acSDan Murphy lp8860_eeprom_disp_regs[i].reg, 282*7a8685acSDan Murphy lp8860_eeprom_disp_regs[i].value); 283*7a8685acSDan Murphy if (ret) { 284*7a8685acSDan Murphy dev_err(&led->client->dev, "Failed writing EEPROM\n"); 285*7a8685acSDan Murphy goto out; 286*7a8685acSDan Murphy } 287*7a8685acSDan Murphy } 288*7a8685acSDan Murphy 289*7a8685acSDan Murphy ret = lp8860_unlock_eeprom(led, LP8860_LOCK_EEPROM); 290*7a8685acSDan Murphy if (ret) 291*7a8685acSDan Murphy goto out; 292*7a8685acSDan Murphy 293*7a8685acSDan Murphy ret = regmap_write(led->regmap, 294*7a8685acSDan Murphy LP8860_EEPROM_CNTRL, 295*7a8685acSDan Murphy LP8860_PROGRAM_EEPROM); 296*7a8685acSDan Murphy if (ret) 297*7a8685acSDan Murphy dev_err(&led->client->dev, "Failed programming EEPROM\n"); 298*7a8685acSDan Murphy out: 299*7a8685acSDan Murphy if (ret) 300*7a8685acSDan Murphy if (led->enable_gpio) 301*7a8685acSDan Murphy gpiod_direction_output(led->enable_gpio, 0); 302*7a8685acSDan Murphy return ret; 303*7a8685acSDan Murphy } 304*7a8685acSDan Murphy 305*7a8685acSDan Murphy static struct reg_default lp8860_reg_defs[] = { 306*7a8685acSDan Murphy { LP8860_DISP_CL1_BRT_MSB, 0x00}, 307*7a8685acSDan Murphy { LP8860_DISP_CL1_BRT_LSB, 0x00}, 308*7a8685acSDan Murphy { LP8860_DISP_CL1_CURR_MSB, 0x00}, 309*7a8685acSDan Murphy { LP8860_DISP_CL1_CURR_LSB, 0x00}, 310*7a8685acSDan Murphy { LP8860_CL2_BRT_MSB, 0x00}, 311*7a8685acSDan Murphy { LP8860_CL2_BRT_LSB, 0x00}, 312*7a8685acSDan Murphy { LP8860_CL2_CURRENT, 0x00}, 313*7a8685acSDan Murphy { LP8860_CL3_BRT_MSB, 0x00}, 314*7a8685acSDan Murphy { LP8860_CL3_BRT_LSB, 0x00}, 315*7a8685acSDan Murphy { LP8860_CL3_CURRENT, 0x00}, 316*7a8685acSDan Murphy { LP8860_CL4_BRT_MSB, 0x00}, 317*7a8685acSDan Murphy { LP8860_CL4_BRT_LSB, 0x00}, 318*7a8685acSDan Murphy { LP8860_CL4_CURRENT, 0x00}, 319*7a8685acSDan Murphy { LP8860_CONFIG, 0x00}, 320*7a8685acSDan Murphy { LP8860_FAULT_CLEAR, 0x00}, 321*7a8685acSDan Murphy { LP8860_EEPROM_CNTRL, 0x80}, 322*7a8685acSDan Murphy { LP8860_EEPROM_UNLOCK, 0x00}, 323*7a8685acSDan Murphy }; 324*7a8685acSDan Murphy 325*7a8685acSDan Murphy static const struct regmap_config lp8860_regmap_config = { 326*7a8685acSDan Murphy .reg_bits = 8, 327*7a8685acSDan Murphy .val_bits = 8, 328*7a8685acSDan Murphy 329*7a8685acSDan Murphy .max_register = LP8860_EEPROM_UNLOCK, 330*7a8685acSDan Murphy .reg_defaults = lp8860_reg_defs, 331*7a8685acSDan Murphy .num_reg_defaults = ARRAY_SIZE(lp8860_reg_defs), 332*7a8685acSDan Murphy .cache_type = REGCACHE_NONE, 333*7a8685acSDan Murphy }; 334*7a8685acSDan Murphy 335*7a8685acSDan Murphy static struct reg_default lp8860_eeprom_defs[] = { 336*7a8685acSDan Murphy { LP8860_EEPROM_REG_0, 0x00 }, 337*7a8685acSDan Murphy { LP8860_EEPROM_REG_1, 0x00 }, 338*7a8685acSDan Murphy { LP8860_EEPROM_REG_2, 0x00 }, 339*7a8685acSDan Murphy { LP8860_EEPROM_REG_3, 0x00 }, 340*7a8685acSDan Murphy { LP8860_EEPROM_REG_4, 0x00 }, 341*7a8685acSDan Murphy { LP8860_EEPROM_REG_5, 0x00 }, 342*7a8685acSDan Murphy { LP8860_EEPROM_REG_6, 0x00 }, 343*7a8685acSDan Murphy { LP8860_EEPROM_REG_7, 0x00 }, 344*7a8685acSDan Murphy { LP8860_EEPROM_REG_8, 0x00 }, 345*7a8685acSDan Murphy { LP8860_EEPROM_REG_9, 0x00 }, 346*7a8685acSDan Murphy { LP8860_EEPROM_REG_10, 0x00 }, 347*7a8685acSDan Murphy { LP8860_EEPROM_REG_11, 0x00 }, 348*7a8685acSDan Murphy { LP8860_EEPROM_REG_12, 0x00 }, 349*7a8685acSDan Murphy { LP8860_EEPROM_REG_13, 0x00 }, 350*7a8685acSDan Murphy { LP8860_EEPROM_REG_14, 0x00 }, 351*7a8685acSDan Murphy { LP8860_EEPROM_REG_15, 0x00 }, 352*7a8685acSDan Murphy { LP8860_EEPROM_REG_16, 0x00 }, 353*7a8685acSDan Murphy { LP8860_EEPROM_REG_17, 0x00 }, 354*7a8685acSDan Murphy { LP8860_EEPROM_REG_18, 0x00 }, 355*7a8685acSDan Murphy { LP8860_EEPROM_REG_19, 0x00 }, 356*7a8685acSDan Murphy { LP8860_EEPROM_REG_20, 0x00 }, 357*7a8685acSDan Murphy { LP8860_EEPROM_REG_21, 0x00 }, 358*7a8685acSDan Murphy { LP8860_EEPROM_REG_22, 0x00 }, 359*7a8685acSDan Murphy { LP8860_EEPROM_REG_23, 0x00 }, 360*7a8685acSDan Murphy { LP8860_EEPROM_REG_24, 0x00 }, 361*7a8685acSDan Murphy }; 362*7a8685acSDan Murphy 363*7a8685acSDan Murphy static const struct regmap_config lp8860_eeprom_regmap_config = { 364*7a8685acSDan Murphy .reg_bits = 8, 365*7a8685acSDan Murphy .val_bits = 8, 366*7a8685acSDan Murphy 367*7a8685acSDan Murphy .max_register = LP8860_EEPROM_REG_24, 368*7a8685acSDan Murphy .reg_defaults = lp8860_eeprom_defs, 369*7a8685acSDan Murphy .num_reg_defaults = ARRAY_SIZE(lp8860_eeprom_defs), 370*7a8685acSDan Murphy .cache_type = REGCACHE_NONE, 371*7a8685acSDan Murphy }; 372*7a8685acSDan Murphy 373*7a8685acSDan Murphy static int lp8860_probe(struct i2c_client *client, 374*7a8685acSDan Murphy const struct i2c_device_id *id) 375*7a8685acSDan Murphy { 376*7a8685acSDan Murphy int ret; 377*7a8685acSDan Murphy struct lp8860_led *led; 378*7a8685acSDan Murphy struct device_node *np = client->dev.of_node; 379*7a8685acSDan Murphy 380*7a8685acSDan Murphy led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL); 381*7a8685acSDan Murphy if (!led) 382*7a8685acSDan Murphy return -ENOMEM; 383*7a8685acSDan Murphy 384*7a8685acSDan Murphy led->label = LP8860_DISP_LED_NAME; 385*7a8685acSDan Murphy 386*7a8685acSDan Murphy if (client->dev.of_node) { 387*7a8685acSDan Murphy ret = of_property_read_string(np, "label", &led->label); 388*7a8685acSDan Murphy if (ret) { 389*7a8685acSDan Murphy dev_err(&client->dev, "Missing label in dt\n"); 390*7a8685acSDan Murphy return -EINVAL; 391*7a8685acSDan Murphy } 392*7a8685acSDan Murphy } 393*7a8685acSDan Murphy 394*7a8685acSDan Murphy led->enable_gpio = devm_gpiod_get(&client->dev, "enable"); 395*7a8685acSDan Murphy if (IS_ERR(led->enable_gpio)) 396*7a8685acSDan Murphy led->enable_gpio = NULL; 397*7a8685acSDan Murphy else 398*7a8685acSDan Murphy gpiod_direction_output(led->enable_gpio, 0); 399*7a8685acSDan Murphy 400*7a8685acSDan Murphy led->regulator = devm_regulator_get(&client->dev, "vled"); 401*7a8685acSDan Murphy if (IS_ERR(led->regulator)) 402*7a8685acSDan Murphy led->regulator = NULL; 403*7a8685acSDan Murphy 404*7a8685acSDan Murphy led->client = client; 405*7a8685acSDan Murphy led->led_dev.name = led->label; 406*7a8685acSDan Murphy led->led_dev.max_brightness = LED_FULL; 407*7a8685acSDan Murphy led->led_dev.brightness_set = lp8860_brightness_set; 408*7a8685acSDan Murphy 409*7a8685acSDan Murphy mutex_init(&led->lock); 410*7a8685acSDan Murphy INIT_WORK(&led->work, lp8860_led_brightness_work); 411*7a8685acSDan Murphy 412*7a8685acSDan Murphy i2c_set_clientdata(client, led); 413*7a8685acSDan Murphy 414*7a8685acSDan Murphy led->regmap = devm_regmap_init_i2c(client, &lp8860_regmap_config); 415*7a8685acSDan Murphy if (IS_ERR(led->regmap)) { 416*7a8685acSDan Murphy ret = PTR_ERR(led->regmap); 417*7a8685acSDan Murphy dev_err(&client->dev, "Failed to allocate register map: %d\n", 418*7a8685acSDan Murphy ret); 419*7a8685acSDan Murphy return ret; 420*7a8685acSDan Murphy } 421*7a8685acSDan Murphy 422*7a8685acSDan Murphy led->eeprom_regmap = devm_regmap_init_i2c(client, &lp8860_eeprom_regmap_config); 423*7a8685acSDan Murphy if (IS_ERR(led->eeprom_regmap)) { 424*7a8685acSDan Murphy ret = PTR_ERR(led->eeprom_regmap); 425*7a8685acSDan Murphy dev_err(&client->dev, "Failed to allocate register map: %d\n", 426*7a8685acSDan Murphy ret); 427*7a8685acSDan Murphy return ret; 428*7a8685acSDan Murphy } 429*7a8685acSDan Murphy 430*7a8685acSDan Murphy ret = lp8860_init(led); 431*7a8685acSDan Murphy if (ret) 432*7a8685acSDan Murphy return ret; 433*7a8685acSDan Murphy 434*7a8685acSDan Murphy ret = led_classdev_register(&client->dev, &led->led_dev); 435*7a8685acSDan Murphy if (ret) { 436*7a8685acSDan Murphy dev_err(&client->dev, "led register err: %d\n", ret); 437*7a8685acSDan Murphy return ret; 438*7a8685acSDan Murphy } 439*7a8685acSDan Murphy 440*7a8685acSDan Murphy return 0; 441*7a8685acSDan Murphy } 442*7a8685acSDan Murphy 443*7a8685acSDan Murphy static int lp8860_remove(struct i2c_client *client) 444*7a8685acSDan Murphy { 445*7a8685acSDan Murphy struct lp8860_led *led = i2c_get_clientdata(client); 446*7a8685acSDan Murphy int ret; 447*7a8685acSDan Murphy 448*7a8685acSDan Murphy led_classdev_unregister(&led->led_dev); 449*7a8685acSDan Murphy cancel_work_sync(&led->work); 450*7a8685acSDan Murphy 451*7a8685acSDan Murphy if (led->enable_gpio) 452*7a8685acSDan Murphy gpiod_direction_output(led->enable_gpio, 0); 453*7a8685acSDan Murphy 454*7a8685acSDan Murphy if (led->regulator) { 455*7a8685acSDan Murphy ret = regulator_disable(led->regulator); 456*7a8685acSDan Murphy if (ret) 457*7a8685acSDan Murphy dev_err(&led->client->dev, 458*7a8685acSDan Murphy "Failed to disable regulator\n"); 459*7a8685acSDan Murphy } 460*7a8685acSDan Murphy 461*7a8685acSDan Murphy return 0; 462*7a8685acSDan Murphy } 463*7a8685acSDan Murphy 464*7a8685acSDan Murphy static const struct i2c_device_id lp8860_id[] = { 465*7a8685acSDan Murphy { "lp8860", 0 }, 466*7a8685acSDan Murphy { } 467*7a8685acSDan Murphy }; 468*7a8685acSDan Murphy MODULE_DEVICE_TABLE(i2c, lp8860_id); 469*7a8685acSDan Murphy 470*7a8685acSDan Murphy #ifdef CONFIG_OF 471*7a8685acSDan Murphy static const struct of_device_id of_lp8860_leds_match[] = { 472*7a8685acSDan Murphy { .compatible = "ti,lp8860", }, 473*7a8685acSDan Murphy {}, 474*7a8685acSDan Murphy }; 475*7a8685acSDan Murphy MODULE_DEVICE_TABLE(of, of_lp8860_leds_match); 476*7a8685acSDan Murphy #endif 477*7a8685acSDan Murphy 478*7a8685acSDan Murphy static struct i2c_driver lp8860_driver = { 479*7a8685acSDan Murphy .driver = { 480*7a8685acSDan Murphy .name = "lp8860", 481*7a8685acSDan Murphy .of_match_table = of_match_ptr(of_lp8860_leds_match), 482*7a8685acSDan Murphy }, 483*7a8685acSDan Murphy .probe = lp8860_probe, 484*7a8685acSDan Murphy .remove = lp8860_remove, 485*7a8685acSDan Murphy .id_table = lp8860_id, 486*7a8685acSDan Murphy }; 487*7a8685acSDan Murphy module_i2c_driver(lp8860_driver); 488*7a8685acSDan Murphy 489*7a8685acSDan Murphy MODULE_DESCRIPTION("Texas Instruments LP8860 LED drvier"); 490*7a8685acSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); 491*7a8685acSDan Murphy MODULE_LICENSE("GPL"); 492