xref: /openbmc/linux/drivers/video/backlight/lm3630a_bl.c (revision 8fbce8efe15cd2ca7a4947bc814f890dbe4e43d7)
128e64a68SDaniel Jeong /*
228e64a68SDaniel Jeong * Simple driver for Texas Instruments LM3630A Backlight driver chip
328e64a68SDaniel Jeong * Copyright (C) 2012 Texas Instruments
428e64a68SDaniel Jeong *
528e64a68SDaniel Jeong * This program is free software; you can redistribute it and/or modify
628e64a68SDaniel Jeong * it under the terms of the GNU General Public License version 2 as
728e64a68SDaniel Jeong * published by the Free Software Foundation.
828e64a68SDaniel Jeong *
928e64a68SDaniel Jeong */
1028e64a68SDaniel Jeong #include <linux/module.h>
1128e64a68SDaniel Jeong #include <linux/slab.h>
1228e64a68SDaniel Jeong #include <linux/i2c.h>
1328e64a68SDaniel Jeong #include <linux/backlight.h>
1428e64a68SDaniel Jeong #include <linux/err.h>
1528e64a68SDaniel Jeong #include <linux/delay.h>
1628e64a68SDaniel Jeong #include <linux/uaccess.h>
1728e64a68SDaniel Jeong #include <linux/interrupt.h>
1828e64a68SDaniel Jeong #include <linux/regmap.h>
1928e64a68SDaniel Jeong #include <linux/pwm.h>
2028e64a68SDaniel Jeong #include <linux/platform_data/lm3630a_bl.h>
2128e64a68SDaniel Jeong 
2228e64a68SDaniel Jeong #define REG_CTRL	0x00
2328e64a68SDaniel Jeong #define REG_BOOST	0x02
2428e64a68SDaniel Jeong #define REG_CONFIG	0x01
2528e64a68SDaniel Jeong #define REG_BRT_A	0x03
2628e64a68SDaniel Jeong #define REG_BRT_B	0x04
2728e64a68SDaniel Jeong #define REG_I_A		0x05
2828e64a68SDaniel Jeong #define REG_I_B		0x06
2928e64a68SDaniel Jeong #define REG_INT_STATUS	0x09
3028e64a68SDaniel Jeong #define REG_INT_EN	0x0A
3128e64a68SDaniel Jeong #define REG_FAULT	0x0B
3228e64a68SDaniel Jeong #define REG_PWM_OUTLOW	0x12
3328e64a68SDaniel Jeong #define REG_PWM_OUTHIGH	0x13
3406168a64SBhushan Shah #define REG_FILTER_STRENGTH	0x50
3506168a64SBhushan Shah #define REG_MAX		0x50
3628e64a68SDaniel Jeong 
3728e64a68SDaniel Jeong #define INT_DEBOUNCE_MSEC	10
38*8fbce8efSBrian Masney 
39*8fbce8efSBrian Masney #define LM3630A_BANK_0		0
40*8fbce8efSBrian Masney #define LM3630A_BANK_1		1
41*8fbce8efSBrian Masney 
42*8fbce8efSBrian Masney #define LM3630A_NUM_SINKS	2
43*8fbce8efSBrian Masney #define LM3630A_SINK_0		0
44*8fbce8efSBrian Masney #define LM3630A_SINK_1		1
45*8fbce8efSBrian Masney 
4628e64a68SDaniel Jeong struct lm3630a_chip {
4728e64a68SDaniel Jeong 	struct device *dev;
4828e64a68SDaniel Jeong 	struct delayed_work work;
4928e64a68SDaniel Jeong 
5028e64a68SDaniel Jeong 	int irq;
5128e64a68SDaniel Jeong 	struct workqueue_struct *irqthread;
5228e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata;
5328e64a68SDaniel Jeong 	struct backlight_device *bleda;
5428e64a68SDaniel Jeong 	struct backlight_device *bledb;
5528e64a68SDaniel Jeong 	struct regmap *regmap;
5628e64a68SDaniel Jeong 	struct pwm_device *pwmd;
5728e64a68SDaniel Jeong };
5828e64a68SDaniel Jeong 
5928e64a68SDaniel Jeong /* i2c access */
6028e64a68SDaniel Jeong static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg)
6128e64a68SDaniel Jeong {
6228e64a68SDaniel Jeong 	int rval;
6328e64a68SDaniel Jeong 	unsigned int reg_val;
6428e64a68SDaniel Jeong 
6528e64a68SDaniel Jeong 	rval = regmap_read(pchip->regmap, reg, &reg_val);
6628e64a68SDaniel Jeong 	if (rval < 0)
6728e64a68SDaniel Jeong 		return rval;
6828e64a68SDaniel Jeong 	return reg_val & 0xFF;
6928e64a68SDaniel Jeong }
7028e64a68SDaniel Jeong 
7128e64a68SDaniel Jeong static int lm3630a_write(struct lm3630a_chip *pchip,
7228e64a68SDaniel Jeong 			 unsigned int reg, unsigned int data)
7328e64a68SDaniel Jeong {
7428e64a68SDaniel Jeong 	return regmap_write(pchip->regmap, reg, data);
7528e64a68SDaniel Jeong }
7628e64a68SDaniel Jeong 
7728e64a68SDaniel Jeong static int lm3630a_update(struct lm3630a_chip *pchip,
7828e64a68SDaniel Jeong 			  unsigned int reg, unsigned int mask,
7928e64a68SDaniel Jeong 			  unsigned int data)
8028e64a68SDaniel Jeong {
8128e64a68SDaniel Jeong 	return regmap_update_bits(pchip->regmap, reg, mask, data);
8228e64a68SDaniel Jeong }
8328e64a68SDaniel Jeong 
8428e64a68SDaniel Jeong /* initialize chip */
8528e64a68SDaniel Jeong static int lm3630a_chip_init(struct lm3630a_chip *pchip)
8628e64a68SDaniel Jeong {
8728e64a68SDaniel Jeong 	int rval;
8828e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata = pchip->pdata;
8928e64a68SDaniel Jeong 
9028e64a68SDaniel Jeong 	usleep_range(1000, 2000);
9128e64a68SDaniel Jeong 	/* set Filter Strength Register */
9206168a64SBhushan Shah 	rval = lm3630a_write(pchip, REG_FILTER_STRENGTH, 0x03);
9328e64a68SDaniel Jeong 	/* set Cofig. register */
9428e64a68SDaniel Jeong 	rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl);
9528e64a68SDaniel Jeong 	/* set boost control */
9628e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BOOST, 0x38);
9728e64a68SDaniel Jeong 	/* set current A */
9828e64a68SDaniel Jeong 	rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F);
9928e64a68SDaniel Jeong 	/* set current B */
10028e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_I_B, 0x1F);
10128e64a68SDaniel Jeong 	/* set control */
1024c4d2a3aSDaniel Jeong 	rval |= lm3630a_update(pchip, REG_CTRL, 0x14, pdata->leda_ctrl);
1034c4d2a3aSDaniel Jeong 	rval |= lm3630a_update(pchip, REG_CTRL, 0x0B, pdata->ledb_ctrl);
10428e64a68SDaniel Jeong 	usleep_range(1000, 2000);
10528e64a68SDaniel Jeong 	/* set brightness A and B */
10628e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt);
10728e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt);
10828e64a68SDaniel Jeong 
10928e64a68SDaniel Jeong 	if (rval < 0)
11028e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
11128e64a68SDaniel Jeong 	return rval;
11228e64a68SDaniel Jeong }
11328e64a68SDaniel Jeong 
11428e64a68SDaniel Jeong /* interrupt handling */
11528e64a68SDaniel Jeong static void lm3630a_delayed_func(struct work_struct *work)
11628e64a68SDaniel Jeong {
1172a0c316bSDan Carpenter 	int rval;
11828e64a68SDaniel Jeong 	struct lm3630a_chip *pchip;
11928e64a68SDaniel Jeong 
12028e64a68SDaniel Jeong 	pchip = container_of(work, struct lm3630a_chip, work.work);
12128e64a68SDaniel Jeong 
12228e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_INT_STATUS);
12328e64a68SDaniel Jeong 	if (rval < 0) {
12428e64a68SDaniel Jeong 		dev_err(pchip->dev,
12528e64a68SDaniel Jeong 			"i2c failed to access REG_INT_STATUS Register\n");
12628e64a68SDaniel Jeong 		return;
12728e64a68SDaniel Jeong 	}
12828e64a68SDaniel Jeong 
12928e64a68SDaniel Jeong 	dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval);
13028e64a68SDaniel Jeong }
13128e64a68SDaniel Jeong 
13228e64a68SDaniel Jeong static irqreturn_t lm3630a_isr_func(int irq, void *chip)
13328e64a68SDaniel Jeong {
13428e64a68SDaniel Jeong 	int rval;
13528e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = chip;
13628e64a68SDaniel Jeong 	unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC);
13728e64a68SDaniel Jeong 
13828e64a68SDaniel Jeong 	queue_delayed_work(pchip->irqthread, &pchip->work, delay);
13928e64a68SDaniel Jeong 
14028e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
14128e64a68SDaniel Jeong 	if (rval < 0) {
14228e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
14328e64a68SDaniel Jeong 		return IRQ_NONE;
14428e64a68SDaniel Jeong 	}
14528e64a68SDaniel Jeong 	return IRQ_HANDLED;
14628e64a68SDaniel Jeong }
14728e64a68SDaniel Jeong 
14828e64a68SDaniel Jeong static int lm3630a_intr_config(struct lm3630a_chip *pchip)
14928e64a68SDaniel Jeong {
15028e64a68SDaniel Jeong 	int rval;
15128e64a68SDaniel Jeong 
15228e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_INT_EN, 0x87);
15328e64a68SDaniel Jeong 	if (rval < 0)
15428e64a68SDaniel Jeong 		return rval;
15528e64a68SDaniel Jeong 
15628e64a68SDaniel Jeong 	INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func);
15728e64a68SDaniel Jeong 	pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd");
15828e64a68SDaniel Jeong 	if (!pchip->irqthread) {
15928e64a68SDaniel Jeong 		dev_err(pchip->dev, "create irq thread fail\n");
16028e64a68SDaniel Jeong 		return -ENOMEM;
16128e64a68SDaniel Jeong 	}
16228e64a68SDaniel Jeong 	if (request_threaded_irq
16328e64a68SDaniel Jeong 	    (pchip->irq, NULL, lm3630a_isr_func,
16428e64a68SDaniel Jeong 	     IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) {
16528e64a68SDaniel Jeong 		dev_err(pchip->dev, "request threaded irq fail\n");
166bcd5b416SWei Yongjun 		destroy_workqueue(pchip->irqthread);
16728e64a68SDaniel Jeong 		return -ENOMEM;
16828e64a68SDaniel Jeong 	}
16928e64a68SDaniel Jeong 	return rval;
17028e64a68SDaniel Jeong }
17128e64a68SDaniel Jeong 
17228e64a68SDaniel Jeong static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
17328e64a68SDaniel Jeong {
1744ff66efdSBoris BREZILLON 	unsigned int period = pchip->pdata->pwm_period;
17528e64a68SDaniel Jeong 	unsigned int duty = br * period / br_max;
17628e64a68SDaniel Jeong 
17728e64a68SDaniel Jeong 	pwm_config(pchip->pwmd, duty, period);
17828e64a68SDaniel Jeong 	if (duty)
17928e64a68SDaniel Jeong 		pwm_enable(pchip->pwmd);
18028e64a68SDaniel Jeong 	else
18128e64a68SDaniel Jeong 		pwm_disable(pchip->pwmd);
18228e64a68SDaniel Jeong }
18328e64a68SDaniel Jeong 
18428e64a68SDaniel Jeong /* update and get brightness */
18528e64a68SDaniel Jeong static int lm3630a_bank_a_update_status(struct backlight_device *bl)
18628e64a68SDaniel Jeong {
18728e64a68SDaniel Jeong 	int ret;
18828e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
18928e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
19028e64a68SDaniel Jeong 
19128e64a68SDaniel Jeong 	/* pwm control */
19228e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
19328e64a68SDaniel Jeong 		lm3630a_pwm_ctrl(pchip, bl->props.brightness,
19428e64a68SDaniel Jeong 				 bl->props.max_brightness);
19528e64a68SDaniel Jeong 		return bl->props.brightness;
19628e64a68SDaniel Jeong 	}
19728e64a68SDaniel Jeong 
19828e64a68SDaniel Jeong 	/* disable sleep */
19928e64a68SDaniel Jeong 	ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
20028e64a68SDaniel Jeong 	if (ret < 0)
20128e64a68SDaniel Jeong 		goto out_i2c_err;
20228e64a68SDaniel Jeong 	usleep_range(1000, 2000);
20328e64a68SDaniel Jeong 	/* minimum brightness is 0x04 */
20428e64a68SDaniel Jeong 	ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness);
20528e64a68SDaniel Jeong 	if (bl->props.brightness < 0x4)
20628e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0);
20728e64a68SDaniel Jeong 	else
20828e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL,
20928e64a68SDaniel Jeong 				      LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE);
21028e64a68SDaniel Jeong 	if (ret < 0)
21128e64a68SDaniel Jeong 		goto out_i2c_err;
212d3f48ec0SBrian Masney 	return 0;
21328e64a68SDaniel Jeong 
21428e64a68SDaniel Jeong out_i2c_err:
21528e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access\n");
21628e64a68SDaniel Jeong 	return bl->props.brightness;
21728e64a68SDaniel Jeong }
21828e64a68SDaniel Jeong 
21928e64a68SDaniel Jeong static int lm3630a_bank_a_get_brightness(struct backlight_device *bl)
22028e64a68SDaniel Jeong {
22128e64a68SDaniel Jeong 	int brightness, rval;
22228e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
22328e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
22428e64a68SDaniel Jeong 
22528e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
22628e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
22728e64a68SDaniel Jeong 		if (rval < 0)
22828e64a68SDaniel Jeong 			goto out_i2c_err;
22928e64a68SDaniel Jeong 		brightness = (rval & 0x01) << 8;
23028e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
23128e64a68SDaniel Jeong 		if (rval < 0)
23228e64a68SDaniel Jeong 			goto out_i2c_err;
23328e64a68SDaniel Jeong 		brightness |= rval;
23428e64a68SDaniel Jeong 		goto out;
23528e64a68SDaniel Jeong 	}
23628e64a68SDaniel Jeong 
23728e64a68SDaniel Jeong 	/* disable sleep */
23828e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
23928e64a68SDaniel Jeong 	if (rval < 0)
24028e64a68SDaniel Jeong 		goto out_i2c_err;
24128e64a68SDaniel Jeong 	usleep_range(1000, 2000);
24228e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_BRT_A);
24328e64a68SDaniel Jeong 	if (rval < 0)
24428e64a68SDaniel Jeong 		goto out_i2c_err;
24528e64a68SDaniel Jeong 	brightness = rval;
24628e64a68SDaniel Jeong 
24728e64a68SDaniel Jeong out:
24828e64a68SDaniel Jeong 	bl->props.brightness = brightness;
24928e64a68SDaniel Jeong 	return bl->props.brightness;
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 */
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;
26728e64a68SDaniel Jeong 
26828e64a68SDaniel Jeong 	/* pwm control */
26928e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
27028e64a68SDaniel Jeong 		lm3630a_pwm_ctrl(pchip, bl->props.brightness,
27128e64a68SDaniel Jeong 				 bl->props.max_brightness);
27228e64a68SDaniel Jeong 		return bl->props.brightness;
27328e64a68SDaniel Jeong 	}
27428e64a68SDaniel Jeong 
27528e64a68SDaniel Jeong 	/* disable sleep */
27628e64a68SDaniel Jeong 	ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
27728e64a68SDaniel Jeong 	if (ret < 0)
27828e64a68SDaniel Jeong 		goto out_i2c_err;
27928e64a68SDaniel Jeong 	usleep_range(1000, 2000);
28028e64a68SDaniel Jeong 	/* minimum brightness is 0x04 */
28128e64a68SDaniel Jeong 	ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness);
28228e64a68SDaniel Jeong 	if (bl->props.brightness < 0x4)
28328e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0);
28428e64a68SDaniel Jeong 	else
28528e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL,
28628e64a68SDaniel Jeong 				      LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE);
28728e64a68SDaniel Jeong 	if (ret < 0)
28828e64a68SDaniel Jeong 		goto out_i2c_err;
289d3f48ec0SBrian Masney 	return 0;
29028e64a68SDaniel Jeong 
29128e64a68SDaniel Jeong out_i2c_err:
29228e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access REG_CTRL\n");
29328e64a68SDaniel Jeong 	return bl->props.brightness;
29428e64a68SDaniel Jeong }
29528e64a68SDaniel Jeong 
29628e64a68SDaniel Jeong static int lm3630a_bank_b_get_brightness(struct backlight_device *bl)
29728e64a68SDaniel Jeong {
29828e64a68SDaniel Jeong 	int brightness, rval;
29928e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
30028e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
30128e64a68SDaniel Jeong 
30228e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
30328e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
30428e64a68SDaniel Jeong 		if (rval < 0)
30528e64a68SDaniel Jeong 			goto out_i2c_err;
30628e64a68SDaniel Jeong 		brightness = (rval & 0x01) << 8;
30728e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
30828e64a68SDaniel Jeong 		if (rval < 0)
30928e64a68SDaniel Jeong 			goto out_i2c_err;
31028e64a68SDaniel Jeong 		brightness |= rval;
31128e64a68SDaniel Jeong 		goto out;
31228e64a68SDaniel Jeong 	}
31328e64a68SDaniel Jeong 
31428e64a68SDaniel Jeong 	/* disable sleep */
31528e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
31628e64a68SDaniel Jeong 	if (rval < 0)
31728e64a68SDaniel Jeong 		goto out_i2c_err;
31828e64a68SDaniel Jeong 	usleep_range(1000, 2000);
31928e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_BRT_B);
32028e64a68SDaniel Jeong 	if (rval < 0)
32128e64a68SDaniel Jeong 		goto out_i2c_err;
32228e64a68SDaniel Jeong 	brightness = rval;
32328e64a68SDaniel Jeong 
32428e64a68SDaniel Jeong out:
32528e64a68SDaniel Jeong 	bl->props.brightness = brightness;
32628e64a68SDaniel Jeong 	return bl->props.brightness;
32728e64a68SDaniel Jeong out_i2c_err:
32828e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access register\n");
32928e64a68SDaniel Jeong 	return 0;
33028e64a68SDaniel Jeong }
33128e64a68SDaniel Jeong 
33228e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_b_ops = {
33328e64a68SDaniel Jeong 	.options = BL_CORE_SUSPENDRESUME,
33428e64a68SDaniel Jeong 	.update_status = lm3630a_bank_b_update_status,
33528e64a68SDaniel Jeong 	.get_brightness = lm3630a_bank_b_get_brightness,
33628e64a68SDaniel Jeong };
33728e64a68SDaniel Jeong 
33828e64a68SDaniel Jeong static int lm3630a_backlight_register(struct lm3630a_chip *pchip)
33928e64a68SDaniel Jeong {
34028e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata = pchip->pdata;
341*8fbce8efSBrian Masney 	struct backlight_properties props;
342*8fbce8efSBrian Masney 	const char *label;
34328e64a68SDaniel Jeong 
34428e64a68SDaniel Jeong 	props.type = BACKLIGHT_RAW;
34528e64a68SDaniel Jeong 	if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) {
34628e64a68SDaniel Jeong 		props.brightness = pdata->leda_init_brt;
34728e64a68SDaniel Jeong 		props.max_brightness = pdata->leda_max_brt;
348*8fbce8efSBrian Masney 		label = pdata->leda_label ? pdata->leda_label : "lm3630a_leda";
34928e64a68SDaniel Jeong 		pchip->bleda =
350*8fbce8efSBrian Masney 		    devm_backlight_device_register(pchip->dev, label,
35128e64a68SDaniel Jeong 						   pchip->dev, pchip,
35228e64a68SDaniel Jeong 						   &lm3630a_bank_a_ops, &props);
35328e64a68SDaniel Jeong 		if (IS_ERR(pchip->bleda))
35428e64a68SDaniel Jeong 			return PTR_ERR(pchip->bleda);
35528e64a68SDaniel Jeong 	}
35628e64a68SDaniel Jeong 
35728e64a68SDaniel Jeong 	if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) &&
35828e64a68SDaniel Jeong 	    (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) {
35928e64a68SDaniel Jeong 		props.brightness = pdata->ledb_init_brt;
36028e64a68SDaniel Jeong 		props.max_brightness = pdata->ledb_max_brt;
361*8fbce8efSBrian Masney 		label = pdata->ledb_label ? pdata->ledb_label : "lm3630a_ledb";
36228e64a68SDaniel Jeong 		pchip->bledb =
363*8fbce8efSBrian Masney 		    devm_backlight_device_register(pchip->dev, label,
36428e64a68SDaniel Jeong 						   pchip->dev, pchip,
36528e64a68SDaniel Jeong 						   &lm3630a_bank_b_ops, &props);
36628e64a68SDaniel Jeong 		if (IS_ERR(pchip->bledb))
36728e64a68SDaniel Jeong 			return PTR_ERR(pchip->bledb);
36828e64a68SDaniel Jeong 	}
36928e64a68SDaniel Jeong 	return 0;
37028e64a68SDaniel Jeong }
37128e64a68SDaniel Jeong 
37228e64a68SDaniel Jeong static const struct regmap_config lm3630a_regmap = {
37328e64a68SDaniel Jeong 	.reg_bits = 8,
37428e64a68SDaniel Jeong 	.val_bits = 8,
37528e64a68SDaniel Jeong 	.max_register = REG_MAX,
37628e64a68SDaniel Jeong };
37728e64a68SDaniel Jeong 
378*8fbce8efSBrian Masney static int lm3630a_parse_led_sources(struct fwnode_handle *node,
379*8fbce8efSBrian Masney 				     int default_led_sources)
380*8fbce8efSBrian Masney {
381*8fbce8efSBrian Masney 	u32 sources[LM3630A_NUM_SINKS];
382*8fbce8efSBrian Masney 	int ret, num_sources, i;
383*8fbce8efSBrian Masney 
384*8fbce8efSBrian Masney 	num_sources = fwnode_property_read_u32_array(node, "led-sources", NULL,
385*8fbce8efSBrian Masney 						     0);
386*8fbce8efSBrian Masney 	if (num_sources < 0)
387*8fbce8efSBrian Masney 		return default_led_sources;
388*8fbce8efSBrian Masney 	else if (num_sources > ARRAY_SIZE(sources))
389*8fbce8efSBrian Masney 		return -EINVAL;
390*8fbce8efSBrian Masney 
391*8fbce8efSBrian Masney 	ret = fwnode_property_read_u32_array(node, "led-sources", sources,
392*8fbce8efSBrian Masney 					     num_sources);
393*8fbce8efSBrian Masney 	if (ret)
394*8fbce8efSBrian Masney 		return ret;
395*8fbce8efSBrian Masney 
396*8fbce8efSBrian Masney 	for (i = 0; i < num_sources; i++) {
397*8fbce8efSBrian Masney 		if (sources[i] < LM3630A_SINK_0 || sources[i] > LM3630A_SINK_1)
398*8fbce8efSBrian Masney 			return -EINVAL;
399*8fbce8efSBrian Masney 
400*8fbce8efSBrian Masney 		ret |= BIT(sources[i]);
401*8fbce8efSBrian Masney 	}
402*8fbce8efSBrian Masney 
403*8fbce8efSBrian Masney 	return ret;
404*8fbce8efSBrian Masney }
405*8fbce8efSBrian Masney 
406*8fbce8efSBrian Masney static int lm3630a_parse_bank(struct lm3630a_platform_data *pdata,
407*8fbce8efSBrian Masney 			      struct fwnode_handle *node, int *seen_led_sources)
408*8fbce8efSBrian Masney {
409*8fbce8efSBrian Masney 	int led_sources, ret;
410*8fbce8efSBrian Masney 	const char *label;
411*8fbce8efSBrian Masney 	u32 bank, val;
412*8fbce8efSBrian Masney 	bool linear;
413*8fbce8efSBrian Masney 
414*8fbce8efSBrian Masney 	ret = fwnode_property_read_u32(node, "reg", &bank);
415*8fbce8efSBrian Masney 	if (ret)
416*8fbce8efSBrian Masney 		return ret;
417*8fbce8efSBrian Masney 
418*8fbce8efSBrian Masney 	if (bank < LM3630A_BANK_0 || bank > LM3630A_BANK_1)
419*8fbce8efSBrian Masney 		return -EINVAL;
420*8fbce8efSBrian Masney 
421*8fbce8efSBrian Masney 	led_sources = lm3630a_parse_led_sources(node, BIT(bank));
422*8fbce8efSBrian Masney 	if (led_sources < 0)
423*8fbce8efSBrian Masney 		return led_sources;
424*8fbce8efSBrian Masney 
425*8fbce8efSBrian Masney 	if (*seen_led_sources & led_sources)
426*8fbce8efSBrian Masney 		return -EINVAL;
427*8fbce8efSBrian Masney 
428*8fbce8efSBrian Masney 	*seen_led_sources |= led_sources;
429*8fbce8efSBrian Masney 
430*8fbce8efSBrian Masney 	linear = fwnode_property_read_bool(node,
431*8fbce8efSBrian Masney 					   "ti,linear-mapping-mode");
432*8fbce8efSBrian Masney 	if (bank) {
433*8fbce8efSBrian Masney 		if (led_sources & BIT(LM3630A_SINK_0) ||
434*8fbce8efSBrian Masney 		    !(led_sources & BIT(LM3630A_SINK_1)))
435*8fbce8efSBrian Masney 			return -EINVAL;
436*8fbce8efSBrian Masney 
437*8fbce8efSBrian Masney 		pdata->ledb_ctrl = linear ?
438*8fbce8efSBrian Masney 			LM3630A_LEDB_ENABLE_LINEAR :
439*8fbce8efSBrian Masney 			LM3630A_LEDB_ENABLE;
440*8fbce8efSBrian Masney 	} else {
441*8fbce8efSBrian Masney 		if (!(led_sources & BIT(LM3630A_SINK_0)))
442*8fbce8efSBrian Masney 			return -EINVAL;
443*8fbce8efSBrian Masney 
444*8fbce8efSBrian Masney 		pdata->leda_ctrl = linear ?
445*8fbce8efSBrian Masney 			LM3630A_LEDA_ENABLE_LINEAR :
446*8fbce8efSBrian Masney 			LM3630A_LEDA_ENABLE;
447*8fbce8efSBrian Masney 
448*8fbce8efSBrian Masney 		if (led_sources & BIT(LM3630A_SINK_1))
449*8fbce8efSBrian Masney 			pdata->ledb_ctrl = LM3630A_LEDB_ON_A;
450*8fbce8efSBrian Masney 	}
451*8fbce8efSBrian Masney 
452*8fbce8efSBrian Masney 	ret = fwnode_property_read_string(node, "label", &label);
453*8fbce8efSBrian Masney 	if (!ret) {
454*8fbce8efSBrian Masney 		if (bank)
455*8fbce8efSBrian Masney 			pdata->ledb_label = label;
456*8fbce8efSBrian Masney 		else
457*8fbce8efSBrian Masney 			pdata->leda_label = label;
458*8fbce8efSBrian Masney 	}
459*8fbce8efSBrian Masney 
460*8fbce8efSBrian Masney 	ret = fwnode_property_read_u32(node, "default-brightness",
461*8fbce8efSBrian Masney 				       &val);
462*8fbce8efSBrian Masney 	if (!ret) {
463*8fbce8efSBrian Masney 		if (bank)
464*8fbce8efSBrian Masney 			pdata->ledb_init_brt = val;
465*8fbce8efSBrian Masney 		else
466*8fbce8efSBrian Masney 			pdata->leda_init_brt = val;
467*8fbce8efSBrian Masney 	}
468*8fbce8efSBrian Masney 
469*8fbce8efSBrian Masney 	ret = fwnode_property_read_u32(node, "max-brightness", &val);
470*8fbce8efSBrian Masney 	if (!ret) {
471*8fbce8efSBrian Masney 		if (bank)
472*8fbce8efSBrian Masney 			pdata->ledb_max_brt = val;
473*8fbce8efSBrian Masney 		else
474*8fbce8efSBrian Masney 			pdata->leda_max_brt = val;
475*8fbce8efSBrian Masney 	}
476*8fbce8efSBrian Masney 
477*8fbce8efSBrian Masney 	return 0;
478*8fbce8efSBrian Masney }
479*8fbce8efSBrian Masney 
480*8fbce8efSBrian Masney static int lm3630a_parse_node(struct lm3630a_chip *pchip,
481*8fbce8efSBrian Masney 			      struct lm3630a_platform_data *pdata)
482*8fbce8efSBrian Masney {
483*8fbce8efSBrian Masney 	int ret = -ENODEV, seen_led_sources = 0;
484*8fbce8efSBrian Masney 	struct fwnode_handle *node;
485*8fbce8efSBrian Masney 
486*8fbce8efSBrian Masney 	device_for_each_child_node(pchip->dev, node) {
487*8fbce8efSBrian Masney 		ret = lm3630a_parse_bank(pdata, node, &seen_led_sources);
488*8fbce8efSBrian Masney 		if (ret)
489*8fbce8efSBrian Masney 			return ret;
490*8fbce8efSBrian Masney 	}
491*8fbce8efSBrian Masney 
492*8fbce8efSBrian Masney 	return ret;
493*8fbce8efSBrian Masney }
494*8fbce8efSBrian Masney 
49528e64a68SDaniel Jeong static int lm3630a_probe(struct i2c_client *client,
49628e64a68SDaniel Jeong 			 const struct i2c_device_id *id)
49728e64a68SDaniel Jeong {
498c512794cSJingoo Han 	struct lm3630a_platform_data *pdata = dev_get_platdata(&client->dev);
49928e64a68SDaniel Jeong 	struct lm3630a_chip *pchip;
50028e64a68SDaniel Jeong 	int rval;
50128e64a68SDaniel Jeong 
50228e64a68SDaniel Jeong 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
50328e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : i2c functionality check\n");
50428e64a68SDaniel Jeong 		return -EOPNOTSUPP;
50528e64a68SDaniel Jeong 	}
50628e64a68SDaniel Jeong 
50728e64a68SDaniel Jeong 	pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip),
50828e64a68SDaniel Jeong 			     GFP_KERNEL);
50928e64a68SDaniel Jeong 	if (!pchip)
51028e64a68SDaniel Jeong 		return -ENOMEM;
51128e64a68SDaniel Jeong 	pchip->dev = &client->dev;
51228e64a68SDaniel Jeong 
51328e64a68SDaniel Jeong 	pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap);
51428e64a68SDaniel Jeong 	if (IS_ERR(pchip->regmap)) {
51528e64a68SDaniel Jeong 		rval = PTR_ERR(pchip->regmap);
51628e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval);
51728e64a68SDaniel Jeong 		return rval;
51828e64a68SDaniel Jeong 	}
51928e64a68SDaniel Jeong 
52028e64a68SDaniel Jeong 	i2c_set_clientdata(client, pchip);
52128e64a68SDaniel Jeong 	if (pdata == NULL) {
522e19493c1SDan Carpenter 		pdata = devm_kzalloc(pchip->dev,
523e19493c1SDan Carpenter 				     sizeof(struct lm3630a_platform_data),
52428e64a68SDaniel Jeong 				     GFP_KERNEL);
525e19493c1SDan Carpenter 		if (pdata == NULL)
52628e64a68SDaniel Jeong 			return -ENOMEM;
527*8fbce8efSBrian Masney 
52828e64a68SDaniel Jeong 		/* default values */
529e19493c1SDan Carpenter 		pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS;
530e19493c1SDan Carpenter 		pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS;
531e19493c1SDan Carpenter 		pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS;
532e19493c1SDan Carpenter 		pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS;
533*8fbce8efSBrian Masney 
534*8fbce8efSBrian Masney 		rval = lm3630a_parse_node(pchip, pdata);
535*8fbce8efSBrian Masney 		if (rval) {
536*8fbce8efSBrian Masney 			dev_err(&client->dev, "fail : parse node\n");
537*8fbce8efSBrian Masney 			return rval;
538*8fbce8efSBrian Masney 		}
53928e64a68SDaniel Jeong 	}
540e19493c1SDan Carpenter 	pchip->pdata = pdata;
541e19493c1SDan Carpenter 
54228e64a68SDaniel Jeong 	/* chip initialize */
54328e64a68SDaniel Jeong 	rval = lm3630a_chip_init(pchip);
54428e64a68SDaniel Jeong 	if (rval < 0) {
54528e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : init chip\n");
54628e64a68SDaniel Jeong 		return rval;
54728e64a68SDaniel Jeong 	}
54828e64a68SDaniel Jeong 	/* backlight register */
54928e64a68SDaniel Jeong 	rval = lm3630a_backlight_register(pchip);
55028e64a68SDaniel Jeong 	if (rval < 0) {
55128e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : backlight register.\n");
55228e64a68SDaniel Jeong 		return rval;
55328e64a68SDaniel Jeong 	}
55428e64a68SDaniel Jeong 	/* pwm */
55528e64a68SDaniel Jeong 	if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) {
55628e64a68SDaniel Jeong 		pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm");
55728e64a68SDaniel Jeong 		if (IS_ERR(pchip->pwmd)) {
55828e64a68SDaniel Jeong 			dev_err(&client->dev, "fail : get pwm device\n");
55928e64a68SDaniel Jeong 			return PTR_ERR(pchip->pwmd);
56028e64a68SDaniel Jeong 		}
5617ff666bcSBoris Brezillon 
5627ff666bcSBoris Brezillon 		/*
5637ff666bcSBoris Brezillon 		 * FIXME: pwm_apply_args() should be removed when switching to
5647ff666bcSBoris Brezillon 		 * the atomic PWM API.
5657ff666bcSBoris Brezillon 		 */
5667ff666bcSBoris Brezillon 		pwm_apply_args(pchip->pwmd);
56728e64a68SDaniel Jeong 	}
56828e64a68SDaniel Jeong 
56928e64a68SDaniel Jeong 	/* interrupt enable  : irq 0 is not allowed */
57028e64a68SDaniel Jeong 	pchip->irq = client->irq;
57128e64a68SDaniel Jeong 	if (pchip->irq) {
57228e64a68SDaniel Jeong 		rval = lm3630a_intr_config(pchip);
57328e64a68SDaniel Jeong 		if (rval < 0)
57428e64a68SDaniel Jeong 			return rval;
57528e64a68SDaniel Jeong 	}
57628e64a68SDaniel Jeong 	dev_info(&client->dev, "LM3630A backlight register OK.\n");
57728e64a68SDaniel Jeong 	return 0;
57828e64a68SDaniel Jeong }
57928e64a68SDaniel Jeong 
58028e64a68SDaniel Jeong static int lm3630a_remove(struct i2c_client *client)
58128e64a68SDaniel Jeong {
58228e64a68SDaniel Jeong 	int rval;
58328e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = i2c_get_clientdata(client);
58428e64a68SDaniel Jeong 
58528e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_BRT_A, 0);
58628e64a68SDaniel Jeong 	if (rval < 0)
58728e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
58828e64a68SDaniel Jeong 
58928e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_BRT_B, 0);
59028e64a68SDaniel Jeong 	if (rval < 0)
59128e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
59228e64a68SDaniel Jeong 
59328e64a68SDaniel Jeong 	if (pchip->irq) {
59428e64a68SDaniel Jeong 		free_irq(pchip->irq, pchip);
59528e64a68SDaniel Jeong 		flush_workqueue(pchip->irqthread);
59628e64a68SDaniel Jeong 		destroy_workqueue(pchip->irqthread);
59728e64a68SDaniel Jeong 	}
59828e64a68SDaniel Jeong 	return 0;
59928e64a68SDaniel Jeong }
60028e64a68SDaniel Jeong 
60128e64a68SDaniel Jeong static const struct i2c_device_id lm3630a_id[] = {
60228e64a68SDaniel Jeong 	{LM3630A_NAME, 0},
60328e64a68SDaniel Jeong 	{}
60428e64a68SDaniel Jeong };
60528e64a68SDaniel Jeong 
606*8fbce8efSBrian Masney static const struct of_device_id lm3630a_match_table[] = {
607*8fbce8efSBrian Masney 	{ .compatible = "ti,lm3630a", },
608*8fbce8efSBrian Masney 	{ },
609*8fbce8efSBrian Masney };
610*8fbce8efSBrian Masney 
61128e64a68SDaniel Jeong MODULE_DEVICE_TABLE(i2c, lm3630a_id);
61228e64a68SDaniel Jeong 
61328e64a68SDaniel Jeong static struct i2c_driver lm3630a_i2c_driver = {
61428e64a68SDaniel Jeong 	.driver = {
61528e64a68SDaniel Jeong 		   .name = LM3630A_NAME,
616*8fbce8efSBrian Masney 		   .of_match_table = lm3630a_match_table,
61728e64a68SDaniel Jeong 		   },
61828e64a68SDaniel Jeong 	.probe = lm3630a_probe,
61928e64a68SDaniel Jeong 	.remove = lm3630a_remove,
62028e64a68SDaniel Jeong 	.id_table = lm3630a_id,
62128e64a68SDaniel Jeong };
62228e64a68SDaniel Jeong 
62328e64a68SDaniel Jeong module_i2c_driver(lm3630a_i2c_driver);
62428e64a68SDaniel Jeong 
62528e64a68SDaniel Jeong MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A");
62628e64a68SDaniel Jeong MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
62728e64a68SDaniel Jeong MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>");
62828e64a68SDaniel Jeong MODULE_LICENSE("GPL v2");
629