xref: /openbmc/linux/drivers/video/backlight/lm3630a_bl.c (revision 2a0c316bf3ccd910dc58ec62465ff620c664b5e4)
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
3428e64a68SDaniel Jeong #define REG_MAX		0x1F
3528e64a68SDaniel Jeong 
3628e64a68SDaniel Jeong #define INT_DEBOUNCE_MSEC	10
3728e64a68SDaniel Jeong struct lm3630a_chip {
3828e64a68SDaniel Jeong 	struct device *dev;
3928e64a68SDaniel Jeong 	struct delayed_work work;
4028e64a68SDaniel Jeong 
4128e64a68SDaniel Jeong 	int irq;
4228e64a68SDaniel Jeong 	struct workqueue_struct *irqthread;
4328e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata;
4428e64a68SDaniel Jeong 	struct backlight_device *bleda;
4528e64a68SDaniel Jeong 	struct backlight_device *bledb;
4628e64a68SDaniel Jeong 	struct regmap *regmap;
4728e64a68SDaniel Jeong 	struct pwm_device *pwmd;
4828e64a68SDaniel Jeong };
4928e64a68SDaniel Jeong 
5028e64a68SDaniel Jeong /* i2c access */
5128e64a68SDaniel Jeong static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg)
5228e64a68SDaniel Jeong {
5328e64a68SDaniel Jeong 	int rval;
5428e64a68SDaniel Jeong 	unsigned int reg_val;
5528e64a68SDaniel Jeong 
5628e64a68SDaniel Jeong 	rval = regmap_read(pchip->regmap, reg, &reg_val);
5728e64a68SDaniel Jeong 	if (rval < 0)
5828e64a68SDaniel Jeong 		return rval;
5928e64a68SDaniel Jeong 	return reg_val & 0xFF;
6028e64a68SDaniel Jeong }
6128e64a68SDaniel Jeong 
6228e64a68SDaniel Jeong static int lm3630a_write(struct lm3630a_chip *pchip,
6328e64a68SDaniel Jeong 			 unsigned int reg, unsigned int data)
6428e64a68SDaniel Jeong {
6528e64a68SDaniel Jeong 	return regmap_write(pchip->regmap, reg, data);
6628e64a68SDaniel Jeong }
6728e64a68SDaniel Jeong 
6828e64a68SDaniel Jeong static int lm3630a_update(struct lm3630a_chip *pchip,
6928e64a68SDaniel Jeong 			  unsigned int reg, unsigned int mask,
7028e64a68SDaniel Jeong 			  unsigned int data)
7128e64a68SDaniel Jeong {
7228e64a68SDaniel Jeong 	return regmap_update_bits(pchip->regmap, reg, mask, data);
7328e64a68SDaniel Jeong }
7428e64a68SDaniel Jeong 
7528e64a68SDaniel Jeong /* initialize chip */
7628e64a68SDaniel Jeong static int lm3630a_chip_init(struct lm3630a_chip *pchip)
7728e64a68SDaniel Jeong {
7828e64a68SDaniel Jeong 	int rval;
7928e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata = pchip->pdata;
8028e64a68SDaniel Jeong 
8128e64a68SDaniel Jeong 	usleep_range(1000, 2000);
8228e64a68SDaniel Jeong 	/* set Filter Strength Register */
8328e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, 0x50, 0x03);
8428e64a68SDaniel Jeong 	/* set Cofig. register */
8528e64a68SDaniel Jeong 	rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl);
8628e64a68SDaniel Jeong 	/* set boost control */
8728e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BOOST, 0x38);
8828e64a68SDaniel Jeong 	/* set current A */
8928e64a68SDaniel Jeong 	rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F);
9028e64a68SDaniel Jeong 	/* set current B */
9128e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_I_B, 0x1F);
9228e64a68SDaniel Jeong 	/* set control */
9328e64a68SDaniel Jeong 	rval |=
9428e64a68SDaniel Jeong 	    lm3630a_write(pchip, REG_CTRL, pdata->leda_ctrl | pdata->ledb_ctrl);
9528e64a68SDaniel Jeong 	usleep_range(1000, 2000);
9628e64a68SDaniel Jeong 	/* set brightness A and B */
9728e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt);
9828e64a68SDaniel Jeong 	rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt);
9928e64a68SDaniel Jeong 
10028e64a68SDaniel Jeong 	if (rval < 0)
10128e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
10228e64a68SDaniel Jeong 	return rval;
10328e64a68SDaniel Jeong }
10428e64a68SDaniel Jeong 
10528e64a68SDaniel Jeong /* interrupt handling */
10628e64a68SDaniel Jeong static void lm3630a_delayed_func(struct work_struct *work)
10728e64a68SDaniel Jeong {
108*2a0c316bSDan Carpenter 	int rval;
10928e64a68SDaniel Jeong 	struct lm3630a_chip *pchip;
11028e64a68SDaniel Jeong 
11128e64a68SDaniel Jeong 	pchip = container_of(work, struct lm3630a_chip, work.work);
11228e64a68SDaniel Jeong 
11328e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_INT_STATUS);
11428e64a68SDaniel Jeong 	if (rval < 0) {
11528e64a68SDaniel Jeong 		dev_err(pchip->dev,
11628e64a68SDaniel Jeong 			"i2c failed to access REG_INT_STATUS Register\n");
11728e64a68SDaniel Jeong 		return;
11828e64a68SDaniel Jeong 	}
11928e64a68SDaniel Jeong 
12028e64a68SDaniel Jeong 	dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval);
12128e64a68SDaniel Jeong }
12228e64a68SDaniel Jeong 
12328e64a68SDaniel Jeong static irqreturn_t lm3630a_isr_func(int irq, void *chip)
12428e64a68SDaniel Jeong {
12528e64a68SDaniel Jeong 	int rval;
12628e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = chip;
12728e64a68SDaniel Jeong 	unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC);
12828e64a68SDaniel Jeong 
12928e64a68SDaniel Jeong 	queue_delayed_work(pchip->irqthread, &pchip->work, delay);
13028e64a68SDaniel Jeong 
13128e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
13228e64a68SDaniel Jeong 	if (rval < 0) {
13328e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
13428e64a68SDaniel Jeong 		return IRQ_NONE;
13528e64a68SDaniel Jeong 	}
13628e64a68SDaniel Jeong 	return IRQ_HANDLED;
13728e64a68SDaniel Jeong }
13828e64a68SDaniel Jeong 
13928e64a68SDaniel Jeong static int lm3630a_intr_config(struct lm3630a_chip *pchip)
14028e64a68SDaniel Jeong {
14128e64a68SDaniel Jeong 	int rval;
14228e64a68SDaniel Jeong 
14328e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_INT_EN, 0x87);
14428e64a68SDaniel Jeong 	if (rval < 0)
14528e64a68SDaniel Jeong 		return rval;
14628e64a68SDaniel Jeong 
14728e64a68SDaniel Jeong 	INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func);
14828e64a68SDaniel Jeong 	pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd");
14928e64a68SDaniel Jeong 	if (!pchip->irqthread) {
15028e64a68SDaniel Jeong 		dev_err(pchip->dev, "create irq thread fail\n");
15128e64a68SDaniel Jeong 		return -ENOMEM;
15228e64a68SDaniel Jeong 	}
15328e64a68SDaniel Jeong 	if (request_threaded_irq
15428e64a68SDaniel Jeong 	    (pchip->irq, NULL, lm3630a_isr_func,
15528e64a68SDaniel Jeong 	     IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) {
15628e64a68SDaniel Jeong 		dev_err(pchip->dev, "request threaded irq fail\n");
15728e64a68SDaniel Jeong 		return -ENOMEM;
15828e64a68SDaniel Jeong 	}
15928e64a68SDaniel Jeong 	return rval;
16028e64a68SDaniel Jeong }
16128e64a68SDaniel Jeong 
16228e64a68SDaniel Jeong static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
16328e64a68SDaniel Jeong {
16428e64a68SDaniel Jeong 	unsigned int period = pwm_get_period(pchip->pwmd);
16528e64a68SDaniel Jeong 	unsigned int duty = br * period / br_max;
16628e64a68SDaniel Jeong 
16728e64a68SDaniel Jeong 	pwm_config(pchip->pwmd, duty, period);
16828e64a68SDaniel Jeong 	if (duty)
16928e64a68SDaniel Jeong 		pwm_enable(pchip->pwmd);
17028e64a68SDaniel Jeong 	else
17128e64a68SDaniel Jeong 		pwm_disable(pchip->pwmd);
17228e64a68SDaniel Jeong }
17328e64a68SDaniel Jeong 
17428e64a68SDaniel Jeong /* update and get brightness */
17528e64a68SDaniel Jeong static int lm3630a_bank_a_update_status(struct backlight_device *bl)
17628e64a68SDaniel Jeong {
17728e64a68SDaniel Jeong 	int ret;
17828e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
17928e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
18028e64a68SDaniel Jeong 
18128e64a68SDaniel Jeong 	/* pwm control */
18228e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
18328e64a68SDaniel Jeong 		lm3630a_pwm_ctrl(pchip, bl->props.brightness,
18428e64a68SDaniel Jeong 				 bl->props.max_brightness);
18528e64a68SDaniel Jeong 		return bl->props.brightness;
18628e64a68SDaniel Jeong 	}
18728e64a68SDaniel Jeong 
18828e64a68SDaniel Jeong 	/* disable sleep */
18928e64a68SDaniel Jeong 	ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
19028e64a68SDaniel Jeong 	if (ret < 0)
19128e64a68SDaniel Jeong 		goto out_i2c_err;
19228e64a68SDaniel Jeong 	usleep_range(1000, 2000);
19328e64a68SDaniel Jeong 	/* minimum brightness is 0x04 */
19428e64a68SDaniel Jeong 	ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness);
19528e64a68SDaniel Jeong 	if (bl->props.brightness < 0x4)
19628e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0);
19728e64a68SDaniel Jeong 	else
19828e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL,
19928e64a68SDaniel Jeong 				      LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE);
20028e64a68SDaniel Jeong 	if (ret < 0)
20128e64a68SDaniel Jeong 		goto out_i2c_err;
20228e64a68SDaniel Jeong 	return bl->props.brightness;
20328e64a68SDaniel Jeong 
20428e64a68SDaniel Jeong out_i2c_err:
20528e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access\n");
20628e64a68SDaniel Jeong 	return bl->props.brightness;
20728e64a68SDaniel Jeong }
20828e64a68SDaniel Jeong 
20928e64a68SDaniel Jeong static int lm3630a_bank_a_get_brightness(struct backlight_device *bl)
21028e64a68SDaniel Jeong {
21128e64a68SDaniel Jeong 	int brightness, rval;
21228e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
21328e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
21428e64a68SDaniel Jeong 
21528e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) {
21628e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
21728e64a68SDaniel Jeong 		if (rval < 0)
21828e64a68SDaniel Jeong 			goto out_i2c_err;
21928e64a68SDaniel Jeong 		brightness = (rval & 0x01) << 8;
22028e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
22128e64a68SDaniel Jeong 		if (rval < 0)
22228e64a68SDaniel Jeong 			goto out_i2c_err;
22328e64a68SDaniel Jeong 		brightness |= rval;
22428e64a68SDaniel Jeong 		goto out;
22528e64a68SDaniel Jeong 	}
22628e64a68SDaniel Jeong 
22728e64a68SDaniel Jeong 	/* disable sleep */
22828e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
22928e64a68SDaniel Jeong 	if (rval < 0)
23028e64a68SDaniel Jeong 		goto out_i2c_err;
23128e64a68SDaniel Jeong 	usleep_range(1000, 2000);
23228e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_BRT_A);
23328e64a68SDaniel Jeong 	if (rval < 0)
23428e64a68SDaniel Jeong 		goto out_i2c_err;
23528e64a68SDaniel Jeong 	brightness = rval;
23628e64a68SDaniel Jeong 
23728e64a68SDaniel Jeong out:
23828e64a68SDaniel Jeong 	bl->props.brightness = brightness;
23928e64a68SDaniel Jeong 	return bl->props.brightness;
24028e64a68SDaniel Jeong out_i2c_err:
24128e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access register\n");
24228e64a68SDaniel Jeong 	return 0;
24328e64a68SDaniel Jeong }
24428e64a68SDaniel Jeong 
24528e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_a_ops = {
24628e64a68SDaniel Jeong 	.options = BL_CORE_SUSPENDRESUME,
24728e64a68SDaniel Jeong 	.update_status = lm3630a_bank_a_update_status,
24828e64a68SDaniel Jeong 	.get_brightness = lm3630a_bank_a_get_brightness,
24928e64a68SDaniel Jeong };
25028e64a68SDaniel Jeong 
25128e64a68SDaniel Jeong /* update and get brightness */
25228e64a68SDaniel Jeong static int lm3630a_bank_b_update_status(struct backlight_device *bl)
25328e64a68SDaniel Jeong {
25428e64a68SDaniel Jeong 	int ret;
25528e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
25628e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
25728e64a68SDaniel Jeong 
25828e64a68SDaniel Jeong 	/* pwm control */
25928e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
26028e64a68SDaniel Jeong 		lm3630a_pwm_ctrl(pchip, bl->props.brightness,
26128e64a68SDaniel Jeong 				 bl->props.max_brightness);
26228e64a68SDaniel Jeong 		return bl->props.brightness;
26328e64a68SDaniel Jeong 	}
26428e64a68SDaniel Jeong 
26528e64a68SDaniel Jeong 	/* disable sleep */
26628e64a68SDaniel Jeong 	ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
26728e64a68SDaniel Jeong 	if (ret < 0)
26828e64a68SDaniel Jeong 		goto out_i2c_err;
26928e64a68SDaniel Jeong 	usleep_range(1000, 2000);
27028e64a68SDaniel Jeong 	/* minimum brightness is 0x04 */
27128e64a68SDaniel Jeong 	ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness);
27228e64a68SDaniel Jeong 	if (bl->props.brightness < 0x4)
27328e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0);
27428e64a68SDaniel Jeong 	else
27528e64a68SDaniel Jeong 		ret |= lm3630a_update(pchip, REG_CTRL,
27628e64a68SDaniel Jeong 				      LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE);
27728e64a68SDaniel Jeong 	if (ret < 0)
27828e64a68SDaniel Jeong 		goto out_i2c_err;
27928e64a68SDaniel Jeong 	return bl->props.brightness;
28028e64a68SDaniel Jeong 
28128e64a68SDaniel Jeong out_i2c_err:
28228e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access REG_CTRL\n");
28328e64a68SDaniel Jeong 	return bl->props.brightness;
28428e64a68SDaniel Jeong }
28528e64a68SDaniel Jeong 
28628e64a68SDaniel Jeong static int lm3630a_bank_b_get_brightness(struct backlight_device *bl)
28728e64a68SDaniel Jeong {
28828e64a68SDaniel Jeong 	int brightness, rval;
28928e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = bl_get_data(bl);
29028e64a68SDaniel Jeong 	enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl;
29128e64a68SDaniel Jeong 
29228e64a68SDaniel Jeong 	if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) {
29328e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTHIGH);
29428e64a68SDaniel Jeong 		if (rval < 0)
29528e64a68SDaniel Jeong 			goto out_i2c_err;
29628e64a68SDaniel Jeong 		brightness = (rval & 0x01) << 8;
29728e64a68SDaniel Jeong 		rval = lm3630a_read(pchip, REG_PWM_OUTLOW);
29828e64a68SDaniel Jeong 		if (rval < 0)
29928e64a68SDaniel Jeong 			goto out_i2c_err;
30028e64a68SDaniel Jeong 		brightness |= rval;
30128e64a68SDaniel Jeong 		goto out;
30228e64a68SDaniel Jeong 	}
30328e64a68SDaniel Jeong 
30428e64a68SDaniel Jeong 	/* disable sleep */
30528e64a68SDaniel Jeong 	rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00);
30628e64a68SDaniel Jeong 	if (rval < 0)
30728e64a68SDaniel Jeong 		goto out_i2c_err;
30828e64a68SDaniel Jeong 	usleep_range(1000, 2000);
30928e64a68SDaniel Jeong 	rval = lm3630a_read(pchip, REG_BRT_B);
31028e64a68SDaniel Jeong 	if (rval < 0)
31128e64a68SDaniel Jeong 		goto out_i2c_err;
31228e64a68SDaniel Jeong 	brightness = rval;
31328e64a68SDaniel Jeong 
31428e64a68SDaniel Jeong out:
31528e64a68SDaniel Jeong 	bl->props.brightness = brightness;
31628e64a68SDaniel Jeong 	return bl->props.brightness;
31728e64a68SDaniel Jeong out_i2c_err:
31828e64a68SDaniel Jeong 	dev_err(pchip->dev, "i2c failed to access register\n");
31928e64a68SDaniel Jeong 	return 0;
32028e64a68SDaniel Jeong }
32128e64a68SDaniel Jeong 
32228e64a68SDaniel Jeong static const struct backlight_ops lm3630a_bank_b_ops = {
32328e64a68SDaniel Jeong 	.options = BL_CORE_SUSPENDRESUME,
32428e64a68SDaniel Jeong 	.update_status = lm3630a_bank_b_update_status,
32528e64a68SDaniel Jeong 	.get_brightness = lm3630a_bank_b_get_brightness,
32628e64a68SDaniel Jeong };
32728e64a68SDaniel Jeong 
32828e64a68SDaniel Jeong static int lm3630a_backlight_register(struct lm3630a_chip *pchip)
32928e64a68SDaniel Jeong {
33028e64a68SDaniel Jeong 	struct backlight_properties props;
33128e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata = pchip->pdata;
33228e64a68SDaniel Jeong 
33328e64a68SDaniel Jeong 	props.type = BACKLIGHT_RAW;
33428e64a68SDaniel Jeong 	if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) {
33528e64a68SDaniel Jeong 		props.brightness = pdata->leda_init_brt;
33628e64a68SDaniel Jeong 		props.max_brightness = pdata->leda_max_brt;
33728e64a68SDaniel Jeong 		pchip->bleda =
33828e64a68SDaniel Jeong 		    devm_backlight_device_register(pchip->dev, "lm3630a_leda",
33928e64a68SDaniel Jeong 						   pchip->dev, pchip,
34028e64a68SDaniel Jeong 						   &lm3630a_bank_a_ops, &props);
34128e64a68SDaniel Jeong 		if (IS_ERR(pchip->bleda))
34228e64a68SDaniel Jeong 			return PTR_ERR(pchip->bleda);
34328e64a68SDaniel Jeong 	}
34428e64a68SDaniel Jeong 
34528e64a68SDaniel Jeong 	if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) &&
34628e64a68SDaniel Jeong 	    (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) {
34728e64a68SDaniel Jeong 		props.brightness = pdata->ledb_init_brt;
34828e64a68SDaniel Jeong 		props.max_brightness = pdata->ledb_max_brt;
34928e64a68SDaniel Jeong 		pchip->bledb =
35028e64a68SDaniel Jeong 		    devm_backlight_device_register(pchip->dev, "lm3630a_ledb",
35128e64a68SDaniel Jeong 						   pchip->dev, pchip,
35228e64a68SDaniel Jeong 						   &lm3630a_bank_b_ops, &props);
35328e64a68SDaniel Jeong 		if (IS_ERR(pchip->bledb))
35428e64a68SDaniel Jeong 			return PTR_ERR(pchip->bledb);
35528e64a68SDaniel Jeong 	}
35628e64a68SDaniel Jeong 	return 0;
35728e64a68SDaniel Jeong }
35828e64a68SDaniel Jeong 
35928e64a68SDaniel Jeong static const struct regmap_config lm3630a_regmap = {
36028e64a68SDaniel Jeong 	.reg_bits = 8,
36128e64a68SDaniel Jeong 	.val_bits = 8,
36228e64a68SDaniel Jeong 	.max_register = REG_MAX,
36328e64a68SDaniel Jeong };
36428e64a68SDaniel Jeong 
36528e64a68SDaniel Jeong static int lm3630a_probe(struct i2c_client *client,
36628e64a68SDaniel Jeong 			 const struct i2c_device_id *id)
36728e64a68SDaniel Jeong {
36828e64a68SDaniel Jeong 	struct lm3630a_platform_data *pdata = client->dev.platform_data;
36928e64a68SDaniel Jeong 	struct lm3630a_chip *pchip;
37028e64a68SDaniel Jeong 	int rval;
37128e64a68SDaniel Jeong 
37228e64a68SDaniel Jeong 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
37328e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : i2c functionality check\n");
37428e64a68SDaniel Jeong 		return -EOPNOTSUPP;
37528e64a68SDaniel Jeong 	}
37628e64a68SDaniel Jeong 
37728e64a68SDaniel Jeong 	pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip),
37828e64a68SDaniel Jeong 			     GFP_KERNEL);
37928e64a68SDaniel Jeong 	if (!pchip)
38028e64a68SDaniel Jeong 		return -ENOMEM;
38128e64a68SDaniel Jeong 	pchip->dev = &client->dev;
38228e64a68SDaniel Jeong 
38328e64a68SDaniel Jeong 	pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap);
38428e64a68SDaniel Jeong 	if (IS_ERR(pchip->regmap)) {
38528e64a68SDaniel Jeong 		rval = PTR_ERR(pchip->regmap);
38628e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval);
38728e64a68SDaniel Jeong 		return rval;
38828e64a68SDaniel Jeong 	}
38928e64a68SDaniel Jeong 
39028e64a68SDaniel Jeong 	i2c_set_clientdata(client, pchip);
39128e64a68SDaniel Jeong 	if (pdata == NULL) {
39228e64a68SDaniel Jeong 		pchip->pdata = devm_kzalloc(pchip->dev,
39328e64a68SDaniel Jeong 					    sizeof(struct
39428e64a68SDaniel Jeong 						   lm3630a_platform_data),
39528e64a68SDaniel Jeong 					    GFP_KERNEL);
39628e64a68SDaniel Jeong 		if (pchip->pdata == NULL)
39728e64a68SDaniel Jeong 			return -ENOMEM;
39828e64a68SDaniel Jeong 		/* default values */
39928e64a68SDaniel Jeong 		pchip->pdata->leda_ctrl = LM3630A_LEDA_ENABLE;
40028e64a68SDaniel Jeong 		pchip->pdata->ledb_ctrl = LM3630A_LEDB_ENABLE;
40128e64a68SDaniel Jeong 		pchip->pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS;
40228e64a68SDaniel Jeong 		pchip->pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS;
40328e64a68SDaniel Jeong 		pchip->pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS;
40428e64a68SDaniel Jeong 		pchip->pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS;
40528e64a68SDaniel Jeong 	} else {
40628e64a68SDaniel Jeong 		pchip->pdata = pdata;
40728e64a68SDaniel Jeong 	}
40828e64a68SDaniel Jeong 	/* chip initialize */
40928e64a68SDaniel Jeong 	rval = lm3630a_chip_init(pchip);
41028e64a68SDaniel Jeong 	if (rval < 0) {
41128e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : init chip\n");
41228e64a68SDaniel Jeong 		return rval;
41328e64a68SDaniel Jeong 	}
41428e64a68SDaniel Jeong 	/* backlight register */
41528e64a68SDaniel Jeong 	rval = lm3630a_backlight_register(pchip);
41628e64a68SDaniel Jeong 	if (rval < 0) {
41728e64a68SDaniel Jeong 		dev_err(&client->dev, "fail : backlight register.\n");
41828e64a68SDaniel Jeong 		return rval;
41928e64a68SDaniel Jeong 	}
42028e64a68SDaniel Jeong 	/* pwm */
42128e64a68SDaniel Jeong 	if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) {
42228e64a68SDaniel Jeong 		pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm");
42328e64a68SDaniel Jeong 		if (IS_ERR(pchip->pwmd)) {
42428e64a68SDaniel Jeong 			dev_err(&client->dev, "fail : get pwm device\n");
42528e64a68SDaniel Jeong 			return PTR_ERR(pchip->pwmd);
42628e64a68SDaniel Jeong 		}
42728e64a68SDaniel Jeong 	}
42828e64a68SDaniel Jeong 	pchip->pwmd->period = pdata->pwm_period;
42928e64a68SDaniel Jeong 
43028e64a68SDaniel Jeong 	/* interrupt enable  : irq 0 is not allowed */
43128e64a68SDaniel Jeong 	pchip->irq = client->irq;
43228e64a68SDaniel Jeong 	if (pchip->irq) {
43328e64a68SDaniel Jeong 		rval = lm3630a_intr_config(pchip);
43428e64a68SDaniel Jeong 		if (rval < 0)
43528e64a68SDaniel Jeong 			return rval;
43628e64a68SDaniel Jeong 	}
43728e64a68SDaniel Jeong 	dev_info(&client->dev, "LM3630A backlight register OK.\n");
43828e64a68SDaniel Jeong 	return 0;
43928e64a68SDaniel Jeong }
44028e64a68SDaniel Jeong 
44128e64a68SDaniel Jeong static int lm3630a_remove(struct i2c_client *client)
44228e64a68SDaniel Jeong {
44328e64a68SDaniel Jeong 	int rval;
44428e64a68SDaniel Jeong 	struct lm3630a_chip *pchip = i2c_get_clientdata(client);
44528e64a68SDaniel Jeong 
44628e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_BRT_A, 0);
44728e64a68SDaniel Jeong 	if (rval < 0)
44828e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
44928e64a68SDaniel Jeong 
45028e64a68SDaniel Jeong 	rval = lm3630a_write(pchip, REG_BRT_B, 0);
45128e64a68SDaniel Jeong 	if (rval < 0)
45228e64a68SDaniel Jeong 		dev_err(pchip->dev, "i2c failed to access register\n");
45328e64a68SDaniel Jeong 
45428e64a68SDaniel Jeong 	if (pchip->irq) {
45528e64a68SDaniel Jeong 		free_irq(pchip->irq, pchip);
45628e64a68SDaniel Jeong 		flush_workqueue(pchip->irqthread);
45728e64a68SDaniel Jeong 		destroy_workqueue(pchip->irqthread);
45828e64a68SDaniel Jeong 	}
45928e64a68SDaniel Jeong 	return 0;
46028e64a68SDaniel Jeong }
46128e64a68SDaniel Jeong 
46228e64a68SDaniel Jeong static const struct i2c_device_id lm3630a_id[] = {
46328e64a68SDaniel Jeong 	{LM3630A_NAME, 0},
46428e64a68SDaniel Jeong 	{}
46528e64a68SDaniel Jeong };
46628e64a68SDaniel Jeong 
46728e64a68SDaniel Jeong MODULE_DEVICE_TABLE(i2c, lm3630a_id);
46828e64a68SDaniel Jeong 
46928e64a68SDaniel Jeong static struct i2c_driver lm3630a_i2c_driver = {
47028e64a68SDaniel Jeong 	.driver = {
47128e64a68SDaniel Jeong 		   .name = LM3630A_NAME,
47228e64a68SDaniel Jeong 		   },
47328e64a68SDaniel Jeong 	.probe = lm3630a_probe,
47428e64a68SDaniel Jeong 	.remove = lm3630a_remove,
47528e64a68SDaniel Jeong 	.id_table = lm3630a_id,
47628e64a68SDaniel Jeong };
47728e64a68SDaniel Jeong 
47828e64a68SDaniel Jeong module_i2c_driver(lm3630a_i2c_driver);
47928e64a68SDaniel Jeong 
48028e64a68SDaniel Jeong MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A");
48128e64a68SDaniel Jeong MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
48228e64a68SDaniel Jeong MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>");
48328e64a68SDaniel Jeong MODULE_LICENSE("GPL v2");
484