1*6f6d5c3fSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0 2de474b5bSGuennadi Liakhovetski /* 3de474b5bSGuennadi Liakhovetski * AS3711 PMIC backlight driver, using DCDC Step Up Converters 4de474b5bSGuennadi Liakhovetski * 5de474b5bSGuennadi Liakhovetski * Copyright (C) 2012 Renesas Electronics Corporation 6de474b5bSGuennadi Liakhovetski * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de> 7de474b5bSGuennadi Liakhovetski */ 8de474b5bSGuennadi Liakhovetski 9de474b5bSGuennadi Liakhovetski #include <linux/backlight.h> 10de474b5bSGuennadi Liakhovetski #include <linux/delay.h> 11de474b5bSGuennadi Liakhovetski #include <linux/device.h> 12de474b5bSGuennadi Liakhovetski #include <linux/err.h> 13de474b5bSGuennadi Liakhovetski #include <linux/fb.h> 14de474b5bSGuennadi Liakhovetski #include <linux/kernel.h> 15de474b5bSGuennadi Liakhovetski #include <linux/mfd/as3711.h> 16de474b5bSGuennadi Liakhovetski #include <linux/module.h> 17de474b5bSGuennadi Liakhovetski #include <linux/platform_device.h> 18de474b5bSGuennadi Liakhovetski #include <linux/regmap.h> 19de474b5bSGuennadi Liakhovetski #include <linux/slab.h> 20de474b5bSGuennadi Liakhovetski 21de474b5bSGuennadi Liakhovetski enum as3711_bl_type { 22de474b5bSGuennadi Liakhovetski AS3711_BL_SU1, 23de474b5bSGuennadi Liakhovetski AS3711_BL_SU2, 24de474b5bSGuennadi Liakhovetski }; 25de474b5bSGuennadi Liakhovetski 26de474b5bSGuennadi Liakhovetski struct as3711_bl_data { 27de474b5bSGuennadi Liakhovetski bool powered; 28de474b5bSGuennadi Liakhovetski enum as3711_bl_type type; 29de474b5bSGuennadi Liakhovetski int brightness; 30de474b5bSGuennadi Liakhovetski struct backlight_device *bl; 31de474b5bSGuennadi Liakhovetski }; 32de474b5bSGuennadi Liakhovetski 33de474b5bSGuennadi Liakhovetski struct as3711_bl_supply { 34de474b5bSGuennadi Liakhovetski struct as3711_bl_data su1; 35de474b5bSGuennadi Liakhovetski struct as3711_bl_data su2; 36de474b5bSGuennadi Liakhovetski const struct as3711_bl_pdata *pdata; 37de474b5bSGuennadi Liakhovetski struct as3711 *as3711; 38de474b5bSGuennadi Liakhovetski }; 39de474b5bSGuennadi Liakhovetski 40de474b5bSGuennadi Liakhovetski static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su) 41de474b5bSGuennadi Liakhovetski { 42de474b5bSGuennadi Liakhovetski switch (su->type) { 43de474b5bSGuennadi Liakhovetski case AS3711_BL_SU1: 44de474b5bSGuennadi Liakhovetski return container_of(su, struct as3711_bl_supply, su1); 45de474b5bSGuennadi Liakhovetski case AS3711_BL_SU2: 46de474b5bSGuennadi Liakhovetski return container_of(su, struct as3711_bl_supply, su2); 47de474b5bSGuennadi Liakhovetski } 48de474b5bSGuennadi Liakhovetski return NULL; 49de474b5bSGuennadi Liakhovetski } 50de474b5bSGuennadi Liakhovetski 51de474b5bSGuennadi Liakhovetski static int as3711_set_brightness_auto_i(struct as3711_bl_data *data, 52de474b5bSGuennadi Liakhovetski unsigned int brightness) 53de474b5bSGuennadi Liakhovetski { 54de474b5bSGuennadi Liakhovetski struct as3711_bl_supply *supply = to_supply(data); 55de474b5bSGuennadi Liakhovetski struct as3711 *as3711 = supply->as3711; 56de474b5bSGuennadi Liakhovetski const struct as3711_bl_pdata *pdata = supply->pdata; 57de474b5bSGuennadi Liakhovetski int ret = 0; 58de474b5bSGuennadi Liakhovetski 59de474b5bSGuennadi Liakhovetski /* Only all equal current values are supported */ 60de474b5bSGuennadi Liakhovetski if (pdata->su2_auto_curr1) 61de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, 62de474b5bSGuennadi Liakhovetski brightness); 63de474b5bSGuennadi Liakhovetski if (!ret && pdata->su2_auto_curr2) 64de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, 65de474b5bSGuennadi Liakhovetski brightness); 66de474b5bSGuennadi Liakhovetski if (!ret && pdata->su2_auto_curr3) 67de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, 68de474b5bSGuennadi Liakhovetski brightness); 69de474b5bSGuennadi Liakhovetski 70de474b5bSGuennadi Liakhovetski return ret; 71de474b5bSGuennadi Liakhovetski } 72de474b5bSGuennadi Liakhovetski 73de474b5bSGuennadi Liakhovetski static int as3711_set_brightness_v(struct as3711 *as3711, 74de474b5bSGuennadi Liakhovetski unsigned int brightness, 75de474b5bSGuennadi Liakhovetski unsigned int reg) 76de474b5bSGuennadi Liakhovetski { 77de474b5bSGuennadi Liakhovetski if (brightness > 31) 78de474b5bSGuennadi Liakhovetski return -EINVAL; 79de474b5bSGuennadi Liakhovetski 80de474b5bSGuennadi Liakhovetski return regmap_update_bits(as3711->regmap, reg, 0xf0, 81de474b5bSGuennadi Liakhovetski brightness << 4); 82de474b5bSGuennadi Liakhovetski } 83de474b5bSGuennadi Liakhovetski 84de474b5bSGuennadi Liakhovetski static int as3711_bl_su2_reset(struct as3711_bl_supply *supply) 85de474b5bSGuennadi Liakhovetski { 86de474b5bSGuennadi Liakhovetski struct as3711 *as3711 = supply->as3711; 87de474b5bSGuennadi Liakhovetski int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5, 88de474b5bSGuennadi Liakhovetski 3, supply->pdata->su2_fbprot); 89de474b5bSGuennadi Liakhovetski if (!ret) 90de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, 91de474b5bSGuennadi Liakhovetski AS3711_STEPUP_CONTROL_2, 1, 0); 92de474b5bSGuennadi Liakhovetski if (!ret) 93de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, 94de474b5bSGuennadi Liakhovetski AS3711_STEPUP_CONTROL_2, 1, 1); 95de474b5bSGuennadi Liakhovetski return ret; 96de474b5bSGuennadi Liakhovetski } 97de474b5bSGuennadi Liakhovetski 98de474b5bSGuennadi Liakhovetski /* 99de474b5bSGuennadi Liakhovetski * Someone with less fragile or less expensive hardware could try to simplify 100de474b5bSGuennadi Liakhovetski * the brightness adjustment procedure. 101de474b5bSGuennadi Liakhovetski */ 102de474b5bSGuennadi Liakhovetski static int as3711_bl_update_status(struct backlight_device *bl) 103de474b5bSGuennadi Liakhovetski { 104de474b5bSGuennadi Liakhovetski struct as3711_bl_data *data = bl_get_data(bl); 105de474b5bSGuennadi Liakhovetski struct as3711_bl_supply *supply = to_supply(data); 106de474b5bSGuennadi Liakhovetski struct as3711 *as3711 = supply->as3711; 107de474b5bSGuennadi Liakhovetski int brightness = bl->props.brightness; 108de474b5bSGuennadi Liakhovetski int ret = 0; 109de474b5bSGuennadi Liakhovetski 110de474b5bSGuennadi Liakhovetski dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n", 111de474b5bSGuennadi Liakhovetski __func__, bl->props.brightness, bl->props.power, 112de474b5bSGuennadi Liakhovetski bl->props.fb_blank, bl->props.state); 113de474b5bSGuennadi Liakhovetski 114de474b5bSGuennadi Liakhovetski if (bl->props.power != FB_BLANK_UNBLANK || 115de474b5bSGuennadi Liakhovetski bl->props.fb_blank != FB_BLANK_UNBLANK || 116de474b5bSGuennadi Liakhovetski bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) 117de474b5bSGuennadi Liakhovetski brightness = 0; 118de474b5bSGuennadi Liakhovetski 119de474b5bSGuennadi Liakhovetski if (data->type == AS3711_BL_SU1) { 120de474b5bSGuennadi Liakhovetski ret = as3711_set_brightness_v(as3711, brightness, 121de474b5bSGuennadi Liakhovetski AS3711_STEPUP_CONTROL_1); 122de474b5bSGuennadi Liakhovetski } else { 123de474b5bSGuennadi Liakhovetski const struct as3711_bl_pdata *pdata = supply->pdata; 124de474b5bSGuennadi Liakhovetski 125de474b5bSGuennadi Liakhovetski switch (pdata->su2_feedback) { 126de474b5bSGuennadi Liakhovetski case AS3711_SU2_VOLTAGE: 127de474b5bSGuennadi Liakhovetski ret = as3711_set_brightness_v(as3711, brightness, 128de474b5bSGuennadi Liakhovetski AS3711_STEPUP_CONTROL_2); 129de474b5bSGuennadi Liakhovetski break; 130de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR_AUTO: 131de474b5bSGuennadi Liakhovetski ret = as3711_set_brightness_auto_i(data, brightness / 4); 132de474b5bSGuennadi Liakhovetski if (ret < 0) 133de474b5bSGuennadi Liakhovetski return ret; 134de474b5bSGuennadi Liakhovetski if (brightness) { 135de474b5bSGuennadi Liakhovetski ret = as3711_bl_su2_reset(supply); 136de474b5bSGuennadi Liakhovetski if (ret < 0) 137de474b5bSGuennadi Liakhovetski return ret; 138de474b5bSGuennadi Liakhovetski udelay(500); 139de474b5bSGuennadi Liakhovetski ret = as3711_set_brightness_auto_i(data, brightness); 140de474b5bSGuennadi Liakhovetski } else { 141de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, 142de474b5bSGuennadi Liakhovetski AS3711_STEPUP_CONTROL_2, 1, 0); 143de474b5bSGuennadi Liakhovetski } 144de474b5bSGuennadi Liakhovetski break; 145de474b5bSGuennadi Liakhovetski /* Manual one current feedback pin below */ 146de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR1: 147de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE, 148de474b5bSGuennadi Liakhovetski brightness); 149de474b5bSGuennadi Liakhovetski break; 150de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR2: 151de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE, 152de474b5bSGuennadi Liakhovetski brightness); 153de474b5bSGuennadi Liakhovetski break; 154de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR3: 155de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE, 156de474b5bSGuennadi Liakhovetski brightness); 157de474b5bSGuennadi Liakhovetski break; 158de474b5bSGuennadi Liakhovetski default: 159de474b5bSGuennadi Liakhovetski ret = -EINVAL; 160de474b5bSGuennadi Liakhovetski } 161de474b5bSGuennadi Liakhovetski } 162de474b5bSGuennadi Liakhovetski if (!ret) 163de474b5bSGuennadi Liakhovetski data->brightness = brightness; 164de474b5bSGuennadi Liakhovetski 165de474b5bSGuennadi Liakhovetski return ret; 166de474b5bSGuennadi Liakhovetski } 167de474b5bSGuennadi Liakhovetski 168de474b5bSGuennadi Liakhovetski static int as3711_bl_get_brightness(struct backlight_device *bl) 169de474b5bSGuennadi Liakhovetski { 170de474b5bSGuennadi Liakhovetski struct as3711_bl_data *data = bl_get_data(bl); 171de474b5bSGuennadi Liakhovetski 172de474b5bSGuennadi Liakhovetski return data->brightness; 173de474b5bSGuennadi Liakhovetski } 174de474b5bSGuennadi Liakhovetski 175de474b5bSGuennadi Liakhovetski static const struct backlight_ops as3711_bl_ops = { 176de474b5bSGuennadi Liakhovetski .update_status = as3711_bl_update_status, 177de474b5bSGuennadi Liakhovetski .get_brightness = as3711_bl_get_brightness, 178de474b5bSGuennadi Liakhovetski }; 179de474b5bSGuennadi Liakhovetski 180de474b5bSGuennadi Liakhovetski static int as3711_bl_init_su2(struct as3711_bl_supply *supply) 181de474b5bSGuennadi Liakhovetski { 182de474b5bSGuennadi Liakhovetski struct as3711 *as3711 = supply->as3711; 183de474b5bSGuennadi Liakhovetski const struct as3711_bl_pdata *pdata = supply->pdata; 184de474b5bSGuennadi Liakhovetski u8 ctl = 0; 185de474b5bSGuennadi Liakhovetski int ret; 186de474b5bSGuennadi Liakhovetski 187de474b5bSGuennadi Liakhovetski dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback); 188de474b5bSGuennadi Liakhovetski 189de474b5bSGuennadi Liakhovetski /* Turn SU2 off */ 190de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0); 191de474b5bSGuennadi Liakhovetski if (ret < 0) 192de474b5bSGuennadi Liakhovetski return ret; 193de474b5bSGuennadi Liakhovetski 194de474b5bSGuennadi Liakhovetski switch (pdata->su2_feedback) { 195de474b5bSGuennadi Liakhovetski case AS3711_SU2_VOLTAGE: 196de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0); 197de474b5bSGuennadi Liakhovetski break; 198de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR1: 199de474b5bSGuennadi Liakhovetski ctl = 1; 200de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1); 201de474b5bSGuennadi Liakhovetski break; 202de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR2: 203de474b5bSGuennadi Liakhovetski ctl = 4; 204de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2); 205de474b5bSGuennadi Liakhovetski break; 206de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR3: 207de474b5bSGuennadi Liakhovetski ctl = 0x10; 208de474b5bSGuennadi Liakhovetski ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3); 209de474b5bSGuennadi Liakhovetski break; 210de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR_AUTO: 211de474b5bSGuennadi Liakhovetski if (pdata->su2_auto_curr1) 212de474b5bSGuennadi Liakhovetski ctl = 2; 213de474b5bSGuennadi Liakhovetski if (pdata->su2_auto_curr2) 214de474b5bSGuennadi Liakhovetski ctl |= 8; 215de474b5bSGuennadi Liakhovetski if (pdata->su2_auto_curr3) 216de474b5bSGuennadi Liakhovetski ctl |= 0x20; 217de474b5bSGuennadi Liakhovetski ret = 0; 218de474b5bSGuennadi Liakhovetski break; 219de474b5bSGuennadi Liakhovetski default: 220de474b5bSGuennadi Liakhovetski return -EINVAL; 221de474b5bSGuennadi Liakhovetski } 222de474b5bSGuennadi Liakhovetski 223de474b5bSGuennadi Liakhovetski if (!ret) 224de474b5bSGuennadi Liakhovetski ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl); 225de474b5bSGuennadi Liakhovetski 226de474b5bSGuennadi Liakhovetski return ret; 227de474b5bSGuennadi Liakhovetski } 228de474b5bSGuennadi Liakhovetski 229de474b5bSGuennadi Liakhovetski static int as3711_bl_register(struct platform_device *pdev, 230de474b5bSGuennadi Liakhovetski unsigned int max_brightness, struct as3711_bl_data *su) 231de474b5bSGuennadi Liakhovetski { 232de474b5bSGuennadi Liakhovetski struct backlight_properties props = {.type = BACKLIGHT_RAW,}; 233de474b5bSGuennadi Liakhovetski struct backlight_device *bl; 234de474b5bSGuennadi Liakhovetski 235de474b5bSGuennadi Liakhovetski /* max tuning I = 31uA for voltage- and 38250uA for current-feedback */ 236de474b5bSGuennadi Liakhovetski props.max_brightness = max_brightness; 237de474b5bSGuennadi Liakhovetski 23883dedc05SJingoo Han bl = devm_backlight_device_register(&pdev->dev, 23983dedc05SJingoo Han su->type == AS3711_BL_SU1 ? 240de474b5bSGuennadi Liakhovetski "as3711-su1" : "as3711-su2", 241de474b5bSGuennadi Liakhovetski &pdev->dev, su, 242de474b5bSGuennadi Liakhovetski &as3711_bl_ops, &props); 243de474b5bSGuennadi Liakhovetski if (IS_ERR(bl)) { 244de474b5bSGuennadi Liakhovetski dev_err(&pdev->dev, "failed to register backlight\n"); 245de474b5bSGuennadi Liakhovetski return PTR_ERR(bl); 246de474b5bSGuennadi Liakhovetski } 247de474b5bSGuennadi Liakhovetski 248de474b5bSGuennadi Liakhovetski bl->props.brightness = props.max_brightness; 249de474b5bSGuennadi Liakhovetski 250de474b5bSGuennadi Liakhovetski backlight_update_status(bl); 251de474b5bSGuennadi Liakhovetski 252de474b5bSGuennadi Liakhovetski su->bl = bl; 253de474b5bSGuennadi Liakhovetski 254de474b5bSGuennadi Liakhovetski return 0; 255de474b5bSGuennadi Liakhovetski } 256de474b5bSGuennadi Liakhovetski 25759eb2b5eSGuennadi Liakhovetski static int as3711_backlight_parse_dt(struct device *dev) 25859eb2b5eSGuennadi Liakhovetski { 25959eb2b5eSGuennadi Liakhovetski struct as3711_bl_pdata *pdata = dev_get_platdata(dev); 2604a9c8bb2SJohan Hovold struct device_node *bl, *fb; 26159eb2b5eSGuennadi Liakhovetski int ret; 26259eb2b5eSGuennadi Liakhovetski 2634a9c8bb2SJohan Hovold bl = of_get_child_by_name(dev->parent->of_node, "backlight"); 26459eb2b5eSGuennadi Liakhovetski if (!bl) { 26559eb2b5eSGuennadi Liakhovetski dev_dbg(dev, "backlight node not found\n"); 26659eb2b5eSGuennadi Liakhovetski return -ENODEV; 26759eb2b5eSGuennadi Liakhovetski } 26859eb2b5eSGuennadi Liakhovetski 26959eb2b5eSGuennadi Liakhovetski fb = of_parse_phandle(bl, "su1-dev", 0); 27059eb2b5eSGuennadi Liakhovetski if (fb) { 271d5318d30SJohan Hovold of_node_put(fb); 272d5318d30SJohan Hovold 273d5318d30SJohan Hovold pdata->su1_fb = true; 27459eb2b5eSGuennadi Liakhovetski 27559eb2b5eSGuennadi Liakhovetski ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA); 27659eb2b5eSGuennadi Liakhovetski if (pdata->su1_max_uA <= 0) 27759eb2b5eSGuennadi Liakhovetski ret = -EINVAL; 27859eb2b5eSGuennadi Liakhovetski if (ret < 0) 2794a9c8bb2SJohan Hovold goto err_put_bl; 28059eb2b5eSGuennadi Liakhovetski } 28159eb2b5eSGuennadi Liakhovetski 28259eb2b5eSGuennadi Liakhovetski fb = of_parse_phandle(bl, "su2-dev", 0); 28359eb2b5eSGuennadi Liakhovetski if (fb) { 28459eb2b5eSGuennadi Liakhovetski int count = 0; 28559eb2b5eSGuennadi Liakhovetski 286d5318d30SJohan Hovold of_node_put(fb); 287d5318d30SJohan Hovold 288d5318d30SJohan Hovold pdata->su2_fb = true; 28959eb2b5eSGuennadi Liakhovetski 29059eb2b5eSGuennadi Liakhovetski ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA); 29159eb2b5eSGuennadi Liakhovetski if (pdata->su2_max_uA <= 0) 29259eb2b5eSGuennadi Liakhovetski ret = -EINVAL; 29359eb2b5eSGuennadi Liakhovetski if (ret < 0) 2944a9c8bb2SJohan Hovold goto err_put_bl; 29559eb2b5eSGuennadi Liakhovetski 29659eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-feedback-voltage", NULL)) { 29759eb2b5eSGuennadi Liakhovetski pdata->su2_feedback = AS3711_SU2_VOLTAGE; 29859eb2b5eSGuennadi Liakhovetski count++; 29959eb2b5eSGuennadi Liakhovetski } 30059eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-feedback-curr1", NULL)) { 30159eb2b5eSGuennadi Liakhovetski pdata->su2_feedback = AS3711_SU2_CURR1; 30259eb2b5eSGuennadi Liakhovetski count++; 30359eb2b5eSGuennadi Liakhovetski } 30459eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-feedback-curr2", NULL)) { 30559eb2b5eSGuennadi Liakhovetski pdata->su2_feedback = AS3711_SU2_CURR2; 30659eb2b5eSGuennadi Liakhovetski count++; 30759eb2b5eSGuennadi Liakhovetski } 30859eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-feedback-curr3", NULL)) { 30959eb2b5eSGuennadi Liakhovetski pdata->su2_feedback = AS3711_SU2_CURR3; 31059eb2b5eSGuennadi Liakhovetski count++; 31159eb2b5eSGuennadi Liakhovetski } 31259eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) { 31359eb2b5eSGuennadi Liakhovetski pdata->su2_feedback = AS3711_SU2_CURR_AUTO; 31459eb2b5eSGuennadi Liakhovetski count++; 31559eb2b5eSGuennadi Liakhovetski } 3164a9c8bb2SJohan Hovold if (count != 1) { 3174a9c8bb2SJohan Hovold ret = -EINVAL; 3184a9c8bb2SJohan Hovold goto err_put_bl; 3194a9c8bb2SJohan Hovold } 32059eb2b5eSGuennadi Liakhovetski 32159eb2b5eSGuennadi Liakhovetski count = 0; 32259eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) { 32359eb2b5eSGuennadi Liakhovetski pdata->su2_fbprot = AS3711_SU2_LX_SD4; 32459eb2b5eSGuennadi Liakhovetski count++; 32559eb2b5eSGuennadi Liakhovetski } 32659eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) { 32759eb2b5eSGuennadi Liakhovetski pdata->su2_fbprot = AS3711_SU2_GPIO2; 32859eb2b5eSGuennadi Liakhovetski count++; 32959eb2b5eSGuennadi Liakhovetski } 33059eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) { 33159eb2b5eSGuennadi Liakhovetski pdata->su2_fbprot = AS3711_SU2_GPIO3; 33259eb2b5eSGuennadi Liakhovetski count++; 33359eb2b5eSGuennadi Liakhovetski } 33459eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) { 33559eb2b5eSGuennadi Liakhovetski pdata->su2_fbprot = AS3711_SU2_GPIO4; 33659eb2b5eSGuennadi Liakhovetski count++; 33759eb2b5eSGuennadi Liakhovetski } 3384a9c8bb2SJohan Hovold if (count != 1) { 3394a9c8bb2SJohan Hovold ret = -EINVAL; 3404a9c8bb2SJohan Hovold goto err_put_bl; 3414a9c8bb2SJohan Hovold } 34259eb2b5eSGuennadi Liakhovetski 34359eb2b5eSGuennadi Liakhovetski count = 0; 34459eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-auto-curr1", NULL)) { 34559eb2b5eSGuennadi Liakhovetski pdata->su2_auto_curr1 = true; 34659eb2b5eSGuennadi Liakhovetski count++; 34759eb2b5eSGuennadi Liakhovetski } 34859eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-auto-curr2", NULL)) { 34959eb2b5eSGuennadi Liakhovetski pdata->su2_auto_curr2 = true; 35059eb2b5eSGuennadi Liakhovetski count++; 35159eb2b5eSGuennadi Liakhovetski } 35259eb2b5eSGuennadi Liakhovetski if (of_find_property(bl, "su2-auto-curr3", NULL)) { 35359eb2b5eSGuennadi Liakhovetski pdata->su2_auto_curr3 = true; 35459eb2b5eSGuennadi Liakhovetski count++; 35559eb2b5eSGuennadi Liakhovetski } 35659eb2b5eSGuennadi Liakhovetski 35759eb2b5eSGuennadi Liakhovetski /* 35859eb2b5eSGuennadi Liakhovetski * At least one su2-auto-curr* must be specified iff 35959eb2b5eSGuennadi Liakhovetski * AS3711_SU2_CURR_AUTO is used 36059eb2b5eSGuennadi Liakhovetski */ 3614a9c8bb2SJohan Hovold if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO)) { 3624a9c8bb2SJohan Hovold ret = -EINVAL; 3634a9c8bb2SJohan Hovold goto err_put_bl; 3644a9c8bb2SJohan Hovold } 36559eb2b5eSGuennadi Liakhovetski } 36659eb2b5eSGuennadi Liakhovetski 3674a9c8bb2SJohan Hovold of_node_put(bl); 3684a9c8bb2SJohan Hovold 36959eb2b5eSGuennadi Liakhovetski return 0; 3704a9c8bb2SJohan Hovold 3714a9c8bb2SJohan Hovold err_put_bl: 3724a9c8bb2SJohan Hovold of_node_put(bl); 3734a9c8bb2SJohan Hovold 3744a9c8bb2SJohan Hovold return ret; 37559eb2b5eSGuennadi Liakhovetski } 37659eb2b5eSGuennadi Liakhovetski 377de474b5bSGuennadi Liakhovetski static int as3711_backlight_probe(struct platform_device *pdev) 378de474b5bSGuennadi Liakhovetski { 379de474b5bSGuennadi Liakhovetski struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev); 380de474b5bSGuennadi Liakhovetski struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent); 381de474b5bSGuennadi Liakhovetski struct as3711_bl_supply *supply; 382de474b5bSGuennadi Liakhovetski struct as3711_bl_data *su; 383de474b5bSGuennadi Liakhovetski unsigned int max_brightness; 384de474b5bSGuennadi Liakhovetski int ret; 385de474b5bSGuennadi Liakhovetski 38659eb2b5eSGuennadi Liakhovetski if (!pdata) { 387de474b5bSGuennadi Liakhovetski dev_err(&pdev->dev, "No platform data, exiting...\n"); 388de474b5bSGuennadi Liakhovetski return -ENODEV; 389de474b5bSGuennadi Liakhovetski } 390de474b5bSGuennadi Liakhovetski 39159eb2b5eSGuennadi Liakhovetski if (pdev->dev.parent->of_node) { 39259eb2b5eSGuennadi Liakhovetski ret = as3711_backlight_parse_dt(&pdev->dev); 39359eb2b5eSGuennadi Liakhovetski if (ret < 0) { 39459eb2b5eSGuennadi Liakhovetski dev_err(&pdev->dev, "DT parsing failed: %d\n", ret); 39559eb2b5eSGuennadi Liakhovetski return ret; 39659eb2b5eSGuennadi Liakhovetski } 39759eb2b5eSGuennadi Liakhovetski } 39859eb2b5eSGuennadi Liakhovetski 39959eb2b5eSGuennadi Liakhovetski if (!pdata->su1_fb && !pdata->su2_fb) { 40059eb2b5eSGuennadi Liakhovetski dev_err(&pdev->dev, "No framebuffer specified\n"); 40159eb2b5eSGuennadi Liakhovetski return -EINVAL; 40259eb2b5eSGuennadi Liakhovetski } 40359eb2b5eSGuennadi Liakhovetski 404de474b5bSGuennadi Liakhovetski /* 405de474b5bSGuennadi Liakhovetski * Due to possible hardware damage I chose to block all modes, 406de474b5bSGuennadi Liakhovetski * unsupported on my hardware. Anyone, wishing to use any of those modes 407de474b5bSGuennadi Liakhovetski * will have to first review the code, then activate and test it. 408de474b5bSGuennadi Liakhovetski */ 409de474b5bSGuennadi Liakhovetski if (pdata->su1_fb || 410de474b5bSGuennadi Liakhovetski pdata->su2_fbprot != AS3711_SU2_GPIO4 || 411de474b5bSGuennadi Liakhovetski pdata->su2_feedback != AS3711_SU2_CURR_AUTO) { 412de474b5bSGuennadi Liakhovetski dev_warn(&pdev->dev, 413de474b5bSGuennadi Liakhovetski "Attention! An untested mode has been chosen!\n" 414de474b5bSGuennadi Liakhovetski "Please, review the code, enable, test, and report success:-)\n"); 415de474b5bSGuennadi Liakhovetski return -EINVAL; 416de474b5bSGuennadi Liakhovetski } 417de474b5bSGuennadi Liakhovetski 418de474b5bSGuennadi Liakhovetski supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL); 419de474b5bSGuennadi Liakhovetski if (!supply) 420de474b5bSGuennadi Liakhovetski return -ENOMEM; 421de474b5bSGuennadi Liakhovetski 422de474b5bSGuennadi Liakhovetski supply->as3711 = as3711; 423de474b5bSGuennadi Liakhovetski supply->pdata = pdata; 424de474b5bSGuennadi Liakhovetski 425de474b5bSGuennadi Liakhovetski if (pdata->su1_fb) { 426de474b5bSGuennadi Liakhovetski su = &supply->su1; 427de474b5bSGuennadi Liakhovetski su->type = AS3711_BL_SU1; 428de474b5bSGuennadi Liakhovetski 429de474b5bSGuennadi Liakhovetski max_brightness = min(pdata->su1_max_uA, 31); 430de474b5bSGuennadi Liakhovetski ret = as3711_bl_register(pdev, max_brightness, su); 431de474b5bSGuennadi Liakhovetski if (ret < 0) 432de474b5bSGuennadi Liakhovetski return ret; 433de474b5bSGuennadi Liakhovetski } 434de474b5bSGuennadi Liakhovetski 435de474b5bSGuennadi Liakhovetski if (pdata->su2_fb) { 436de474b5bSGuennadi Liakhovetski su = &supply->su2; 437de474b5bSGuennadi Liakhovetski su->type = AS3711_BL_SU2; 438de474b5bSGuennadi Liakhovetski 439de474b5bSGuennadi Liakhovetski switch (pdata->su2_fbprot) { 440de474b5bSGuennadi Liakhovetski case AS3711_SU2_GPIO2: 441de474b5bSGuennadi Liakhovetski case AS3711_SU2_GPIO3: 442de474b5bSGuennadi Liakhovetski case AS3711_SU2_GPIO4: 443de474b5bSGuennadi Liakhovetski case AS3711_SU2_LX_SD4: 444de474b5bSGuennadi Liakhovetski break; 445de474b5bSGuennadi Liakhovetski default: 44683dedc05SJingoo Han return -EINVAL; 447de474b5bSGuennadi Liakhovetski } 448de474b5bSGuennadi Liakhovetski 449de474b5bSGuennadi Liakhovetski switch (pdata->su2_feedback) { 450de474b5bSGuennadi Liakhovetski case AS3711_SU2_VOLTAGE: 451de474b5bSGuennadi Liakhovetski max_brightness = min(pdata->su2_max_uA, 31); 452de474b5bSGuennadi Liakhovetski break; 453de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR1: 454de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR2: 455de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR3: 456de474b5bSGuennadi Liakhovetski case AS3711_SU2_CURR_AUTO: 457de474b5bSGuennadi Liakhovetski max_brightness = min(pdata->su2_max_uA / 150, 255); 458de474b5bSGuennadi Liakhovetski break; 459de474b5bSGuennadi Liakhovetski default: 46083dedc05SJingoo Han return -EINVAL; 461de474b5bSGuennadi Liakhovetski } 462de474b5bSGuennadi Liakhovetski 463de474b5bSGuennadi Liakhovetski ret = as3711_bl_init_su2(supply); 464de474b5bSGuennadi Liakhovetski if (ret < 0) 465de474b5bSGuennadi Liakhovetski return ret; 466de474b5bSGuennadi Liakhovetski 467de474b5bSGuennadi Liakhovetski ret = as3711_bl_register(pdev, max_brightness, su); 468de474b5bSGuennadi Liakhovetski if (ret < 0) 469de474b5bSGuennadi Liakhovetski return ret; 470de474b5bSGuennadi Liakhovetski } 471de474b5bSGuennadi Liakhovetski 47283dedc05SJingoo Han platform_set_drvdata(pdev, supply); 473de474b5bSGuennadi Liakhovetski 474de474b5bSGuennadi Liakhovetski return 0; 475de474b5bSGuennadi Liakhovetski } 476de474b5bSGuennadi Liakhovetski 477de474b5bSGuennadi Liakhovetski static struct platform_driver as3711_backlight_driver = { 478de474b5bSGuennadi Liakhovetski .driver = { 479de474b5bSGuennadi Liakhovetski .name = "as3711-backlight", 480de474b5bSGuennadi Liakhovetski }, 481de474b5bSGuennadi Liakhovetski .probe = as3711_backlight_probe, 482de474b5bSGuennadi Liakhovetski }; 483de474b5bSGuennadi Liakhovetski 484de474b5bSGuennadi Liakhovetski module_platform_driver(as3711_backlight_driver); 485de474b5bSGuennadi Liakhovetski 486de474b5bSGuennadi Liakhovetski MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs"); 487de474b5bSGuennadi Liakhovetski MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de"); 488*6f6d5c3fSKuninori Morimoto MODULE_LICENSE("GPL v2"); 489de474b5bSGuennadi Liakhovetski MODULE_ALIAS("platform:as3711-backlight"); 490