18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* Simple driver for Texas Instruments LM3630A Backlight driver chip 48c2ecf20Sopenharmony_ci* Copyright (C) 2012 Texas Instruments 58c2ecf20Sopenharmony_ci*/ 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/i2c.h> 98c2ecf20Sopenharmony_ci#include <linux/backlight.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/pwm.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_data/lm3630a_bl.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define REG_CTRL 0x00 208c2ecf20Sopenharmony_ci#define REG_BOOST 0x02 218c2ecf20Sopenharmony_ci#define REG_CONFIG 0x01 228c2ecf20Sopenharmony_ci#define REG_BRT_A 0x03 238c2ecf20Sopenharmony_ci#define REG_BRT_B 0x04 248c2ecf20Sopenharmony_ci#define REG_I_A 0x05 258c2ecf20Sopenharmony_ci#define REG_I_B 0x06 268c2ecf20Sopenharmony_ci#define REG_INT_STATUS 0x09 278c2ecf20Sopenharmony_ci#define REG_INT_EN 0x0A 288c2ecf20Sopenharmony_ci#define REG_FAULT 0x0B 298c2ecf20Sopenharmony_ci#define REG_PWM_OUTLOW 0x12 308c2ecf20Sopenharmony_ci#define REG_PWM_OUTHIGH 0x13 318c2ecf20Sopenharmony_ci#define REG_FILTER_STRENGTH 0x50 328c2ecf20Sopenharmony_ci#define REG_MAX 0x50 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define INT_DEBOUNCE_MSEC 10 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define LM3630A_BANK_0 0 378c2ecf20Sopenharmony_ci#define LM3630A_BANK_1 1 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define LM3630A_NUM_SINKS 2 408c2ecf20Sopenharmony_ci#define LM3630A_SINK_0 0 418c2ecf20Sopenharmony_ci#define LM3630A_SINK_1 1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct lm3630a_chip { 448c2ecf20Sopenharmony_ci struct device *dev; 458c2ecf20Sopenharmony_ci struct delayed_work work; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci int irq; 488c2ecf20Sopenharmony_ci struct workqueue_struct *irqthread; 498c2ecf20Sopenharmony_ci struct lm3630a_platform_data *pdata; 508c2ecf20Sopenharmony_ci struct backlight_device *bleda; 518c2ecf20Sopenharmony_ci struct backlight_device *bledb; 528c2ecf20Sopenharmony_ci struct gpio_desc *enable_gpio; 538c2ecf20Sopenharmony_ci struct regmap *regmap; 548c2ecf20Sopenharmony_ci struct pwm_device *pwmd; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* i2c access */ 588c2ecf20Sopenharmony_cistatic int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int rval; 618c2ecf20Sopenharmony_ci unsigned int reg_val; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci rval = regmap_read(pchip->regmap, reg, ®_val); 648c2ecf20Sopenharmony_ci if (rval < 0) 658c2ecf20Sopenharmony_ci return rval; 668c2ecf20Sopenharmony_ci return reg_val & 0xFF; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int lm3630a_write(struct lm3630a_chip *pchip, 708c2ecf20Sopenharmony_ci unsigned int reg, unsigned int data) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return regmap_write(pchip->regmap, reg, data); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int lm3630a_update(struct lm3630a_chip *pchip, 768c2ecf20Sopenharmony_ci unsigned int reg, unsigned int mask, 778c2ecf20Sopenharmony_ci unsigned int data) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return regmap_update_bits(pchip->regmap, reg, mask, data); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* initialize chip */ 838c2ecf20Sopenharmony_cistatic int lm3630a_chip_init(struct lm3630a_chip *pchip) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int rval; 868c2ecf20Sopenharmony_ci struct lm3630a_platform_data *pdata = pchip->pdata; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 898c2ecf20Sopenharmony_ci /* set Filter Strength Register */ 908c2ecf20Sopenharmony_ci rval = lm3630a_write(pchip, REG_FILTER_STRENGTH, 0x03); 918c2ecf20Sopenharmony_ci /* set Cofig. register */ 928c2ecf20Sopenharmony_ci rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl); 938c2ecf20Sopenharmony_ci /* set boost control */ 948c2ecf20Sopenharmony_ci rval |= lm3630a_write(pchip, REG_BOOST, 0x38); 958c2ecf20Sopenharmony_ci /* set current A */ 968c2ecf20Sopenharmony_ci rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F); 978c2ecf20Sopenharmony_ci /* set current B */ 988c2ecf20Sopenharmony_ci rval |= lm3630a_write(pchip, REG_I_B, 0x1F); 998c2ecf20Sopenharmony_ci /* set control */ 1008c2ecf20Sopenharmony_ci rval |= lm3630a_update(pchip, REG_CTRL, 0x14, pdata->leda_ctrl); 1018c2ecf20Sopenharmony_ci rval |= lm3630a_update(pchip, REG_CTRL, 0x0B, pdata->ledb_ctrl); 1028c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1038c2ecf20Sopenharmony_ci /* set brightness A and B */ 1048c2ecf20Sopenharmony_ci rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt); 1058c2ecf20Sopenharmony_ci rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (rval < 0) 1088c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 1098c2ecf20Sopenharmony_ci return rval; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* interrupt handling */ 1138c2ecf20Sopenharmony_cistatic void lm3630a_delayed_func(struct work_struct *work) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int rval; 1168c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci pchip = container_of(work, struct lm3630a_chip, work.work); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_INT_STATUS); 1218c2ecf20Sopenharmony_ci if (rval < 0) { 1228c2ecf20Sopenharmony_ci dev_err(pchip->dev, 1238c2ecf20Sopenharmony_ci "i2c failed to access REG_INT_STATUS Register\n"); 1248c2ecf20Sopenharmony_ci return; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic irqreturn_t lm3630a_isr_func(int irq, void *chip) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int rval; 1338c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = chip; 1348c2ecf20Sopenharmony_ci unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci queue_delayed_work(pchip->irqthread, &pchip->work, delay); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 1398c2ecf20Sopenharmony_ci if (rval < 0) { 1408c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 1418c2ecf20Sopenharmony_ci return IRQ_NONE; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int lm3630a_intr_config(struct lm3630a_chip *pchip) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int rval; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci rval = lm3630a_write(pchip, REG_INT_EN, 0x87); 1518c2ecf20Sopenharmony_ci if (rval < 0) 1528c2ecf20Sopenharmony_ci return rval; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func); 1558c2ecf20Sopenharmony_ci pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd"); 1568c2ecf20Sopenharmony_ci if (!pchip->irqthread) { 1578c2ecf20Sopenharmony_ci dev_err(pchip->dev, "create irq thread fail\n"); 1588c2ecf20Sopenharmony_ci return -ENOMEM; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci if (request_threaded_irq 1618c2ecf20Sopenharmony_ci (pchip->irq, NULL, lm3630a_isr_func, 1628c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) { 1638c2ecf20Sopenharmony_ci dev_err(pchip->dev, "request threaded irq fail\n"); 1648c2ecf20Sopenharmony_ci destroy_workqueue(pchip->irqthread); 1658c2ecf20Sopenharmony_ci return -ENOMEM; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci return rval; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned int period = pchip->pdata->pwm_period; 1738c2ecf20Sopenharmony_ci unsigned int duty = br * period / br_max; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pwm_config(pchip->pwmd, duty, period); 1768c2ecf20Sopenharmony_ci if (duty) 1778c2ecf20Sopenharmony_ci pwm_enable(pchip->pwmd); 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci pwm_disable(pchip->pwmd); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* update and get brightness */ 1838c2ecf20Sopenharmony_cistatic int lm3630a_bank_a_update_status(struct backlight_device *bl) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int ret; 1868c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = bl_get_data(bl); 1878c2ecf20Sopenharmony_ci enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* pwm control */ 1908c2ecf20Sopenharmony_ci if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { 1918c2ecf20Sopenharmony_ci lm3630a_pwm_ctrl(pchip, bl->props.brightness, 1928c2ecf20Sopenharmony_ci bl->props.max_brightness); 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* disable sleep */ 1978c2ecf20Sopenharmony_ci ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 1988c2ecf20Sopenharmony_ci if (ret < 0) 1998c2ecf20Sopenharmony_ci goto out_i2c_err; 2008c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 2018c2ecf20Sopenharmony_ci /* minimum brightness is 0x04 */ 2028c2ecf20Sopenharmony_ci ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness); 2038c2ecf20Sopenharmony_ci if (bl->props.brightness < 0x4) 2048c2ecf20Sopenharmony_ci ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0); 2058c2ecf20Sopenharmony_ci else 2068c2ecf20Sopenharmony_ci ret |= lm3630a_update(pchip, REG_CTRL, 2078c2ecf20Sopenharmony_ci LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE); 2088c2ecf20Sopenharmony_ci if (ret < 0) 2098c2ecf20Sopenharmony_ci goto out_i2c_err; 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciout_i2c_err: 2138c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret)); 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int lm3630a_bank_a_get_brightness(struct backlight_device *bl) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int brightness, rval; 2208c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = bl_get_data(bl); 2218c2ecf20Sopenharmony_ci enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { 2248c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); 2258c2ecf20Sopenharmony_ci if (rval < 0) 2268c2ecf20Sopenharmony_ci goto out_i2c_err; 2278c2ecf20Sopenharmony_ci brightness = (rval & 0x01) << 8; 2288c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_PWM_OUTLOW); 2298c2ecf20Sopenharmony_ci if (rval < 0) 2308c2ecf20Sopenharmony_ci goto out_i2c_err; 2318c2ecf20Sopenharmony_ci brightness |= rval; 2328c2ecf20Sopenharmony_ci goto out; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* disable sleep */ 2368c2ecf20Sopenharmony_ci rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 2378c2ecf20Sopenharmony_ci if (rval < 0) 2388c2ecf20Sopenharmony_ci goto out_i2c_err; 2398c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 2408c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_BRT_A); 2418c2ecf20Sopenharmony_ci if (rval < 0) 2428c2ecf20Sopenharmony_ci goto out_i2c_err; 2438c2ecf20Sopenharmony_ci brightness = rval; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciout: 2468c2ecf20Sopenharmony_ci bl->props.brightness = brightness; 2478c2ecf20Sopenharmony_ci return bl->props.brightness; 2488c2ecf20Sopenharmony_ciout_i2c_err: 2498c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic const struct backlight_ops lm3630a_bank_a_ops = { 2548c2ecf20Sopenharmony_ci .options = BL_CORE_SUSPENDRESUME, 2558c2ecf20Sopenharmony_ci .update_status = lm3630a_bank_a_update_status, 2568c2ecf20Sopenharmony_ci .get_brightness = lm3630a_bank_a_get_brightness, 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* update and get brightness */ 2608c2ecf20Sopenharmony_cistatic int lm3630a_bank_b_update_status(struct backlight_device *bl) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int ret; 2638c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = bl_get_data(bl); 2648c2ecf20Sopenharmony_ci enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* pwm control */ 2678c2ecf20Sopenharmony_ci if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { 2688c2ecf20Sopenharmony_ci lm3630a_pwm_ctrl(pchip, bl->props.brightness, 2698c2ecf20Sopenharmony_ci bl->props.max_brightness); 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* disable sleep */ 2748c2ecf20Sopenharmony_ci ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 2758c2ecf20Sopenharmony_ci if (ret < 0) 2768c2ecf20Sopenharmony_ci goto out_i2c_err; 2778c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 2788c2ecf20Sopenharmony_ci /* minimum brightness is 0x04 */ 2798c2ecf20Sopenharmony_ci ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness); 2808c2ecf20Sopenharmony_ci if (bl->props.brightness < 0x4) 2818c2ecf20Sopenharmony_ci ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0); 2828c2ecf20Sopenharmony_ci else 2838c2ecf20Sopenharmony_ci ret |= lm3630a_update(pchip, REG_CTRL, 2848c2ecf20Sopenharmony_ci LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE); 2858c2ecf20Sopenharmony_ci if (ret < 0) 2868c2ecf20Sopenharmony_ci goto out_i2c_err; 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciout_i2c_err: 2908c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access (%pe)\n", ERR_PTR(ret)); 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int lm3630a_bank_b_get_brightness(struct backlight_device *bl) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci int brightness, rval; 2978c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = bl_get_data(bl); 2988c2ecf20Sopenharmony_ci enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { 3018c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); 3028c2ecf20Sopenharmony_ci if (rval < 0) 3038c2ecf20Sopenharmony_ci goto out_i2c_err; 3048c2ecf20Sopenharmony_ci brightness = (rval & 0x01) << 8; 3058c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_PWM_OUTLOW); 3068c2ecf20Sopenharmony_ci if (rval < 0) 3078c2ecf20Sopenharmony_ci goto out_i2c_err; 3088c2ecf20Sopenharmony_ci brightness |= rval; 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* disable sleep */ 3138c2ecf20Sopenharmony_ci rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); 3148c2ecf20Sopenharmony_ci if (rval < 0) 3158c2ecf20Sopenharmony_ci goto out_i2c_err; 3168c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3178c2ecf20Sopenharmony_ci rval = lm3630a_read(pchip, REG_BRT_B); 3188c2ecf20Sopenharmony_ci if (rval < 0) 3198c2ecf20Sopenharmony_ci goto out_i2c_err; 3208c2ecf20Sopenharmony_ci brightness = rval; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ciout: 3238c2ecf20Sopenharmony_ci bl->props.brightness = brightness; 3248c2ecf20Sopenharmony_ci return bl->props.brightness; 3258c2ecf20Sopenharmony_ciout_i2c_err: 3268c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic const struct backlight_ops lm3630a_bank_b_ops = { 3318c2ecf20Sopenharmony_ci .options = BL_CORE_SUSPENDRESUME, 3328c2ecf20Sopenharmony_ci .update_status = lm3630a_bank_b_update_status, 3338c2ecf20Sopenharmony_ci .get_brightness = lm3630a_bank_b_get_brightness, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int lm3630a_backlight_register(struct lm3630a_chip *pchip) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct lm3630a_platform_data *pdata = pchip->pdata; 3398c2ecf20Sopenharmony_ci struct backlight_properties props; 3408c2ecf20Sopenharmony_ci const char *label; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci props.type = BACKLIGHT_RAW; 3438c2ecf20Sopenharmony_ci if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) { 3448c2ecf20Sopenharmony_ci props.brightness = pdata->leda_init_brt; 3458c2ecf20Sopenharmony_ci props.max_brightness = pdata->leda_max_brt; 3468c2ecf20Sopenharmony_ci label = pdata->leda_label ? pdata->leda_label : "lm3630a_leda"; 3478c2ecf20Sopenharmony_ci pchip->bleda = 3488c2ecf20Sopenharmony_ci devm_backlight_device_register(pchip->dev, label, 3498c2ecf20Sopenharmony_ci pchip->dev, pchip, 3508c2ecf20Sopenharmony_ci &lm3630a_bank_a_ops, &props); 3518c2ecf20Sopenharmony_ci if (IS_ERR(pchip->bleda)) 3528c2ecf20Sopenharmony_ci return PTR_ERR(pchip->bleda); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) && 3568c2ecf20Sopenharmony_ci (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) { 3578c2ecf20Sopenharmony_ci props.brightness = pdata->ledb_init_brt; 3588c2ecf20Sopenharmony_ci props.max_brightness = pdata->ledb_max_brt; 3598c2ecf20Sopenharmony_ci label = pdata->ledb_label ? pdata->ledb_label : "lm3630a_ledb"; 3608c2ecf20Sopenharmony_ci pchip->bledb = 3618c2ecf20Sopenharmony_ci devm_backlight_device_register(pchip->dev, label, 3628c2ecf20Sopenharmony_ci pchip->dev, pchip, 3638c2ecf20Sopenharmony_ci &lm3630a_bank_b_ops, &props); 3648c2ecf20Sopenharmony_ci if (IS_ERR(pchip->bledb)) 3658c2ecf20Sopenharmony_ci return PTR_ERR(pchip->bledb); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic const struct regmap_config lm3630a_regmap = { 3718c2ecf20Sopenharmony_ci .reg_bits = 8, 3728c2ecf20Sopenharmony_ci .val_bits = 8, 3738c2ecf20Sopenharmony_ci .max_register = REG_MAX, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int lm3630a_parse_led_sources(struct fwnode_handle *node, 3778c2ecf20Sopenharmony_ci int default_led_sources) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci u32 sources[LM3630A_NUM_SINKS]; 3808c2ecf20Sopenharmony_ci int ret, num_sources, i; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci num_sources = fwnode_property_count_u32(node, "led-sources"); 3838c2ecf20Sopenharmony_ci if (num_sources < 0) 3848c2ecf20Sopenharmony_ci return default_led_sources; 3858c2ecf20Sopenharmony_ci else if (num_sources > ARRAY_SIZE(sources)) 3868c2ecf20Sopenharmony_ci return -EINVAL; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32_array(node, "led-sources", sources, 3898c2ecf20Sopenharmony_ci num_sources); 3908c2ecf20Sopenharmony_ci if (ret) 3918c2ecf20Sopenharmony_ci return ret; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci for (i = 0; i < num_sources; i++) { 3948c2ecf20Sopenharmony_ci if (sources[i] != LM3630A_SINK_0 && sources[i] != LM3630A_SINK_1) 3958c2ecf20Sopenharmony_ci return -EINVAL; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret |= BIT(sources[i]); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return ret; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int lm3630a_parse_bank(struct lm3630a_platform_data *pdata, 4048c2ecf20Sopenharmony_ci struct fwnode_handle *node, int *seen_led_sources) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int led_sources, ret; 4078c2ecf20Sopenharmony_ci const char *label; 4088c2ecf20Sopenharmony_ci u32 bank, val; 4098c2ecf20Sopenharmony_ci bool linear; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(node, "reg", &bank); 4128c2ecf20Sopenharmony_ci if (ret) 4138c2ecf20Sopenharmony_ci return ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (bank != LM3630A_BANK_0 && bank != LM3630A_BANK_1) 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci led_sources = lm3630a_parse_led_sources(node, BIT(bank)); 4198c2ecf20Sopenharmony_ci if (led_sources < 0) 4208c2ecf20Sopenharmony_ci return led_sources; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (*seen_led_sources & led_sources) 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci *seen_led_sources |= led_sources; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci linear = fwnode_property_read_bool(node, 4288c2ecf20Sopenharmony_ci "ti,linear-mapping-mode"); 4298c2ecf20Sopenharmony_ci if (bank) { 4308c2ecf20Sopenharmony_ci if (led_sources & BIT(LM3630A_SINK_0) || 4318c2ecf20Sopenharmony_ci !(led_sources & BIT(LM3630A_SINK_1))) 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci pdata->ledb_ctrl = linear ? 4358c2ecf20Sopenharmony_ci LM3630A_LEDB_ENABLE_LINEAR : 4368c2ecf20Sopenharmony_ci LM3630A_LEDB_ENABLE; 4378c2ecf20Sopenharmony_ci } else { 4388c2ecf20Sopenharmony_ci if (!(led_sources & BIT(LM3630A_SINK_0))) 4398c2ecf20Sopenharmony_ci return -EINVAL; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci pdata->leda_ctrl = linear ? 4428c2ecf20Sopenharmony_ci LM3630A_LEDA_ENABLE_LINEAR : 4438c2ecf20Sopenharmony_ci LM3630A_LEDA_ENABLE; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (led_sources & BIT(LM3630A_SINK_1)) 4468c2ecf20Sopenharmony_ci pdata->ledb_ctrl = LM3630A_LEDB_ON_A; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci ret = fwnode_property_read_string(node, "label", &label); 4508c2ecf20Sopenharmony_ci if (!ret) { 4518c2ecf20Sopenharmony_ci if (bank) 4528c2ecf20Sopenharmony_ci pdata->ledb_label = label; 4538c2ecf20Sopenharmony_ci else 4548c2ecf20Sopenharmony_ci pdata->leda_label = label; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(node, "default-brightness", 4588c2ecf20Sopenharmony_ci &val); 4598c2ecf20Sopenharmony_ci if (!ret) { 4608c2ecf20Sopenharmony_ci if (bank) 4618c2ecf20Sopenharmony_ci pdata->ledb_init_brt = val; 4628c2ecf20Sopenharmony_ci else 4638c2ecf20Sopenharmony_ci pdata->leda_init_brt = val; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(node, "max-brightness", &val); 4678c2ecf20Sopenharmony_ci if (!ret) { 4688c2ecf20Sopenharmony_ci if (bank) 4698c2ecf20Sopenharmony_ci pdata->ledb_max_brt = val; 4708c2ecf20Sopenharmony_ci else 4718c2ecf20Sopenharmony_ci pdata->leda_max_brt = val; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int lm3630a_parse_node(struct lm3630a_chip *pchip, 4788c2ecf20Sopenharmony_ci struct lm3630a_platform_data *pdata) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int ret = -ENODEV, seen_led_sources = 0; 4818c2ecf20Sopenharmony_ci struct fwnode_handle *node; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci device_for_each_child_node(pchip->dev, node) { 4848c2ecf20Sopenharmony_ci ret = lm3630a_parse_bank(pdata, node, &seen_led_sources); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci fwnode_handle_put(node); 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int lm3630a_probe(struct i2c_client *client, 4958c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct lm3630a_platform_data *pdata = dev_get_platdata(&client->dev); 4988c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip; 4998c2ecf20Sopenharmony_ci int rval; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 5028c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : i2c functionality check\n"); 5038c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip), 5078c2ecf20Sopenharmony_ci GFP_KERNEL); 5088c2ecf20Sopenharmony_ci if (!pchip) 5098c2ecf20Sopenharmony_ci return -ENOMEM; 5108c2ecf20Sopenharmony_ci pchip->dev = &client->dev; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap); 5138c2ecf20Sopenharmony_ci if (IS_ERR(pchip->regmap)) { 5148c2ecf20Sopenharmony_ci rval = PTR_ERR(pchip->regmap); 5158c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval); 5168c2ecf20Sopenharmony_ci return rval; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci i2c_set_clientdata(client, pchip); 5208c2ecf20Sopenharmony_ci if (pdata == NULL) { 5218c2ecf20Sopenharmony_ci pdata = devm_kzalloc(pchip->dev, 5228c2ecf20Sopenharmony_ci sizeof(struct lm3630a_platform_data), 5238c2ecf20Sopenharmony_ci GFP_KERNEL); 5248c2ecf20Sopenharmony_ci if (pdata == NULL) 5258c2ecf20Sopenharmony_ci return -ENOMEM; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* default values */ 5288c2ecf20Sopenharmony_ci pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS; 5298c2ecf20Sopenharmony_ci pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS; 5308c2ecf20Sopenharmony_ci pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS; 5318c2ecf20Sopenharmony_ci pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci rval = lm3630a_parse_node(pchip, pdata); 5348c2ecf20Sopenharmony_ci if (rval) { 5358c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : parse node\n"); 5368c2ecf20Sopenharmony_ci return rval; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci pchip->pdata = pdata; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci pchip->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", 5428c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 5438c2ecf20Sopenharmony_ci if (IS_ERR(pchip->enable_gpio)) { 5448c2ecf20Sopenharmony_ci rval = PTR_ERR(pchip->enable_gpio); 5458c2ecf20Sopenharmony_ci return rval; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* chip initialize */ 5498c2ecf20Sopenharmony_ci rval = lm3630a_chip_init(pchip); 5508c2ecf20Sopenharmony_ci if (rval < 0) { 5518c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : init chip\n"); 5528c2ecf20Sopenharmony_ci return rval; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci /* backlight register */ 5558c2ecf20Sopenharmony_ci rval = lm3630a_backlight_register(pchip); 5568c2ecf20Sopenharmony_ci if (rval < 0) { 5578c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : backlight register.\n"); 5588c2ecf20Sopenharmony_ci return rval; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci /* pwm */ 5618c2ecf20Sopenharmony_ci if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) { 5628c2ecf20Sopenharmony_ci pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm"); 5638c2ecf20Sopenharmony_ci if (IS_ERR(pchip->pwmd)) { 5648c2ecf20Sopenharmony_ci dev_err(&client->dev, "fail : get pwm device\n"); 5658c2ecf20Sopenharmony_ci return PTR_ERR(pchip->pwmd); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * FIXME: pwm_apply_args() should be removed when switching to 5708c2ecf20Sopenharmony_ci * the atomic PWM API. 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_ci pwm_apply_args(pchip->pwmd); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* interrupt enable : irq 0 is not allowed */ 5768c2ecf20Sopenharmony_ci pchip->irq = client->irq; 5778c2ecf20Sopenharmony_ci if (pchip->irq) { 5788c2ecf20Sopenharmony_ci rval = lm3630a_intr_config(pchip); 5798c2ecf20Sopenharmony_ci if (rval < 0) 5808c2ecf20Sopenharmony_ci return rval; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci dev_info(&client->dev, "LM3630A backlight register OK.\n"); 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int lm3630a_remove(struct i2c_client *client) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int rval; 5898c2ecf20Sopenharmony_ci struct lm3630a_chip *pchip = i2c_get_clientdata(client); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci rval = lm3630a_write(pchip, REG_BRT_A, 0); 5928c2ecf20Sopenharmony_ci if (rval < 0) 5938c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci rval = lm3630a_write(pchip, REG_BRT_B, 0); 5968c2ecf20Sopenharmony_ci if (rval < 0) 5978c2ecf20Sopenharmony_ci dev_err(pchip->dev, "i2c failed to access register\n"); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (pchip->irq) { 6008c2ecf20Sopenharmony_ci free_irq(pchip->irq, pchip); 6018c2ecf20Sopenharmony_ci flush_workqueue(pchip->irqthread); 6028c2ecf20Sopenharmony_ci destroy_workqueue(pchip->irqthread); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic const struct i2c_device_id lm3630a_id[] = { 6088c2ecf20Sopenharmony_ci {LM3630A_NAME, 0}, 6098c2ecf20Sopenharmony_ci {} 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lm3630a_id); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic const struct of_device_id lm3630a_match_table[] = { 6158c2ecf20Sopenharmony_ci { .compatible = "ti,lm3630a", }, 6168c2ecf20Sopenharmony_ci { }, 6178c2ecf20Sopenharmony_ci}; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lm3630a_match_table); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic struct i2c_driver lm3630a_i2c_driver = { 6228c2ecf20Sopenharmony_ci .driver = { 6238c2ecf20Sopenharmony_ci .name = LM3630A_NAME, 6248c2ecf20Sopenharmony_ci .of_match_table = lm3630a_match_table, 6258c2ecf20Sopenharmony_ci }, 6268c2ecf20Sopenharmony_ci .probe = lm3630a_probe, 6278c2ecf20Sopenharmony_ci .remove = lm3630a_remove, 6288c2ecf20Sopenharmony_ci .id_table = lm3630a_id, 6298c2ecf20Sopenharmony_ci}; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cimodule_i2c_driver(lm3630a_i2c_driver); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A"); 6348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); 6358c2ecf20Sopenharmony_ciMODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>"); 6368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 637