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;
551181f216SUwe Kleine-König struct pwm_state pwmd_state;
5628e64a68SDaniel Jeong };
5728e64a68SDaniel Jeong
5828e64a68SDaniel Jeong /* i2c access */
lm3630a_read(struct lm3630a_chip * pchip,unsigned int reg)5928e64a68SDaniel Jeong static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg)
6028e64a68SDaniel Jeong {
6128e64a68SDaniel Jeong int rval;
6228e64a68SDaniel Jeong unsigned int reg_val;
6328e64a68SDaniel Jeong
6428e64a68SDaniel Jeong rval = regmap_read(pchip->regmap, reg, ®_val);
6528e64a68SDaniel Jeong if (rval < 0)
6628e64a68SDaniel Jeong return rval;
6728e64a68SDaniel Jeong return reg_val & 0xFF;
6828e64a68SDaniel Jeong }
6928e64a68SDaniel Jeong
lm3630a_write(struct lm3630a_chip * pchip,unsigned int reg,unsigned int data)7028e64a68SDaniel Jeong static int lm3630a_write(struct lm3630a_chip *pchip,
7128e64a68SDaniel Jeong unsigned int reg, unsigned int data)
7228e64a68SDaniel Jeong {
7328e64a68SDaniel Jeong return regmap_write(pchip->regmap, reg, data);
7428e64a68SDaniel Jeong }
7528e64a68SDaniel Jeong
lm3630a_update(struct lm3630a_chip * pchip,unsigned int reg,unsigned int mask,unsigned int data)7628e64a68SDaniel Jeong static int lm3630a_update(struct lm3630a_chip *pchip,
7728e64a68SDaniel Jeong unsigned int reg, unsigned int mask,
7828e64a68SDaniel Jeong unsigned int data)
7928e64a68SDaniel Jeong {
8028e64a68SDaniel Jeong return regmap_update_bits(pchip->regmap, reg, mask, data);
8128e64a68SDaniel Jeong }
8228e64a68SDaniel Jeong
8328e64a68SDaniel Jeong /* initialize chip */
lm3630a_chip_init(struct lm3630a_chip * pchip)8428e64a68SDaniel Jeong static int lm3630a_chip_init(struct lm3630a_chip *pchip)
8528e64a68SDaniel Jeong {
8628e64a68SDaniel Jeong int rval;
8728e64a68SDaniel Jeong struct lm3630a_platform_data *pdata = pchip->pdata;
8828e64a68SDaniel Jeong
8928e64a68SDaniel Jeong usleep_range(1000, 2000);
9028e64a68SDaniel Jeong /* set Filter Strength Register */
9106168a64SBhushan Shah rval = lm3630a_write(pchip, REG_FILTER_STRENGTH, 0x03);
9228e64a68SDaniel Jeong /* set Cofig. register */
9328e64a68SDaniel Jeong rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl);
9428e64a68SDaniel Jeong /* set boost control */
9528e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BOOST, 0x38);
9628e64a68SDaniel Jeong /* set current A */
9728e64a68SDaniel Jeong rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F);
9828e64a68SDaniel Jeong /* set current B */
9928e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_I_B, 0x1F);
10028e64a68SDaniel Jeong /* set control */
1014c4d2a3aSDaniel Jeong rval |= lm3630a_update(pchip, REG_CTRL, 0x14, pdata->leda_ctrl);
1024c4d2a3aSDaniel Jeong rval |= lm3630a_update(pchip, REG_CTRL, 0x0B, pdata->ledb_ctrl);
10328e64a68SDaniel Jeong usleep_range(1000, 2000);
10428e64a68SDaniel Jeong /* set brightness A and B */
10528e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt);
10628e64a68SDaniel Jeong rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt);
10728e64a68SDaniel Jeong
10828e64a68SDaniel Jeong if (rval < 0)
10928e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n");
11028e64a68SDaniel Jeong return rval;
11128e64a68SDaniel Jeong }
11228e64a68SDaniel Jeong
11328e64a68SDaniel Jeong /* interrupt handling */
lm3630a_delayed_func(struct work_struct * work)11428e64a68SDaniel Jeong static void lm3630a_delayed_func(struct work_struct *work)
11528e64a68SDaniel Jeong {
1162a0c316bSDan Carpenter int rval;
11728e64a68SDaniel Jeong struct lm3630a_chip *pchip;
11828e64a68SDaniel Jeong
11928e64a68SDaniel Jeong pchip = container_of(work, struct lm3630a_chip, work.work);
12028e64a68SDaniel Jeong
12128e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_INT_STATUS);
12228e64a68SDaniel Jeong if (rval < 0) {
12328e64a68SDaniel Jeong dev_err(pchip->dev,
12428e64a68SDaniel Jeong "i2c failed to access REG_INT_STATUS Register\n");
12528e64a68SDaniel Jeong return;
12628e64a68SDaniel Jeong }
12728e64a68SDaniel Jeong
12828e64a68SDaniel Jeong dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval);
12928e64a68SDaniel Jeong }
13028e64a68SDaniel Jeong
lm3630a_isr_func(int irq,void * chip)13128e64a68SDaniel Jeong static irqreturn_t lm3630a_isr_func(int irq, void *chip)
13228e64a68SDaniel Jeong {
13328e64a68SDaniel Jeong int rval;
13428e64a68SDaniel Jeong struct lm3630a_chip *pchip = chip;
13528e64a68SDaniel Jeong unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC);
13628e64a68SDaniel Jeong
13728e64a68SDaniel Jeong queue_delayed_work(pchip->irqthread, &pchip->work, delay);
13828e64a68SDaniel Jeong
13928e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
14028e64a68SDaniel Jeong if (rval < 0) {
14128e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n");
14228e64a68SDaniel Jeong return IRQ_NONE;
14328e64a68SDaniel Jeong }
14428e64a68SDaniel Jeong return IRQ_HANDLED;
14528e64a68SDaniel Jeong }
14628e64a68SDaniel Jeong
lm3630a_intr_config(struct lm3630a_chip * pchip)14728e64a68SDaniel Jeong static int lm3630a_intr_config(struct lm3630a_chip *pchip)
14828e64a68SDaniel Jeong {
14928e64a68SDaniel Jeong int rval;
15028e64a68SDaniel Jeong
15128e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_INT_EN, 0x87);
15228e64a68SDaniel Jeong if (rval < 0)
15328e64a68SDaniel Jeong return rval;
15428e64a68SDaniel Jeong
15528e64a68SDaniel Jeong INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func);
15628e64a68SDaniel Jeong pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd");
15728e64a68SDaniel Jeong if (!pchip->irqthread) {
15828e64a68SDaniel Jeong dev_err(pchip->dev, "create irq thread fail\n");
15928e64a68SDaniel Jeong return -ENOMEM;
16028e64a68SDaniel Jeong }
16128e64a68SDaniel Jeong if (request_threaded_irq
16228e64a68SDaniel Jeong (pchip->irq, NULL, lm3630a_isr_func,
16328e64a68SDaniel Jeong IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) {
16428e64a68SDaniel Jeong dev_err(pchip->dev, "request threaded irq fail\n");
165bcd5b416SWei Yongjun destroy_workqueue(pchip->irqthread);
16628e64a68SDaniel Jeong return -ENOMEM;
16728e64a68SDaniel Jeong }
16828e64a68SDaniel Jeong return rval;
16928e64a68SDaniel Jeong }
17028e64a68SDaniel Jeong
lm3630a_pwm_ctrl(struct lm3630a_chip * pchip,int br,int br_max)1711181f216SUwe Kleine-König static int lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
17228e64a68SDaniel Jeong {
1731181f216SUwe Kleine-König int err;
17428e64a68SDaniel Jeong
1751181f216SUwe Kleine-König pchip->pwmd_state.period = pchip->pdata->pwm_period;
1761181f216SUwe Kleine-König
1771181f216SUwe Kleine-König err = pwm_set_relative_duty_cycle(&pchip->pwmd_state, br, br_max);
1781181f216SUwe Kleine-König if (err)
1791181f216SUwe Kleine-König return err;
1801181f216SUwe Kleine-König
1811181f216SUwe Kleine-König pchip->pwmd_state.enabled = pchip->pwmd_state.duty_cycle ? true : false;
1821181f216SUwe Kleine-König
183c748a6d7SSean Young return pwm_apply_might_sleep(pchip->pwmd, &pchip->pwmd_state);
18428e64a68SDaniel Jeong }
18528e64a68SDaniel Jeong
18628e64a68SDaniel Jeong /* update and get brightness */
lm3630a_bank_a_update_status(struct backlight_device * bl)18728e64a68SDaniel Jeong static int lm3630a_bank_a_update_status(struct backlight_device *bl)
18828e64a68SDaniel Jeong {
18928e64a68SDaniel Jeong int ret;
19028e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl);
19128e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
192dc0ba74eSLuca Weiss int brightness = backlight_get_brightness(bl);
19328e64a68SDaniel Jeong
19428e64a68SDaniel Jeong /* pwm control */
1951181f216SUwe Kleine-König if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0)
196dc0ba74eSLuca Weiss return lm3630a_pwm_ctrl(pchip, brightness,
19728e64a68SDaniel Jeong bl->props.max_brightness);
19828e64a68SDaniel Jeong
19928e64a68SDaniel Jeong /* disable sleep */
20028e64a68SDaniel Jeong ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
20128e64a68SDaniel Jeong if (ret < 0)
20228e64a68SDaniel Jeong goto out_i2c_err;
20328e64a68SDaniel Jeong usleep_range(1000, 2000);
20428e64a68SDaniel Jeong /* minimum brightness is 0x04 */
205dc0ba74eSLuca Weiss ret = lm3630a_write(pchip, REG_BRT_A, brightness);
206ae9c4808SMaximilian Weigand
207dc0ba74eSLuca Weiss if (brightness < 0x4)
208ae9c4808SMaximilian Weigand /* turn the string off */
20928e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0);
21028e64a68SDaniel Jeong else
21128e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL,
21228e64a68SDaniel Jeong LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE);
21328e64a68SDaniel Jeong if (ret < 0)
21428e64a68SDaniel Jeong goto out_i2c_err;
215d3f48ec0SBrian Masney return 0;
21628e64a68SDaniel Jeong
21728e64a68SDaniel Jeong out_i2c_err:
218b9481a66SUwe Kleine-König dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret));
219b9481a66SUwe Kleine-König return ret;
22028e64a68SDaniel Jeong }
22128e64a68SDaniel Jeong
lm3630a_bank_a_get_brightness(struct backlight_device * bl)22228e64a68SDaniel Jeong static int lm3630a_bank_a_get_brightness(struct backlight_device *bl)
22328e64a68SDaniel Jeong {
22428e64a68SDaniel Jeong int brightness, rval;
22528e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl);
22628e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
22728e64a68SDaniel Jeong
22828e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
22928e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
23028e64a68SDaniel Jeong if (rval < 0)
23128e64a68SDaniel Jeong goto out_i2c_err;
23228e64a68SDaniel Jeong brightness = (rval & 0x01) << 8;
23328e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
23428e64a68SDaniel Jeong if (rval < 0)
23528e64a68SDaniel Jeong goto out_i2c_err;
23628e64a68SDaniel Jeong brightness |= rval;
2374bf7ddd2SLuca Weiss return brightness;
23828e64a68SDaniel Jeong }
23928e64a68SDaniel Jeong
24028e64a68SDaniel Jeong /* disable sleep */
24128e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
24228e64a68SDaniel Jeong if (rval < 0)
24328e64a68SDaniel Jeong goto out_i2c_err;
24428e64a68SDaniel Jeong usleep_range(1000, 2000);
24528e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_BRT_A);
24628e64a68SDaniel Jeong if (rval < 0)
24728e64a68SDaniel Jeong goto out_i2c_err;
2484bf7ddd2SLuca Weiss return rval;
24928e64a68SDaniel Jeong
25028e64a68SDaniel Jeong out_i2c_err:
25128e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n");
25228e64a68SDaniel Jeong return 0;
25328e64a68SDaniel Jeong }
25428e64a68SDaniel Jeong
25528e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_a_ops = {
25628e64a68SDaniel Jeong .options = BL_CORE_SUSPENDRESUME,
25728e64a68SDaniel Jeong .update_status = lm3630a_bank_a_update_status,
25828e64a68SDaniel Jeong .get_brightness = lm3630a_bank_a_get_brightness,
25928e64a68SDaniel Jeong };
26028e64a68SDaniel Jeong
26128e64a68SDaniel Jeong /* update and get brightness */
lm3630a_bank_b_update_status(struct backlight_device * bl)26228e64a68SDaniel Jeong static int lm3630a_bank_b_update_status(struct backlight_device *bl)
26328e64a68SDaniel Jeong {
26428e64a68SDaniel Jeong int ret;
26528e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl);
26628e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
267dc0ba74eSLuca Weiss int brightness = backlight_get_brightness(bl);
26828e64a68SDaniel Jeong
26928e64a68SDaniel Jeong /* pwm control */
2701181f216SUwe Kleine-König if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0)
271dc0ba74eSLuca Weiss return lm3630a_pwm_ctrl(pchip, brightness,
27228e64a68SDaniel Jeong bl->props.max_brightness);
27328e64a68SDaniel Jeong
27428e64a68SDaniel Jeong /* disable sleep */
27528e64a68SDaniel Jeong ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
27628e64a68SDaniel Jeong if (ret < 0)
27728e64a68SDaniel Jeong goto out_i2c_err;
27828e64a68SDaniel Jeong usleep_range(1000, 2000);
27928e64a68SDaniel Jeong /* minimum brightness is 0x04 */
280dc0ba74eSLuca Weiss ret = lm3630a_write(pchip, REG_BRT_B, brightness);
281ae9c4808SMaximilian Weigand
282dc0ba74eSLuca Weiss if (brightness < 0x4)
283ae9c4808SMaximilian Weigand /* turn the string off */
28428e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0);
28528e64a68SDaniel Jeong else
28628e64a68SDaniel Jeong ret |= lm3630a_update(pchip, REG_CTRL,
28728e64a68SDaniel Jeong LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE);
28828e64a68SDaniel Jeong if (ret < 0)
28928e64a68SDaniel Jeong goto out_i2c_err;
290d3f48ec0SBrian Masney return 0;
29128e64a68SDaniel Jeong
29228e64a68SDaniel Jeong out_i2c_err:
293b9481a66SUwe Kleine-König dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret));
294b9481a66SUwe Kleine-König return ret;
29528e64a68SDaniel Jeong }
29628e64a68SDaniel Jeong
lm3630a_bank_b_get_brightness(struct backlight_device * bl)29728e64a68SDaniel Jeong static int lm3630a_bank_b_get_brightness(struct backlight_device *bl)
29828e64a68SDaniel Jeong {
29928e64a68SDaniel Jeong int brightness, rval;
30028e64a68SDaniel Jeong struct lm3630a_chip *pchip = bl_get_data(bl);
30128e64a68SDaniel Jeong enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
30228e64a68SDaniel Jeong
30328e64a68SDaniel Jeong if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
30428e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
30528e64a68SDaniel Jeong if (rval < 0)
30628e64a68SDaniel Jeong goto out_i2c_err;
30728e64a68SDaniel Jeong brightness = (rval & 0x01) << 8;
30828e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
30928e64a68SDaniel Jeong if (rval < 0)
31028e64a68SDaniel Jeong goto out_i2c_err;
31128e64a68SDaniel Jeong brightness |= rval;
3124bf7ddd2SLuca Weiss return brightness;
31328e64a68SDaniel Jeong }
31428e64a68SDaniel Jeong
31528e64a68SDaniel Jeong /* disable sleep */
31628e64a68SDaniel Jeong rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
31728e64a68SDaniel Jeong if (rval < 0)
31828e64a68SDaniel Jeong goto out_i2c_err;
31928e64a68SDaniel Jeong usleep_range(1000, 2000);
32028e64a68SDaniel Jeong rval = lm3630a_read(pchip, REG_BRT_B);
32128e64a68SDaniel Jeong if (rval < 0)
32228e64a68SDaniel Jeong goto out_i2c_err;
3234bf7ddd2SLuca Weiss return rval;
32428e64a68SDaniel Jeong
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
lm3630a_backlight_register(struct lm3630a_chip * pchip)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
342ad9aeb0eSLuca Weiss memset(&props, 0, sizeof(struct backlight_properties));
34328e64a68SDaniel Jeong props.type = BACKLIGHT_RAW;
34428e64a68SDaniel Jeong if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) {
34528e64a68SDaniel Jeong props.brightness = pdata->leda_init_brt;
34628e64a68SDaniel Jeong props.max_brightness = pdata->leda_max_brt;
3478fbce8efSBrian Masney label = pdata->leda_label ? pdata->leda_label : "lm3630a_leda";
34828e64a68SDaniel Jeong pchip->bleda =
3498fbce8efSBrian Masney devm_backlight_device_register(pchip->dev, label,
35028e64a68SDaniel Jeong pchip->dev, pchip,
35128e64a68SDaniel Jeong &lm3630a_bank_a_ops, &props);
35228e64a68SDaniel Jeong if (IS_ERR(pchip->bleda))
35328e64a68SDaniel Jeong return PTR_ERR(pchip->bleda);
35428e64a68SDaniel Jeong }
35528e64a68SDaniel Jeong
35628e64a68SDaniel Jeong if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) &&
35728e64a68SDaniel Jeong (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) {
35828e64a68SDaniel Jeong props.brightness = pdata->ledb_init_brt;
35928e64a68SDaniel Jeong props.max_brightness = pdata->ledb_max_brt;
3608fbce8efSBrian Masney label = pdata->ledb_label ? pdata->ledb_label : "lm3630a_ledb";
36128e64a68SDaniel Jeong pchip->bledb =
3628fbce8efSBrian Masney devm_backlight_device_register(pchip->dev, label,
36328e64a68SDaniel Jeong pchip->dev, pchip,
36428e64a68SDaniel Jeong &lm3630a_bank_b_ops, &props);
36528e64a68SDaniel Jeong if (IS_ERR(pchip->bledb))
36628e64a68SDaniel Jeong return PTR_ERR(pchip->bledb);
36728e64a68SDaniel Jeong }
36828e64a68SDaniel Jeong return 0;
36928e64a68SDaniel Jeong }
37028e64a68SDaniel Jeong
37128e64a68SDaniel Jeong static const struct regmap_config lm3630a_regmap = {
37228e64a68SDaniel Jeong .reg_bits = 8,
37328e64a68SDaniel Jeong .val_bits = 8,
37428e64a68SDaniel Jeong .max_register = REG_MAX,
37528e64a68SDaniel Jeong };
37628e64a68SDaniel Jeong
lm3630a_parse_led_sources(struct fwnode_handle * node,int default_led_sources)3778fbce8efSBrian Masney static int lm3630a_parse_led_sources(struct fwnode_handle *node,
3788fbce8efSBrian Masney int default_led_sources)
3798fbce8efSBrian Masney {
3808fbce8efSBrian Masney u32 sources[LM3630A_NUM_SINKS];
3818fbce8efSBrian Masney int ret, num_sources, i;
3828fbce8efSBrian Masney
383072e2c81SAndy Shevchenko num_sources = fwnode_property_count_u32(node, "led-sources");
3848fbce8efSBrian Masney if (num_sources < 0)
3858fbce8efSBrian Masney return default_led_sources;
3868fbce8efSBrian Masney else if (num_sources > ARRAY_SIZE(sources))
3878fbce8efSBrian Masney return -EINVAL;
3888fbce8efSBrian Masney
3898fbce8efSBrian Masney ret = fwnode_property_read_u32_array(node, "led-sources", sources,
3908fbce8efSBrian Masney num_sources);
3918fbce8efSBrian Masney if (ret)
3928fbce8efSBrian Masney return ret;
3938fbce8efSBrian Masney
3948fbce8efSBrian Masney for (i = 0; i < num_sources; i++) {
3953e799ccdSLee Jones if (sources[i] != LM3630A_SINK_0 && sources[i] != LM3630A_SINK_1)
3968fbce8efSBrian Masney return -EINVAL;
3978fbce8efSBrian Masney
3988fbce8efSBrian Masney ret |= BIT(sources[i]);
3998fbce8efSBrian Masney }
4008fbce8efSBrian Masney
4018fbce8efSBrian Masney return ret;
4028fbce8efSBrian Masney }
4038fbce8efSBrian Masney
lm3630a_parse_bank(struct lm3630a_platform_data * pdata,struct fwnode_handle * node,int * seen_led_sources)4048fbce8efSBrian Masney static int lm3630a_parse_bank(struct lm3630a_platform_data *pdata,
4058fbce8efSBrian Masney struct fwnode_handle *node, int *seen_led_sources)
4068fbce8efSBrian Masney {
4078fbce8efSBrian Masney int led_sources, ret;
4088fbce8efSBrian Masney const char *label;
4098fbce8efSBrian Masney u32 bank, val;
4108fbce8efSBrian Masney bool linear;
4118fbce8efSBrian Masney
4128fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "reg", &bank);
4138fbce8efSBrian Masney if (ret)
4148fbce8efSBrian Masney return ret;
4158fbce8efSBrian Masney
4163e799ccdSLee Jones if (bank != LM3630A_BANK_0 && bank != LM3630A_BANK_1)
4178fbce8efSBrian Masney return -EINVAL;
4188fbce8efSBrian Masney
4198fbce8efSBrian Masney led_sources = lm3630a_parse_led_sources(node, BIT(bank));
4208fbce8efSBrian Masney if (led_sources < 0)
4218fbce8efSBrian Masney return led_sources;
4228fbce8efSBrian Masney
4238fbce8efSBrian Masney if (*seen_led_sources & led_sources)
4248fbce8efSBrian Masney return -EINVAL;
4258fbce8efSBrian Masney
4268fbce8efSBrian Masney *seen_led_sources |= led_sources;
4278fbce8efSBrian Masney
4288fbce8efSBrian Masney linear = fwnode_property_read_bool(node,
4298fbce8efSBrian Masney "ti,linear-mapping-mode");
4308fbce8efSBrian Masney if (bank) {
4318fbce8efSBrian Masney if (led_sources & BIT(LM3630A_SINK_0) ||
4328fbce8efSBrian Masney !(led_sources & BIT(LM3630A_SINK_1)))
4338fbce8efSBrian Masney return -EINVAL;
4348fbce8efSBrian Masney
4358fbce8efSBrian Masney pdata->ledb_ctrl = linear ?
4368fbce8efSBrian Masney LM3630A_LEDB_ENABLE_LINEAR :
4378fbce8efSBrian Masney LM3630A_LEDB_ENABLE;
4388fbce8efSBrian Masney } else {
4398fbce8efSBrian Masney if (!(led_sources & BIT(LM3630A_SINK_0)))
4408fbce8efSBrian Masney return -EINVAL;
4418fbce8efSBrian Masney
4428fbce8efSBrian Masney pdata->leda_ctrl = linear ?
4438fbce8efSBrian Masney LM3630A_LEDA_ENABLE_LINEAR :
4448fbce8efSBrian Masney LM3630A_LEDA_ENABLE;
4458fbce8efSBrian Masney
4468fbce8efSBrian Masney if (led_sources & BIT(LM3630A_SINK_1))
4478fbce8efSBrian Masney pdata->ledb_ctrl = LM3630A_LEDB_ON_A;
4488fbce8efSBrian Masney }
4498fbce8efSBrian Masney
4508fbce8efSBrian Masney ret = fwnode_property_read_string(node, "label", &label);
4518fbce8efSBrian Masney if (!ret) {
4528fbce8efSBrian Masney if (bank)
4538fbce8efSBrian Masney pdata->ledb_label = label;
4548fbce8efSBrian Masney else
4558fbce8efSBrian Masney pdata->leda_label = label;
4568fbce8efSBrian Masney }
4578fbce8efSBrian Masney
4588fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "default-brightness",
4598fbce8efSBrian Masney &val);
4608fbce8efSBrian Masney if (!ret) {
4618fbce8efSBrian Masney if (bank)
4628fbce8efSBrian Masney pdata->ledb_init_brt = val;
4638fbce8efSBrian Masney else
4648fbce8efSBrian Masney pdata->leda_init_brt = val;
4658fbce8efSBrian Masney }
4668fbce8efSBrian Masney
4678fbce8efSBrian Masney ret = fwnode_property_read_u32(node, "max-brightness", &val);
4688fbce8efSBrian Masney if (!ret) {
4698fbce8efSBrian Masney if (bank)
4708fbce8efSBrian Masney pdata->ledb_max_brt = val;
4718fbce8efSBrian Masney else
4728fbce8efSBrian Masney pdata->leda_max_brt = val;
4738fbce8efSBrian Masney }
4748fbce8efSBrian Masney
4758fbce8efSBrian Masney return 0;
4768fbce8efSBrian Masney }
4778fbce8efSBrian Masney
lm3630a_parse_node(struct lm3630a_chip * pchip,struct lm3630a_platform_data * pdata)4788fbce8efSBrian Masney static int lm3630a_parse_node(struct lm3630a_chip *pchip,
4798fbce8efSBrian Masney struct lm3630a_platform_data *pdata)
4808fbce8efSBrian Masney {
4818fbce8efSBrian Masney int ret = -ENODEV, seen_led_sources = 0;
4828fbce8efSBrian Masney struct fwnode_handle *node;
4838fbce8efSBrian Masney
4848fbce8efSBrian Masney device_for_each_child_node(pchip->dev, node) {
4858fbce8efSBrian Masney ret = lm3630a_parse_bank(pdata, node, &seen_led_sources);
4866d1c32dbSAndy Shevchenko if (ret) {
4876d1c32dbSAndy Shevchenko fwnode_handle_put(node);
4888fbce8efSBrian Masney return ret;
4898fbce8efSBrian Masney }
4906d1c32dbSAndy Shevchenko }
4918fbce8efSBrian Masney
4928fbce8efSBrian Masney return ret;
4938fbce8efSBrian Masney }
4948fbce8efSBrian Masney
lm3630a_probe(struct i2c_client * client)495b2d4f93fSUwe Kleine-König static int lm3630a_probe(struct i2c_client *client)
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);
543c9128ed7SKrzysztof Kozlowski if (IS_ERR(pchip->enable_gpio))
544c9128ed7SKrzysztof Kozlowski return PTR_ERR(pchip->enable_gpio);
5450e0e78e3SAndreas Kemnade
54628e64a68SDaniel Jeong /* chip initialize */
54728e64a68SDaniel Jeong rval = lm3630a_chip_init(pchip);
54828e64a68SDaniel Jeong if (rval < 0) {
54928e64a68SDaniel Jeong dev_err(&client->dev, "fail : init chip\n");
55028e64a68SDaniel Jeong return rval;
55128e64a68SDaniel Jeong }
55228e64a68SDaniel Jeong /* backlight register */
55328e64a68SDaniel Jeong rval = lm3630a_backlight_register(pchip);
55428e64a68SDaniel Jeong if (rval < 0) {
55528e64a68SDaniel Jeong dev_err(&client->dev, "fail : backlight register.\n");
55628e64a68SDaniel Jeong return rval;
55728e64a68SDaniel Jeong }
55828e64a68SDaniel Jeong /* pwm */
55928e64a68SDaniel Jeong if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) {
56028e64a68SDaniel Jeong pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm");
561b4c385b2SKrzysztof Kozlowski if (IS_ERR(pchip->pwmd))
562b4c385b2SKrzysztof Kozlowski return dev_err_probe(&client->dev, PTR_ERR(pchip->pwmd),
563b4c385b2SKrzysztof Kozlowski "fail : get pwm device\n");
5647ff666bcSBoris Brezillon
5651181f216SUwe Kleine-König pwm_init_state(pchip->pwmd, &pchip->pwmd_state);
56628e64a68SDaniel Jeong }
56728e64a68SDaniel Jeong
56828e64a68SDaniel Jeong /* interrupt enable : irq 0 is not allowed */
56928e64a68SDaniel Jeong pchip->irq = client->irq;
57028e64a68SDaniel Jeong if (pchip->irq) {
57128e64a68SDaniel Jeong rval = lm3630a_intr_config(pchip);
57228e64a68SDaniel Jeong if (rval < 0)
57328e64a68SDaniel Jeong return rval;
57428e64a68SDaniel Jeong }
57528e64a68SDaniel Jeong dev_info(&client->dev, "LM3630A backlight register OK.\n");
57628e64a68SDaniel Jeong return 0;
57728e64a68SDaniel Jeong }
57828e64a68SDaniel Jeong
lm3630a_remove(struct i2c_client * client)579ed5c2f5fSUwe Kleine-König static void lm3630a_remove(struct i2c_client *client)
58028e64a68SDaniel Jeong {
58128e64a68SDaniel Jeong int rval;
58228e64a68SDaniel Jeong struct lm3630a_chip *pchip = i2c_get_clientdata(client);
58328e64a68SDaniel Jeong
58428e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_BRT_A, 0);
58528e64a68SDaniel Jeong if (rval < 0)
58628e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n");
58728e64a68SDaniel Jeong
58828e64a68SDaniel Jeong rval = lm3630a_write(pchip, REG_BRT_B, 0);
58928e64a68SDaniel Jeong if (rval < 0)
59028e64a68SDaniel Jeong dev_err(pchip->dev, "i2c failed to access register\n");
59128e64a68SDaniel Jeong
59228e64a68SDaniel Jeong if (pchip->irq) {
59328e64a68SDaniel Jeong free_irq(pchip->irq, pchip);
59428e64a68SDaniel Jeong destroy_workqueue(pchip->irqthread);
59528e64a68SDaniel Jeong }
59628e64a68SDaniel Jeong }
59728e64a68SDaniel Jeong
59828e64a68SDaniel Jeong static const struct i2c_device_id lm3630a_id[] = {
599*bfd35877SUwe Kleine-König { LM3630A_NAME },
60028e64a68SDaniel Jeong {}
60128e64a68SDaniel Jeong };
60228e64a68SDaniel Jeong
6038ad003e7SAndreas Kemnade MODULE_DEVICE_TABLE(i2c, lm3630a_id);
6048ad003e7SAndreas Kemnade
6058fbce8efSBrian Masney static const struct of_device_id lm3630a_match_table[] = {
6068fbce8efSBrian Masney { .compatible = "ti,lm3630a", },
6078fbce8efSBrian Masney { },
6088fbce8efSBrian Masney };
6098fbce8efSBrian Masney
6108ad003e7SAndreas Kemnade MODULE_DEVICE_TABLE(of, lm3630a_match_table);
61128e64a68SDaniel Jeong
61228e64a68SDaniel Jeong static struct i2c_driver lm3630a_i2c_driver = {
61328e64a68SDaniel Jeong .driver = {
61428e64a68SDaniel Jeong .name = LM3630A_NAME,
6158fbce8efSBrian Masney .of_match_table = lm3630a_match_table,
61628e64a68SDaniel Jeong },
61729554f2eSUwe Kleine-König .probe = lm3630a_probe,
61828e64a68SDaniel Jeong .remove = lm3630a_remove,
61928e64a68SDaniel Jeong .id_table = lm3630a_id,
62028e64a68SDaniel Jeong };
62128e64a68SDaniel Jeong
62228e64a68SDaniel Jeong module_i2c_driver(lm3630a_i2c_driver);
62328e64a68SDaniel Jeong
62428e64a68SDaniel Jeong MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A");
62528e64a68SDaniel Jeong MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
62628e64a68SDaniel Jeong MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>");
62728e64a68SDaniel Jeong MODULE_LICENSE("GPL v2");
628