1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 228e64a68SDaniel Jeong /* 328e64a68SDaniel Jeong * Simple driver for Texas Instruments LM3630A Backlight driver chip 428e64a68SDaniel Jeong * Copyright (C) 2012 Texas Instruments 528e64a68SDaniel Jeong */ 628e64a68SDaniel Jeong #include <linux/module.h> 728e64a68SDaniel Jeong #include <linux/slab.h> 828e64a68SDaniel Jeong #include <linux/i2c.h> 928e64a68SDaniel Jeong #include <linux/backlight.h> 1028e64a68SDaniel Jeong #include <linux/err.h> 1128e64a68SDaniel Jeong #include <linux/delay.h> 1228e64a68SDaniel Jeong #include <linux/uaccess.h> 1328e64a68SDaniel Jeong #include <linux/interrupt.h> 1428e64a68SDaniel Jeong #include <linux/regmap.h> 150e0e78e3SAndreas Kemnade #include <linux/gpio/consumer.h> 1628e64a68SDaniel Jeong #include <linux/pwm.h> 1728e64a68SDaniel Jeong #include <linux/platform_data/lm3630a_bl.h> 1828e64a68SDaniel Jeong 1928e64a68SDaniel Jeong #define REG_CTRL 0x00 2028e64a68SDaniel Jeong #define REG_BOOST 0x02 2128e64a68SDaniel Jeong #define REG_CONFIG 0x01 2228e64a68SDaniel Jeong #define REG_BRT_A 0x03 2328e64a68SDaniel Jeong #define REG_BRT_B 0x04 2428e64a68SDaniel Jeong #define REG_I_A 0x05 2528e64a68SDaniel Jeong #define REG_I_B 0x06 2628e64a68SDaniel Jeong #define REG_INT_STATUS 0x09 2728e64a68SDaniel Jeong #define REG_INT_EN 0x0A 2828e64a68SDaniel Jeong #define REG_FAULT 0x0B 2928e64a68SDaniel Jeong #define REG_PWM_OUTLOW 0x12 3028e64a68SDaniel Jeong #define REG_PWM_OUTHIGH 0x13 3106168a64SBhushan Shah #define REG_FILTER_STRENGTH 0x50 3206168a64SBhushan Shah #define REG_MAX 0x50 3328e64a68SDaniel Jeong 3428e64a68SDaniel Jeong #define INT_DEBOUNCE_MSEC 10 358fbce8efSBrian Masney 368fbce8efSBrian Masney #define LM3630A_BANK_0 0 378fbce8efSBrian Masney #define LM3630A_BANK_1 1 388fbce8efSBrian Masney 398fbce8efSBrian Masney #define LM3630A_NUM_SINKS 2 408fbce8efSBrian Masney #define LM3630A_SINK_0 0 418fbce8efSBrian Masney #define LM3630A_SINK_1 1 428fbce8efSBrian Masney 4328e64a68SDaniel Jeong struct lm3630a_chip { 4428e64a68SDaniel Jeong struct device *dev; 4528e64a68SDaniel Jeong struct delayed_work work; 4628e64a68SDaniel Jeong 4728e64a68SDaniel Jeong int irq; 4828e64a68SDaniel Jeong struct workqueue_struct *irqthread; 4928e64a68SDaniel Jeong struct lm3630a_platform_data *pdata; 5028e64a68SDaniel Jeong struct backlight_device *bleda; 5128e64a68SDaniel Jeong struct backlight_device *bledb; 520e0e78e3SAndreas Kemnade struct gpio_desc *enable_gpio; 5328e64a68SDaniel Jeong struct regmap *regmap; 5428e64a68SDaniel Jeong struct pwm_device *pwmd; 5528e64a68SDaniel Jeong }; 5628e64a68SDaniel Jeong 5728e64a68SDaniel Jeong /* i2c access */ 5828e64a68SDaniel Jeong static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg) 5928e64a68SDaniel Jeong { 6028e64a68SDaniel Jeong int rval; 6128e64a68SDaniel Jeong unsigned int reg_val; 6228e64a68SDaniel Jeong 6328e64a68SDaniel Jeong rval = regmap_read(pchip->regmap, reg, ®_val); 6428e64a68SDaniel Jeong if (rval < 0) 6528e64a68SDaniel Jeong return rval; 6628e64a68SDaniel Jeong return reg_val & 0xFF; 6728e64a68SDaniel Jeong } 6828e64a68SDaniel Jeong 6928e64a68SDaniel Jeong static int lm3630a_write(struct lm3630a_chip *pchip, 7028e64a68SDaniel Jeong unsigned int reg, unsigned int data) 7128e64a68SDaniel Jeong { 7228e64a68SDaniel Jeong return regmap_write(pchip->regmap, reg, data); 7328e64a68SDaniel Jeong } 7428e64a68SDaniel Jeong 7528e64a68SDaniel Jeong static int lm3630a_update(struct lm3630a_chip *pchip, 7628e64a68SDaniel Jeong unsigned int reg, unsigned int mask, 7728e64a68SDaniel Jeong unsigned int data) 7828e64a68SDaniel Jeong { 7928e64a68SDaniel Jeong return regmap_update_bits(pchip->regmap, reg, mask, data); 8028e64a68SDaniel Jeong } 8128e64a68SDaniel Jeong 8228e64a68SDaniel Jeong /* initialize chip */ 8328e64a68SDaniel Jeong static int lm3630a_chip_init(struct lm3630a_chip *pchip) 8428e64a68SDaniel Jeong { 8528e64a68SDaniel Jeong int rval; 8628e64a68SDaniel Jeong struct lm3630a_platform_data *pdata = pchip->pdata; 8728e64a68SDaniel Jeong 8828e64a68SDaniel Jeong usleep_range(1000, 2000); 8928e64a68SDaniel Jeong /* set Filter Strength Register */ 9006168a64SBhushan Shah rval = lm3630a_write(pchip, REG_FILTER_STRENGTH, 0x03); 9128e64a68SDaniel Jeong /* set Cofig. register */ 9228e64a68SDaniel Jeong rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl); 9328e64a68SDaniel Jeong /* set boost control */ 9428e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BOOST, 0x38); 9528e64a68SDaniel Jeong /* set current A */ 9628e64a68SDaniel Jeong rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F); 9728e64a68SDaniel Jeong /* set current B */ 9828e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_I_B, 0x1F); 9928e64a68SDaniel Jeong /* set control */ 1004c4d2a3aSDaniel Jeong rval |= lm3630a_update(pchip, REG_CTRL, 0x14, pdata->leda_ctrl); 1014c4d2a3aSDaniel Jeong rval |= lm3630a_update(pchip, REG_CTRL, 0x0B, pdata->ledb_ctrl); 10228e64a68SDaniel Jeong usleep_range(1000, 2000); 10328e64a68SDaniel Jeong /* set brightness A and B */ 10428e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt); 10528e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt); 10628e64a68SDaniel Jeong 10728e64a68SDaniel Jeong if (rval < 0) 10828e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 10928e64a68SDaniel Jeong return rval; 11028e64a68SDaniel Jeong } 11128e64a68SDaniel Jeong 11228e64a68SDaniel Jeong /* interrupt handling */ 11328e64a68SDaniel Jeong static void lm3630a_delayed_func(struct work_struct *work) 11428e64a68SDaniel Jeong { 1152a0c316bSDan Carpenter int rval; 11628e64a68SDaniel Jeong struct lm3630a_chip *pchip; 11728e64a68SDaniel Jeong 11828e64a68SDaniel Jeong pchip = container_of(work, struct lm3630a_chip, work.work); 11928e64a68SDaniel Jeong 12028e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_INT_STATUS); 12128e64a68SDaniel Jeong if (rval < 0) { 12228e64a68SDaniel Jeong dev_err(pchip->dev, 12328e64a68SDaniel Jeong "i2c failed to access REG_INT_STATUS Register\n"); 12428e64a68SDaniel Jeong return; 12528e64a68SDaniel Jeong } 12628e64a68SDaniel Jeong 12728e64a68SDaniel Jeong dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval); 12828e64a68SDaniel Jeong } 12928e64a68SDaniel Jeong 13028e64a68SDaniel Jeong static irqreturn_t lm3630a_isr_func(int irq, void *chip) 13128e64a68SDaniel Jeong { 13228e64a68SDaniel Jeong int rval; 13328e64a68SDaniel Jeong struct lm3630a_chip *pchip = chip; 13428e64a68SDaniel Jeong unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); 13528e64a68SDaniel Jeong 13628e64a68SDaniel Jeong queue_delayed_work(pchip->irqthread, &pchip->work, delay); 13728e64a68SDaniel Jeong 13828e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 13928e64a68SDaniel Jeong if (rval < 0) { 14028e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 14128e64a68SDaniel Jeong return IRQ_NONE; 14228e64a68SDaniel Jeong } 14328e64a68SDaniel Jeong return IRQ_HANDLED; 14428e64a68SDaniel Jeong } 14528e64a68SDaniel Jeong 14628e64a68SDaniel Jeong static int lm3630a_intr_config(struct lm3630a_chip *pchip) 14728e64a68SDaniel Jeong { 14828e64a68SDaniel Jeong int rval; 14928e64a68SDaniel Jeong 15028e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_INT_EN, 0x87); 15128e64a68SDaniel Jeong if (rval < 0) 15228e64a68SDaniel Jeong return rval; 15328e64a68SDaniel Jeong 15428e64a68SDaniel Jeong INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func); 15528e64a68SDaniel Jeong pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd"); 15628e64a68SDaniel Jeong if (!pchip->irqthread) { 15728e64a68SDaniel Jeong dev_err(pchip->dev, "create irq thread fail\n"); 15828e64a68SDaniel Jeong return -ENOMEM; 15928e64a68SDaniel Jeong } 16028e64a68SDaniel Jeong if (request_threaded_irq 16128e64a68SDaniel Jeong (pchip->irq, NULL, lm3630a_isr_func, 16228e64a68SDaniel Jeong IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) { 16328e64a68SDaniel Jeong dev_err(pchip->dev, "request threaded irq fail\n"); 164bcd5b416SWei Yongjun destroy_workqueue(pchip->irqthread); 16528e64a68SDaniel Jeong return -ENOMEM; 16628e64a68SDaniel Jeong } 16728e64a68SDaniel Jeong return rval; 16828e64a68SDaniel Jeong } 16928e64a68SDaniel Jeong 17028e64a68SDaniel Jeong static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) 17128e64a68SDaniel Jeong { 1724ff66efdSBoris BREZILLON unsigned int period = pchip->pdata->pwm_period; 17328e64a68SDaniel Jeong unsigned int duty = br * period / br_max; 17428e64a68SDaniel Jeong 17528e64a68SDaniel Jeong pwm_config(pchip->pwmd, duty, period); 17628e64a68SDaniel Jeong if (duty) 17728e64a68SDaniel Jeong pwm_enable(pchip->pwmd); 17828e64a68SDaniel Jeong else 17928e64a68SDaniel Jeong pwm_disable(pchip->pwmd); 18028e64a68SDaniel Jeong } 18128e64a68SDaniel Jeong 18228e64a68SDaniel Jeong /* update and get brightness */ 18328e64a68SDaniel Jeong static int lm3630a_bank_a_update_status(struct backlight_device *bl) 18428e64a68SDaniel Jeong { 18528e64a68SDaniel Jeong int ret; 18628e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl); 18728e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 18828e64a68SDaniel Jeong 18928e64a68SDaniel Jeong /* pwm control */ 19028e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { 19128e64a68SDaniel Jeong lm3630a_pwm_ctrl(pchip, bl->props.brightness, 19228e64a68SDaniel Jeong bl->props.max_brightness); 19328e64a68SDaniel Jeong return bl->props.brightness; 19428e64a68SDaniel Jeong } 19528e64a68SDaniel Jeong 19628e64a68SDaniel Jeong /* disable sleep */ 19728e64a68SDaniel Jeong ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 19828e64a68SDaniel Jeong if (ret < 0) 19928e64a68SDaniel Jeong goto out_i2c_err; 20028e64a68SDaniel Jeong usleep_range(1000, 2000); 20128e64a68SDaniel Jeong /* minimum brightness is 0x04 */ 20228e64a68SDaniel Jeong ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness); 20328e64a68SDaniel Jeong if (bl->props.brightness < 0x4) 20428e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0); 20528e64a68SDaniel Jeong else 20628e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, 20728e64a68SDaniel Jeong LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE); 20828e64a68SDaniel Jeong if (ret < 0) 20928e64a68SDaniel Jeong goto out_i2c_err; 210d3f48ec0SBrian Masney return 0; 21128e64a68SDaniel Jeong 21228e64a68SDaniel Jeong out_i2c_err: 21328e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access\n"); 21428e64a68SDaniel Jeong return bl->props.brightness; 21528e64a68SDaniel Jeong } 21628e64a68SDaniel Jeong 21728e64a68SDaniel Jeong static int lm3630a_bank_a_get_brightness(struct backlight_device *bl) 21828e64a68SDaniel Jeong { 21928e64a68SDaniel Jeong int brightness, rval; 22028e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl); 22128e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 22228e64a68SDaniel Jeong 22328e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { 22428e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); 22528e64a68SDaniel Jeong if (rval < 0) 22628e64a68SDaniel Jeong goto out_i2c_err; 22728e64a68SDaniel Jeong brightness = (rval & 0x01) << 8; 22828e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTLOW); 22928e64a68SDaniel Jeong if (rval < 0) 23028e64a68SDaniel Jeong goto out_i2c_err; 23128e64a68SDaniel Jeong brightness |= rval; 23228e64a68SDaniel Jeong goto out; 23328e64a68SDaniel Jeong } 23428e64a68SDaniel Jeong 23528e64a68SDaniel Jeong /* disable sleep */ 23628e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 23728e64a68SDaniel Jeong if (rval < 0) 23828e64a68SDaniel Jeong goto out_i2c_err; 23928e64a68SDaniel Jeong usleep_range(1000, 2000); 24028e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_BRT_A); 24128e64a68SDaniel Jeong if (rval < 0) 24228e64a68SDaniel Jeong goto out_i2c_err; 24328e64a68SDaniel Jeong brightness = rval; 24428e64a68SDaniel Jeong 24528e64a68SDaniel Jeong out: 24628e64a68SDaniel Jeong bl->props.brightness = brightness; 24728e64a68SDaniel Jeong return bl->props.brightness; 24828e64a68SDaniel Jeong out_i2c_err: 24928e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 25028e64a68SDaniel Jeong return 0; 25128e64a68SDaniel Jeong } 25228e64a68SDaniel Jeong 25328e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_a_ops = { 25428e64a68SDaniel Jeong .options = BL_CORE_SUSPENDRESUME, 25528e64a68SDaniel Jeong .update_status = lm3630a_bank_a_update_status, 25628e64a68SDaniel Jeong .get_brightness = lm3630a_bank_a_get_brightness, 25728e64a68SDaniel Jeong }; 25828e64a68SDaniel Jeong 25928e64a68SDaniel Jeong /* update and get brightness */ 26028e64a68SDaniel Jeong static int lm3630a_bank_b_update_status(struct backlight_device *bl) 26128e64a68SDaniel Jeong { 26228e64a68SDaniel Jeong int ret; 26328e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl); 26428e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 26528e64a68SDaniel Jeong 26628e64a68SDaniel Jeong /* pwm control */ 26728e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { 26828e64a68SDaniel Jeong lm3630a_pwm_ctrl(pchip, bl->props.brightness, 26928e64a68SDaniel Jeong bl->props.max_brightness); 27028e64a68SDaniel Jeong return bl->props.brightness; 27128e64a68SDaniel Jeong } 27228e64a68SDaniel Jeong 27328e64a68SDaniel Jeong /* disable sleep */ 27428e64a68SDaniel Jeong ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 27528e64a68SDaniel Jeong if (ret < 0) 27628e64a68SDaniel Jeong goto out_i2c_err; 27728e64a68SDaniel Jeong usleep_range(1000, 2000); 27828e64a68SDaniel Jeong /* minimum brightness is 0x04 */ 27928e64a68SDaniel Jeong ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness); 28028e64a68SDaniel Jeong if (bl->props.brightness < 0x4) 28128e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0); 28228e64a68SDaniel Jeong else 28328e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, 28428e64a68SDaniel Jeong LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE); 28528e64a68SDaniel Jeong if (ret < 0) 28628e64a68SDaniel Jeong goto out_i2c_err; 287d3f48ec0SBrian Masney return 0; 28828e64a68SDaniel Jeong 28928e64a68SDaniel Jeong out_i2c_err: 29028e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); 29128e64a68SDaniel Jeong return bl->props.brightness; 29228e64a68SDaniel Jeong } 29328e64a68SDaniel Jeong 29428e64a68SDaniel Jeong static int lm3630a_bank_b_get_brightness(struct backlight_device *bl) 29528e64a68SDaniel Jeong { 29628e64a68SDaniel Jeong int brightness, rval; 29728e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl); 29828e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 29928e64a68SDaniel Jeong 30028e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { 30128e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); 30228e64a68SDaniel Jeong if (rval < 0) 30328e64a68SDaniel Jeong goto out_i2c_err; 30428e64a68SDaniel Jeong brightness = (rval & 0x01) << 8; 30528e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTLOW); 30628e64a68SDaniel Jeong if (rval < 0) 30728e64a68SDaniel Jeong goto out_i2c_err; 30828e64a68SDaniel Jeong brightness |= rval; 30928e64a68SDaniel Jeong goto out; 31028e64a68SDaniel Jeong } 31128e64a68SDaniel Jeong 31228e64a68SDaniel Jeong /* disable sleep */ 31328e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 31428e64a68SDaniel Jeong if (rval < 0) 31528e64a68SDaniel Jeong goto out_i2c_err; 31628e64a68SDaniel Jeong usleep_range(1000, 2000); 31728e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_BRT_B); 31828e64a68SDaniel Jeong if (rval < 0) 31928e64a68SDaniel Jeong goto out_i2c_err; 32028e64a68SDaniel Jeong brightness = rval; 32128e64a68SDaniel Jeong 32228e64a68SDaniel Jeong out: 32328e64a68SDaniel Jeong bl->props.brightness = brightness; 32428e64a68SDaniel Jeong return bl->props.brightness; 32528e64a68SDaniel Jeong out_i2c_err: 32628e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 32728e64a68SDaniel Jeong return 0; 32828e64a68SDaniel Jeong } 32928e64a68SDaniel Jeong 33028e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_b_ops = { 33128e64a68SDaniel Jeong .options = BL_CORE_SUSPENDRESUME, 33228e64a68SDaniel Jeong .update_status = lm3630a_bank_b_update_status, 33328e64a68SDaniel Jeong .get_brightness = lm3630a_bank_b_get_brightness, 33428e64a68SDaniel Jeong }; 33528e64a68SDaniel Jeong 33628e64a68SDaniel Jeong static int lm3630a_backlight_register(struct lm3630a_chip *pchip) 33728e64a68SDaniel Jeong { 33828e64a68SDaniel Jeong struct lm3630a_platform_data *pdata = pchip->pdata; 3398fbce8efSBrian Masney struct backlight_properties props; 3408fbce8efSBrian Masney const char *label; 34128e64a68SDaniel Jeong 34228e64a68SDaniel Jeong props.type = BACKLIGHT_RAW; 34328e64a68SDaniel Jeong if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) { 34428e64a68SDaniel Jeong props.brightness = pdata->leda_init_brt; 34528e64a68SDaniel Jeong props.max_brightness = pdata->leda_max_brt; 3468fbce8efSBrian Masney label = pdata->leda_label ? pdata->leda_label : "lm3630a_leda"; 34728e64a68SDaniel Jeong pchip->bleda = 3488fbce8efSBrian Masney devm_backlight_device_register(pchip->dev, label, 34928e64a68SDaniel Jeong pchip->dev, pchip, 35028e64a68SDaniel Jeong &lm3630a_bank_a_ops, &props); 35128e64a68SDaniel Jeong if (IS_ERR(pchip->bleda)) 35228e64a68SDaniel Jeong return PTR_ERR(pchip->bleda); 35328e64a68SDaniel Jeong } 35428e64a68SDaniel Jeong 35528e64a68SDaniel Jeong if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) && 35628e64a68SDaniel Jeong (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) { 35728e64a68SDaniel Jeong props.brightness = pdata->ledb_init_brt; 35828e64a68SDaniel Jeong props.max_brightness = pdata->ledb_max_brt; 3598fbce8efSBrian Masney label = pdata->ledb_label ? pdata->ledb_label : "lm3630a_ledb"; 36028e64a68SDaniel Jeong pchip->bledb = 3618fbce8efSBrian Masney devm_backlight_device_register(pchip->dev, label, 36228e64a68SDaniel Jeong pchip->dev, pchip, 36328e64a68SDaniel Jeong &lm3630a_bank_b_ops, &props); 36428e64a68SDaniel Jeong if (IS_ERR(pchip->bledb)) 36528e64a68SDaniel Jeong return PTR_ERR(pchip->bledb); 36628e64a68SDaniel Jeong } 36728e64a68SDaniel Jeong return 0; 36828e64a68SDaniel Jeong } 36928e64a68SDaniel Jeong 37028e64a68SDaniel Jeong static const struct regmap_config lm3630a_regmap = { 37128e64a68SDaniel Jeong .reg_bits = 8, 37228e64a68SDaniel Jeong .val_bits = 8, 37328e64a68SDaniel Jeong .max_register = REG_MAX, 37428e64a68SDaniel Jeong }; 37528e64a68SDaniel Jeong 3768fbce8efSBrian Masney static int lm3630a_parse_led_sources(struct fwnode_handle *node, 3778fbce8efSBrian Masney int default_led_sources) 3788fbce8efSBrian Masney { 3798fbce8efSBrian Masney u32 sources[LM3630A_NUM_SINKS]; 3808fbce8efSBrian Masney int ret, num_sources, i; 3818fbce8efSBrian Masney 382072e2c81SAndy Shevchenko num_sources = fwnode_property_count_u32(node, "led-sources"); 3838fbce8efSBrian Masney if (num_sources < 0) 3848fbce8efSBrian Masney return default_led_sources; 3858fbce8efSBrian Masney else if (num_sources > ARRAY_SIZE(sources)) 3868fbce8efSBrian Masney return -EINVAL; 3878fbce8efSBrian Masney 3888fbce8efSBrian Masney ret = fwnode_property_read_u32_array(node, "led-sources", sources, 3898fbce8efSBrian Masney num_sources); 3908fbce8efSBrian Masney if (ret) 3918fbce8efSBrian Masney return ret; 3928fbce8efSBrian Masney 3938fbce8efSBrian Masney for (i = 0; i < num_sources; i++) { 3943e799ccdSLee Jones if (sources[i] != LM3630A_SINK_0 && sources[i] != LM3630A_SINK_1) 3958fbce8efSBrian Masney return -EINVAL; 3968fbce8efSBrian Masney 3978fbce8efSBrian Masney ret |= BIT(sources[i]); 3988fbce8efSBrian Masney } 3998fbce8efSBrian Masney 4008fbce8efSBrian Masney return ret; 4018fbce8efSBrian Masney } 4028fbce8efSBrian Masney 4038fbce8efSBrian Masney static int lm3630a_parse_bank(struct lm3630a_platform_data *pdata, 4048fbce8efSBrian Masney struct fwnode_handle *node, int *seen_led_sources) 4058fbce8efSBrian Masney { 4068fbce8efSBrian Masney int led_sources, ret; 4078fbce8efSBrian Masney const char *label; 4088fbce8efSBrian Masney u32 bank, val; 4098fbce8efSBrian Masney bool linear; 4108fbce8efSBrian Masney 4118fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "reg", &bank); 4128fbce8efSBrian Masney if (ret) 4138fbce8efSBrian Masney return ret; 4148fbce8efSBrian Masney 4153e799ccdSLee Jones if (bank != LM3630A_BANK_0 && bank != LM3630A_BANK_1) 4168fbce8efSBrian Masney return -EINVAL; 4178fbce8efSBrian Masney 4188fbce8efSBrian Masney led_sources = lm3630a_parse_led_sources(node, BIT(bank)); 4198fbce8efSBrian Masney if (led_sources < 0) 4208fbce8efSBrian Masney return led_sources; 4218fbce8efSBrian Masney 4228fbce8efSBrian Masney if (*seen_led_sources & led_sources) 4238fbce8efSBrian Masney return -EINVAL; 4248fbce8efSBrian Masney 4258fbce8efSBrian Masney *seen_led_sources |= led_sources; 4268fbce8efSBrian Masney 4278fbce8efSBrian Masney linear = fwnode_property_read_bool(node, 4288fbce8efSBrian Masney "ti,linear-mapping-mode"); 4298fbce8efSBrian Masney if (bank) { 4308fbce8efSBrian Masney if (led_sources & BIT(LM3630A_SINK_0) || 4318fbce8efSBrian Masney !(led_sources & BIT(LM3630A_SINK_1))) 4328fbce8efSBrian Masney return -EINVAL; 4338fbce8efSBrian Masney 4348fbce8efSBrian Masney pdata->ledb_ctrl = linear ? 4358fbce8efSBrian Masney LM3630A_LEDB_ENABLE_LINEAR : 4368fbce8efSBrian Masney LM3630A_LEDB_ENABLE; 4378fbce8efSBrian Masney } else { 4388fbce8efSBrian Masney if (!(led_sources & BIT(LM3630A_SINK_0))) 4398fbce8efSBrian Masney return -EINVAL; 4408fbce8efSBrian Masney 4418fbce8efSBrian Masney pdata->leda_ctrl = linear ? 4428fbce8efSBrian Masney LM3630A_LEDA_ENABLE_LINEAR : 4438fbce8efSBrian Masney LM3630A_LEDA_ENABLE; 4448fbce8efSBrian Masney 4458fbce8efSBrian Masney if (led_sources & BIT(LM3630A_SINK_1)) 4468fbce8efSBrian Masney pdata->ledb_ctrl = LM3630A_LEDB_ON_A; 4478fbce8efSBrian Masney } 4488fbce8efSBrian Masney 4498fbce8efSBrian Masney ret = fwnode_property_read_string(node, "label", &label); 4508fbce8efSBrian Masney if (!ret) { 4518fbce8efSBrian Masney if (bank) 4528fbce8efSBrian Masney pdata->ledb_label = label; 4538fbce8efSBrian Masney else 4548fbce8efSBrian Masney pdata->leda_label = label; 4558fbce8efSBrian Masney } 4568fbce8efSBrian Masney 4578fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "default-brightness", 4588fbce8efSBrian Masney &val); 4598fbce8efSBrian Masney if (!ret) { 4608fbce8efSBrian Masney if (bank) 4618fbce8efSBrian Masney pdata->ledb_init_brt = val; 4628fbce8efSBrian Masney else 4638fbce8efSBrian Masney pdata->leda_init_brt = val; 4648fbce8efSBrian Masney } 4658fbce8efSBrian Masney 4668fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "max-brightness", &val); 4678fbce8efSBrian Masney if (!ret) { 4688fbce8efSBrian Masney if (bank) 4698fbce8efSBrian Masney pdata->ledb_max_brt = val; 4708fbce8efSBrian Masney else 4718fbce8efSBrian Masney pdata->leda_max_brt = val; 4728fbce8efSBrian Masney } 4738fbce8efSBrian Masney 4748fbce8efSBrian Masney return 0; 4758fbce8efSBrian Masney } 4768fbce8efSBrian Masney 4778fbce8efSBrian Masney static int lm3630a_parse_node(struct lm3630a_chip *pchip, 4788fbce8efSBrian Masney struct lm3630a_platform_data *pdata) 4798fbce8efSBrian Masney { 4808fbce8efSBrian Masney int ret = -ENODEV, seen_led_sources = 0; 4818fbce8efSBrian Masney struct fwnode_handle *node; 4828fbce8efSBrian Masney 4838fbce8efSBrian Masney device_for_each_child_node(pchip->dev, node) { 4848fbce8efSBrian Masney ret = lm3630a_parse_bank(pdata, node, &seen_led_sources); 485*6d1c32dbSAndy Shevchenko if (ret) { 486*6d1c32dbSAndy Shevchenko fwnode_handle_put(node); 4878fbce8efSBrian Masney return ret; 4888fbce8efSBrian Masney } 489*6d1c32dbSAndy Shevchenko } 4908fbce8efSBrian Masney 4918fbce8efSBrian Masney return ret; 4928fbce8efSBrian Masney } 4938fbce8efSBrian Masney 49428e64a68SDaniel Jeong static int lm3630a_probe(struct i2c_client *client, 49528e64a68SDaniel Jeong const struct i2c_device_id *id) 49628e64a68SDaniel Jeong { 497c512794cSJingoo Han struct lm3630a_platform_data *pdata = dev_get_platdata(&client->dev); 49828e64a68SDaniel Jeong struct lm3630a_chip *pchip; 49928e64a68SDaniel Jeong int rval; 50028e64a68SDaniel Jeong 50128e64a68SDaniel Jeong if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 50228e64a68SDaniel Jeong dev_err(&client->dev, "fail : i2c functionality check\n"); 50328e64a68SDaniel Jeong return -EOPNOTSUPP; 50428e64a68SDaniel Jeong } 50528e64a68SDaniel Jeong 50628e64a68SDaniel Jeong pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip), 50728e64a68SDaniel Jeong GFP_KERNEL); 50828e64a68SDaniel Jeong if (!pchip) 50928e64a68SDaniel Jeong return -ENOMEM; 51028e64a68SDaniel Jeong pchip->dev = &client->dev; 51128e64a68SDaniel Jeong 51228e64a68SDaniel Jeong pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap); 51328e64a68SDaniel Jeong if (IS_ERR(pchip->regmap)) { 51428e64a68SDaniel Jeong rval = PTR_ERR(pchip->regmap); 51528e64a68SDaniel Jeong dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval); 51628e64a68SDaniel Jeong return rval; 51728e64a68SDaniel Jeong } 51828e64a68SDaniel Jeong 51928e64a68SDaniel Jeong i2c_set_clientdata(client, pchip); 52028e64a68SDaniel Jeong if (pdata == NULL) { 521e19493c1SDan Carpenter pdata = devm_kzalloc(pchip->dev, 522e19493c1SDan Carpenter sizeof(struct lm3630a_platform_data), 52328e64a68SDaniel Jeong GFP_KERNEL); 524e19493c1SDan Carpenter if (pdata == NULL) 52528e64a68SDaniel Jeong return -ENOMEM; 5268fbce8efSBrian Masney 52728e64a68SDaniel Jeong /* default values */ 528e19493c1SDan Carpenter pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS; 529e19493c1SDan Carpenter pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS; 530e19493c1SDan Carpenter pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS; 531e19493c1SDan Carpenter pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS; 5328fbce8efSBrian Masney 5338fbce8efSBrian Masney rval = lm3630a_parse_node(pchip, pdata); 5348fbce8efSBrian Masney if (rval) { 5358fbce8efSBrian Masney dev_err(&client->dev, "fail : parse node\n"); 5368fbce8efSBrian Masney return rval; 5378fbce8efSBrian Masney } 53828e64a68SDaniel Jeong } 539e19493c1SDan Carpenter pchip->pdata = pdata; 540e19493c1SDan Carpenter 5410e0e78e3SAndreas Kemnade pchip->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", 5420e0e78e3SAndreas Kemnade GPIOD_OUT_HIGH); 5430e0e78e3SAndreas Kemnade if (IS_ERR(pchip->enable_gpio)) { 5440e0e78e3SAndreas Kemnade rval = PTR_ERR(pchip->enable_gpio); 5450e0e78e3SAndreas Kemnade return rval; 5460e0e78e3SAndreas Kemnade } 5470e0e78e3SAndreas Kemnade 54828e64a68SDaniel Jeong /* chip initialize */ 54928e64a68SDaniel Jeong rval = lm3630a_chip_init(pchip); 55028e64a68SDaniel Jeong if (rval < 0) { 55128e64a68SDaniel Jeong dev_err(&client->dev, "fail : init chip\n"); 55228e64a68SDaniel Jeong return rval; 55328e64a68SDaniel Jeong } 55428e64a68SDaniel Jeong /* backlight register */ 55528e64a68SDaniel Jeong rval = lm3630a_backlight_register(pchip); 55628e64a68SDaniel Jeong if (rval < 0) { 55728e64a68SDaniel Jeong dev_err(&client->dev, "fail : backlight register.\n"); 55828e64a68SDaniel Jeong return rval; 55928e64a68SDaniel Jeong } 56028e64a68SDaniel Jeong /* pwm */ 56128e64a68SDaniel Jeong if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) { 56228e64a68SDaniel Jeong pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm"); 56328e64a68SDaniel Jeong if (IS_ERR(pchip->pwmd)) { 56428e64a68SDaniel Jeong dev_err(&client->dev, "fail : get pwm device\n"); 56528e64a68SDaniel Jeong return PTR_ERR(pchip->pwmd); 56628e64a68SDaniel Jeong } 5677ff666bcSBoris Brezillon 5687ff666bcSBoris Brezillon /* 5697ff666bcSBoris Brezillon * FIXME: pwm_apply_args() should be removed when switching to 5707ff666bcSBoris Brezillon * the atomic PWM API. 5717ff666bcSBoris Brezillon */ 5727ff666bcSBoris Brezillon pwm_apply_args(pchip->pwmd); 57328e64a68SDaniel Jeong } 57428e64a68SDaniel Jeong 57528e64a68SDaniel Jeong /* interrupt enable : irq 0 is not allowed */ 57628e64a68SDaniel Jeong pchip->irq = client->irq; 57728e64a68SDaniel Jeong if (pchip->irq) { 57828e64a68SDaniel Jeong rval = lm3630a_intr_config(pchip); 57928e64a68SDaniel Jeong if (rval < 0) 58028e64a68SDaniel Jeong return rval; 58128e64a68SDaniel Jeong } 58228e64a68SDaniel Jeong dev_info(&client->dev, "LM3630A backlight register OK.\n"); 58328e64a68SDaniel Jeong return 0; 58428e64a68SDaniel Jeong } 58528e64a68SDaniel Jeong 58628e64a68SDaniel Jeong static int lm3630a_remove(struct i2c_client *client) 58728e64a68SDaniel Jeong { 58828e64a68SDaniel Jeong int rval; 58928e64a68SDaniel Jeong struct lm3630a_chip *pchip = i2c_get_clientdata(client); 59028e64a68SDaniel Jeong 59128e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_BRT_A, 0); 59228e64a68SDaniel Jeong if (rval < 0) 59328e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 59428e64a68SDaniel Jeong 59528e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_BRT_B, 0); 59628e64a68SDaniel Jeong if (rval < 0) 59728e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n"); 59828e64a68SDaniel Jeong 59928e64a68SDaniel Jeong if (pchip->irq) { 60028e64a68SDaniel Jeong free_irq(pchip->irq, pchip); 60128e64a68SDaniel Jeong flush_workqueue(pchip->irqthread); 60228e64a68SDaniel Jeong destroy_workqueue(pchip->irqthread); 60328e64a68SDaniel Jeong } 60428e64a68SDaniel Jeong return 0; 60528e64a68SDaniel Jeong } 60628e64a68SDaniel Jeong 60728e64a68SDaniel Jeong static const struct i2c_device_id lm3630a_id[] = { 60828e64a68SDaniel Jeong {LM3630A_NAME, 0}, 60928e64a68SDaniel Jeong {} 61028e64a68SDaniel Jeong }; 61128e64a68SDaniel Jeong 6128ad003e7SAndreas Kemnade MODULE_DEVICE_TABLE(i2c, lm3630a_id); 6138ad003e7SAndreas Kemnade 6148fbce8efSBrian Masney static const struct of_device_id lm3630a_match_table[] = { 6158fbce8efSBrian Masney { .compatible = "ti,lm3630a", }, 6168fbce8efSBrian Masney { }, 6178fbce8efSBrian Masney }; 6188fbce8efSBrian Masney 6198ad003e7SAndreas Kemnade MODULE_DEVICE_TABLE(of, lm3630a_match_table); 62028e64a68SDaniel Jeong 62128e64a68SDaniel Jeong static struct i2c_driver lm3630a_i2c_driver = { 62228e64a68SDaniel Jeong .driver = { 62328e64a68SDaniel Jeong .name = LM3630A_NAME, 6248fbce8efSBrian Masney .of_match_table = lm3630a_match_table, 62528e64a68SDaniel Jeong }, 62628e64a68SDaniel Jeong .probe = lm3630a_probe, 62728e64a68SDaniel Jeong .remove = lm3630a_remove, 62828e64a68SDaniel Jeong .id_table = lm3630a_id, 62928e64a68SDaniel Jeong }; 63028e64a68SDaniel Jeong 63128e64a68SDaniel Jeong module_i2c_driver(lm3630a_i2c_driver); 63228e64a68SDaniel Jeong 63328e64a68SDaniel Jeong MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A"); 63428e64a68SDaniel Jeong MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); 63528e64a68SDaniel Jeong MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>"); 63628e64a68SDaniel Jeong MODULE_LICENSE("GPL v2"); 637