brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · e6f66bb Raw
481 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * AS3711 PMIC backlight driver, using DCDC Step Up Converters4 *5 * Copyright (C) 2012 Renesas Electronics Corporation6 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>7 */8 9#include <linux/backlight.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/err.h>13#include <linux/fb.h>14#include <linux/kernel.h>15#include <linux/mfd/as3711.h>16#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/regmap.h>19#include <linux/slab.h>20 21enum as3711_bl_type {22	AS3711_BL_SU1,23	AS3711_BL_SU2,24};25 26struct as3711_bl_data {27	bool powered;28	enum as3711_bl_type type;29	int brightness;30	struct backlight_device *bl;31};32 33struct as3711_bl_supply {34	struct as3711_bl_data su1;35	struct as3711_bl_data su2;36	const struct as3711_bl_pdata *pdata;37	struct as3711 *as3711;38};39 40static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su)41{42	switch (su->type) {43	case AS3711_BL_SU1:44		return container_of(su, struct as3711_bl_supply, su1);45	case AS3711_BL_SU2:46		return container_of(su, struct as3711_bl_supply, su2);47	}48	return NULL;49}50 51static int as3711_set_brightness_auto_i(struct as3711_bl_data *data,52					unsigned int brightness)53{54	struct as3711_bl_supply *supply = to_supply(data);55	struct as3711 *as3711 = supply->as3711;56	const struct as3711_bl_pdata *pdata = supply->pdata;57	int ret = 0;58 59	/* Only all equal current values are supported */60	if (pdata->su2_auto_curr1)61		ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,62				   brightness);63	if (!ret && pdata->su2_auto_curr2)64		ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,65				   brightness);66	if (!ret && pdata->su2_auto_curr3)67		ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,68				   brightness);69 70	return ret;71}72 73static int as3711_set_brightness_v(struct as3711 *as3711,74				   unsigned int brightness,75				   unsigned int reg)76{77	if (brightness > 31)78		return -EINVAL;79 80	return regmap_update_bits(as3711->regmap, reg, 0xf0,81				  brightness << 4);82}83 84static int as3711_bl_su2_reset(struct as3711_bl_supply *supply)85{86	struct as3711 *as3711 = supply->as3711;87	int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5,88				     3, supply->pdata->su2_fbprot);89	if (!ret)90		ret = regmap_update_bits(as3711->regmap,91					 AS3711_STEPUP_CONTROL_2, 1, 0);92	if (!ret)93		ret = regmap_update_bits(as3711->regmap,94					 AS3711_STEPUP_CONTROL_2, 1, 1);95	return ret;96}97 98/*99 * Someone with less fragile or less expensive hardware could try to simplify100 * the brightness adjustment procedure.101 */102static int as3711_bl_update_status(struct backlight_device *bl)103{104	struct as3711_bl_data *data = bl_get_data(bl);105	struct as3711_bl_supply *supply = to_supply(data);106	struct as3711 *as3711 = supply->as3711;107	int brightness;108	int ret = 0;109 110	brightness = backlight_get_brightness(bl);111 112	if (data->type == AS3711_BL_SU1) {113		ret = as3711_set_brightness_v(as3711, brightness,114					      AS3711_STEPUP_CONTROL_1);115	} else {116		const struct as3711_bl_pdata *pdata = supply->pdata;117 118		switch (pdata->su2_feedback) {119		case AS3711_SU2_VOLTAGE:120			ret = as3711_set_brightness_v(as3711, brightness,121						      AS3711_STEPUP_CONTROL_2);122			break;123		case AS3711_SU2_CURR_AUTO:124			ret = as3711_set_brightness_auto_i(data, brightness / 4);125			if (ret < 0)126				return ret;127			if (brightness) {128				ret = as3711_bl_su2_reset(supply);129				if (ret < 0)130					return ret;131				udelay(500);132				ret = as3711_set_brightness_auto_i(data, brightness);133			} else {134				ret = regmap_update_bits(as3711->regmap,135						AS3711_STEPUP_CONTROL_2, 1, 0);136			}137			break;138		/* Manual one current feedback pin below */139		case AS3711_SU2_CURR1:140			ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,141					   brightness);142			break;143		case AS3711_SU2_CURR2:144			ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,145					   brightness);146			break;147		case AS3711_SU2_CURR3:148			ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,149					   brightness);150			break;151		default:152			ret = -EINVAL;153		}154	}155	if (!ret)156		data->brightness = brightness;157 158	return ret;159}160 161static int as3711_bl_get_brightness(struct backlight_device *bl)162{163	struct as3711_bl_data *data = bl_get_data(bl);164 165	return data->brightness;166}167 168static const struct backlight_ops as3711_bl_ops = {169	.update_status	= as3711_bl_update_status,170	.get_brightness	= as3711_bl_get_brightness,171};172 173static int as3711_bl_init_su2(struct as3711_bl_supply *supply)174{175	struct as3711 *as3711 = supply->as3711;176	const struct as3711_bl_pdata *pdata = supply->pdata;177	u8 ctl = 0;178	int ret;179 180	dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback);181 182	/* Turn SU2 off */183	ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0);184	if (ret < 0)185		return ret;186 187	switch (pdata->su2_feedback) {188	case AS3711_SU2_VOLTAGE:189		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0);190		break;191	case AS3711_SU2_CURR1:192		ctl = 1;193		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1);194		break;195	case AS3711_SU2_CURR2:196		ctl = 4;197		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2);198		break;199	case AS3711_SU2_CURR3:200		ctl = 0x10;201		ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3);202		break;203	case AS3711_SU2_CURR_AUTO:204		if (pdata->su2_auto_curr1)205			ctl = 2;206		if (pdata->su2_auto_curr2)207			ctl |= 8;208		if (pdata->su2_auto_curr3)209			ctl |= 0x20;210		ret = 0;211		break;212	default:213		return -EINVAL;214	}215 216	if (!ret)217		ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl);218 219	return ret;220}221 222static int as3711_bl_register(struct platform_device *pdev,223			      unsigned int max_brightness, struct as3711_bl_data *su)224{225	struct backlight_properties props = {.type = BACKLIGHT_RAW,};226	struct backlight_device *bl;227 228	/* max tuning I = 31uA for voltage- and 38250uA for current-feedback */229	props.max_brightness = max_brightness;230 231	bl = devm_backlight_device_register(&pdev->dev,232				       su->type == AS3711_BL_SU1 ?233				       "as3711-su1" : "as3711-su2",234				       &pdev->dev, su,235				       &as3711_bl_ops, &props);236	if (IS_ERR(bl)) {237		dev_err(&pdev->dev, "failed to register backlight\n");238		return PTR_ERR(bl);239	}240 241	bl->props.brightness = props.max_brightness;242 243	backlight_update_status(bl);244 245	su->bl = bl;246 247	return 0;248}249 250static int as3711_backlight_parse_dt(struct device *dev)251{252	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);253	struct device_node *bl, *fb;254	int ret;255 256	bl = of_get_child_by_name(dev->parent->of_node, "backlight");257	if (!bl) {258		dev_dbg(dev, "backlight node not found\n");259		return -ENODEV;260	}261 262	fb = of_parse_phandle(bl, "su1-dev", 0);263	if (fb) {264		of_node_put(fb);265 266		pdata->su1_fb = true;267 268		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);269		if (pdata->su1_max_uA <= 0)270			ret = -EINVAL;271		if (ret < 0)272			goto err_put_bl;273	}274 275	fb = of_parse_phandle(bl, "su2-dev", 0);276	if (fb) {277		int count = 0;278 279		of_node_put(fb);280 281		pdata->su2_fb = true;282 283		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);284		if (pdata->su2_max_uA <= 0)285			ret = -EINVAL;286		if (ret < 0)287			goto err_put_bl;288 289		if (of_property_read_bool(bl, "su2-feedback-voltage")) {290			pdata->su2_feedback = AS3711_SU2_VOLTAGE;291			count++;292		}293		if (of_property_read_bool(bl, "su2-feedback-curr1")) {294			pdata->su2_feedback = AS3711_SU2_CURR1;295			count++;296		}297		if (of_property_read_bool(bl, "su2-feedback-curr2")) {298			pdata->su2_feedback = AS3711_SU2_CURR2;299			count++;300		}301		if (of_property_read_bool(bl, "su2-feedback-curr3")) {302			pdata->su2_feedback = AS3711_SU2_CURR3;303			count++;304		}305		if (of_property_read_bool(bl, "su2-feedback-curr-auto")) {306			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;307			count++;308		}309		if (count != 1) {310			ret = -EINVAL;311			goto err_put_bl;312		}313 314		count = 0;315		if (of_property_read_bool(bl, "su2-fbprot-lx-sd4")) {316			pdata->su2_fbprot = AS3711_SU2_LX_SD4;317			count++;318		}319		if (of_property_read_bool(bl, "su2-fbprot-gpio2")) {320			pdata->su2_fbprot = AS3711_SU2_GPIO2;321			count++;322		}323		if (of_property_read_bool(bl, "su2-fbprot-gpio3")) {324			pdata->su2_fbprot = AS3711_SU2_GPIO3;325			count++;326		}327		if (of_property_read_bool(bl, "su2-fbprot-gpio4")) {328			pdata->su2_fbprot = AS3711_SU2_GPIO4;329			count++;330		}331		if (count != 1) {332			ret = -EINVAL;333			goto err_put_bl;334		}335 336		count = 0;337		if (of_property_read_bool(bl, "su2-auto-curr1")) {338			pdata->su2_auto_curr1 = true;339			count++;340		}341		if (of_property_read_bool(bl, "su2-auto-curr2")) {342			pdata->su2_auto_curr2 = true;343			count++;344		}345		if (of_property_read_bool(bl, "su2-auto-curr3")) {346			pdata->su2_auto_curr3 = true;347			count++;348		}349 350		/*351		 * At least one su2-auto-curr* must be specified iff352		 * AS3711_SU2_CURR_AUTO is used353		 */354		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO)) {355			ret = -EINVAL;356			goto err_put_bl;357		}358	}359 360	of_node_put(bl);361 362	return 0;363 364err_put_bl:365	of_node_put(bl);366 367	return ret;368}369 370static int as3711_backlight_probe(struct platform_device *pdev)371{372	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);373	struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);374	struct as3711_bl_supply *supply;375	struct as3711_bl_data *su;376	unsigned int max_brightness;377	int ret;378 379	if (!pdata) {380		dev_err(&pdev->dev, "No platform data, exiting...\n");381		return -ENODEV;382	}383 384	if (pdev->dev.parent->of_node) {385		ret = as3711_backlight_parse_dt(&pdev->dev);386		if (ret < 0)387			return dev_err_probe(&pdev->dev, ret, "DT parsing failed\n");388	}389 390	if (!pdata->su1_fb && !pdata->su2_fb) {391		dev_err(&pdev->dev, "No framebuffer specified\n");392		return -EINVAL;393	}394 395	/*396	 * Due to possible hardware damage I chose to block all modes,397	 * unsupported on my hardware. Anyone, wishing to use any of those modes398	 * will have to first review the code, then activate and test it.399	 */400	if (pdata->su1_fb ||401	    pdata->su2_fbprot != AS3711_SU2_GPIO4 ||402	    pdata->su2_feedback != AS3711_SU2_CURR_AUTO) {403		dev_warn(&pdev->dev,404			 "Attention! An untested mode has been chosen!\n"405			 "Please, review the code, enable, test, and report success:-)\n");406		return -EINVAL;407	}408 409	supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);410	if (!supply)411		return -ENOMEM;412 413	supply->as3711 = as3711;414	supply->pdata = pdata;415 416	if (pdata->su1_fb) {417		su = &supply->su1;418		su->type = AS3711_BL_SU1;419 420		max_brightness = min(pdata->su1_max_uA, 31);421		ret = as3711_bl_register(pdev, max_brightness, su);422		if (ret < 0)423			return ret;424	}425 426	if (pdata->su2_fb) {427		su = &supply->su2;428		su->type = AS3711_BL_SU2;429 430		switch (pdata->su2_fbprot) {431		case AS3711_SU2_GPIO2:432		case AS3711_SU2_GPIO3:433		case AS3711_SU2_GPIO4:434		case AS3711_SU2_LX_SD4:435			break;436		default:437			return -EINVAL;438		}439 440		switch (pdata->su2_feedback) {441		case AS3711_SU2_VOLTAGE:442			max_brightness = min(pdata->su2_max_uA, 31);443			break;444		case AS3711_SU2_CURR1:445		case AS3711_SU2_CURR2:446		case AS3711_SU2_CURR3:447		case AS3711_SU2_CURR_AUTO:448			max_brightness = min(pdata->su2_max_uA / 150, 255);449			break;450		default:451			return -EINVAL;452		}453 454		ret = as3711_bl_init_su2(supply);455		if (ret < 0)456			return ret;457 458		ret = as3711_bl_register(pdev, max_brightness, su);459		if (ret < 0)460			return ret;461	}462 463	platform_set_drvdata(pdev, supply);464 465	return 0;466}467 468static struct platform_driver as3711_backlight_driver = {469	.driver		= {470		.name	= "as3711-backlight",471	},472	.probe		= as3711_backlight_probe,473};474 475module_platform_driver(as3711_backlight_driver);476 477MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs");478MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de");479MODULE_LICENSE("GPL v2");480MODULE_ALIAS("platform:as3711-backlight");481