pwm_bl.c 11.3 KB
Newer Older
1 2 3 4 5
/*
 * linux/drivers/video/backlight/pwm_bl.c
 *
 * simple PWM based backlight control, board code has to setup
 * 1) pin configuration so PWM waveforms can output
6
 * 2) platform_data being correctly configured
7 8 9 10 11 12
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

13
#include <linux/gpio/consumer.h>
14
#include <linux/gpio.h>
15 16 17 18 19 20 21 22 23
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
24
#include <linux/regulator/consumer.h>
25
#include <linux/slab.h>
26 27 28

struct pwm_bl_data {
	struct pwm_device	*pwm;
29
	struct device		*dev;
30
	unsigned int		period;
31
	unsigned int		lth_brightness;
32
	unsigned int		*levels;
T
Thierry Reding 已提交
33
	bool			enabled;
34
	struct regulator	*power_supply;
35
	struct gpio_desc	*enable_gpio;
36
	unsigned int		scale;
37
	bool			legacy;
38 39
	int			(*notify)(struct device *,
					  int brightness);
40 41
	void			(*notify_after)(struct device *,
					int brightness);
42
	int			(*check_fb)(struct device *, struct fb_info *);
43
	void			(*exit)(struct device *);
44 45
};

46
static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
47
{
48
	int err;
49

T
Thierry Reding 已提交
50 51 52
	if (pb->enabled)
		return;

53 54 55 56
	err = regulator_enable(pb->power_supply);
	if (err < 0)
		dev_err(pb->dev, "failed to enable power supply\n");

57
	if (pb->enable_gpio)
58
		gpiod_set_value_cansleep(pb->enable_gpio, 1);
59

60
	pwm_enable(pb->pwm);
T
Thierry Reding 已提交
61
	pb->enabled = true;
62 63 64 65
}

static void pwm_backlight_power_off(struct pwm_bl_data *pb)
{
T
Thierry Reding 已提交
66 67 68
	if (!pb->enabled)
		return;

69 70
	pwm_config(pb->pwm, 0, pb->period);
	pwm_disable(pb->pwm);
T
Thierry Reding 已提交
71

72
	if (pb->enable_gpio)
73
		gpiod_set_value_cansleep(pb->enable_gpio, 0);
74

75
	regulator_disable(pb->power_supply);
T
Thierry Reding 已提交
76
	pb->enabled = false;
77 78
}

79 80 81 82 83 84 85 86 87 88 89 90 91
static int compute_duty_cycle(struct pwm_bl_data *pb, int brightness)
{
	unsigned int lth = pb->lth_brightness;
	int duty_cycle;

	if (pb->levels)
		duty_cycle = pb->levels[brightness];
	else
		duty_cycle = brightness;

	return (duty_cycle * (pb->period - lth) / pb->scale) + lth;
}

92 93
static int pwm_backlight_update_status(struct backlight_device *bl)
{
94
	struct pwm_bl_data *pb = bl_get_data(bl);
95
	int brightness = bl->props.brightness;
96
	int duty_cycle;
97

98 99 100
	if (bl->props.power != FB_BLANK_UNBLANK ||
	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
	    bl->props.state & BL_CORE_FBBLANK)
101 102
		brightness = 0;

103
	if (pb->notify)
104
		brightness = pb->notify(pb->dev, brightness);
105

106 107
	if (brightness > 0) {
		duty_cycle = compute_duty_cycle(pb, brightness);
108
		pwm_config(pb->pwm, duty_cycle, pb->period);
109
		pwm_backlight_power_on(pb, brightness);
110
	} else
111
		pwm_backlight_power_off(pb);
112 113 114 115

	if (pb->notify_after)
		pb->notify_after(pb->dev, brightness);

116 117 118
	return 0;
}

119 120 121
static int pwm_backlight_check_fb(struct backlight_device *bl,
				  struct fb_info *info)
{
122
	struct pwm_bl_data *pb = bl_get_data(bl);
123 124 125 126

	return !pb->check_fb || pb->check_fb(pb->dev, info);
}

127
static const struct backlight_ops pwm_backlight_ops = {
128
	.update_status	= pwm_backlight_update_status,
129
	.check_fb	= pwm_backlight_check_fb,
130 131
};

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
#ifdef CONFIG_OF
static int pwm_backlight_parse_dt(struct device *dev,
				  struct platform_pwm_backlight_data *data)
{
	struct device_node *node = dev->of_node;
	struct property *prop;
	int length;
	u32 value;
	int ret;

	if (!node)
		return -ENODEV;

	memset(data, 0, sizeof(*data));

	/* determine the number of brightness levels */
	prop = of_find_property(node, "brightness-levels", &length);
	if (!prop)
		return -EINVAL;

	data->max_brightness = length / sizeof(u32);

	/* read brightness levels from DT property */
	if (data->max_brightness > 0) {
		size_t size = sizeof(*data->levels) * data->max_brightness;

		data->levels = devm_kzalloc(dev, size, GFP_KERNEL);
		if (!data->levels)
			return -ENOMEM;

		ret = of_property_read_u32_array(node, "brightness-levels",
						 data->levels,
						 data->max_brightness);
		if (ret < 0)
			return ret;

		ret = of_property_read_u32(node, "default-brightness-level",
					   &value);
		if (ret < 0)
			return ret;

		data->dft_brightness = value;
		data->max_brightness--;
	}

177
	data->enable_gpio = -EINVAL;
178 179 180
	return 0;
}

181
static const struct of_device_id pwm_backlight_of_match[] = {
182 183 184 185 186 187 188 189 190 191 192 193 194
	{ .compatible = "pwm-backlight" },
	{ }
};

MODULE_DEVICE_TABLE(of, pwm_backlight_of_match);
#else
static int pwm_backlight_parse_dt(struct device *dev,
				  struct platform_pwm_backlight_data *data)
{
	return -ENODEV;
}
#endif

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
static int pwm_backlight_initial_power_state(const struct pwm_bl_data *pb)
{
	struct device_node *node = pb->dev->of_node;

	/* Not booted with device tree or no phandle link to the node */
	if (!node || !node->phandle)
		return FB_BLANK_UNBLANK;

	/*
	 * If the driver is probed from the device tree and there is a
	 * phandle link pointing to the backlight node, it is safe to
	 * assume that another driver will enable the backlight at the
	 * appropriate time. Therefore, if it is disabled, keep it so.
	 */

	/* if the enable GPIO is disabled, do not enable the backlight */
	if (pb->enable_gpio && gpiod_get_value(pb->enable_gpio) == 0)
		return FB_BLANK_POWERDOWN;

	/* The regulator is disabled, do not enable the backlight */
	if (!regulator_is_enabled(pb->power_supply))
		return FB_BLANK_POWERDOWN;

218 219 220 221
	/* The PWM is disabled, keep it like this */
	if (!pwm_is_enabled(pb->pwm))
		return FB_BLANK_POWERDOWN;

222 223 224
	return FB_BLANK_UNBLANK;
}

225 226
static int pwm_backlight_probe(struct platform_device *pdev)
{
J
Jingoo Han 已提交
227
	struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
228 229
	struct platform_pwm_backlight_data defdata;
	struct backlight_properties props;
230
	struct backlight_device *bl;
231
	struct device_node *node = pdev->dev.of_node;
232
	struct pwm_bl_data *pb;
233
	struct pwm_args pargs;
234
	int ret;
235

236
	if (!data) {
237 238 239 240 241 242 243
		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to find platform data\n");
			return ret;
		}

		data = &defdata;
244
	}
245

246 247 248 249 250 251
	if (data->init) {
		ret = data->init(&pdev->dev);
		if (ret < 0)
			return ret;
	}

252
	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
253 254 255 256
	if (!pb) {
		ret = -ENOMEM;
		goto err_alloc;
	}
257

258
	if (data->levels) {
259 260 261 262 263 264
		unsigned int i;

		for (i = 0; i <= data->max_brightness; i++)
			if (data->levels[i] > pb->scale)
				pb->scale = data->levels[i];

265 266
		pb->levels = data->levels;
	} else
267
		pb->scale = data->max_brightness;
268

269
	pb->notify = data->notify;
270
	pb->notify_after = data->notify_after;
271
	pb->check_fb = data->check_fb;
272
	pb->exit = data->exit;
273
	pb->dev = &pdev->dev;
T
Thierry Reding 已提交
274
	pb->enabled = false;
275

276
	pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
277
						  GPIOD_ASIS);
278 279
	if (IS_ERR(pb->enable_gpio)) {
		ret = PTR_ERR(pb->enable_gpio);
280
		goto err_alloc;
281
	}
282

283 284 285 286 287 288 289
	/*
	 * Compatibility fallback for drivers still using the integer GPIO
	 * platform data. Must go away soon.
	 */
	if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
		ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,
					    GPIOF_OUT_INIT_HIGH, "enable");
290 291
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
292
				data->enable_gpio, ret);
293 294
			goto err_alloc;
		}
295 296

		pb->enable_gpio = gpio_to_desc(data->enable_gpio);
297 298
	}

299
	/*
300 301 302
	 * If the GPIO is not known to be already configured as output, that
	 * is, if gpiod_get_direction returns either GPIOF_DIR_IN or -EINVAL,
	 * change the direction to output and set the GPIO as active.
303 304 305 306 307
	 * Do not force the GPIO to active when it was already output as it
	 * could cause backlight flickering or we would enable the backlight too
	 * early. Leave the decision of the initial backlight state for later.
	 */
	if (pb->enable_gpio &&
308
	    gpiod_get_direction(pb->enable_gpio) != GPIOF_DIR_OUT)
309
		gpiod_direction_output(pb->enable_gpio, 1);
310

311 312 313
	pb->power_supply = devm_regulator_get(&pdev->dev, "power");
	if (IS_ERR(pb->power_supply)) {
		ret = PTR_ERR(pb->power_supply);
314
		goto err_alloc;
315
	}
316

317
	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
318
	if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
319
		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
320
		pb->legacy = true;
321
		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
322 323 324 325 326 327 328
	}

	if (IS_ERR(pb->pwm)) {
		ret = PTR_ERR(pb->pwm);
		if (ret != -EPROBE_DEFER)
			dev_err(&pdev->dev, "unable to request PWM\n");
		goto err_alloc;
329 330 331 332
	}

	dev_dbg(&pdev->dev, "got pwm for backlight\n");

333 334 335 336 337 338
	/*
	 * FIXME: pwm_apply_args() should be removed when switching to
	 * the atomic PWM API.
	 */
	pwm_apply_args(pb->pwm);

339 340 341
	/*
	 * The DT case will set the pwm_period_ns field to 0 and store the
	 * period, parsed from the DT, in the PWM device. For the non-DT case,
342 343
	 * set the period from platform data if it has not already been set
	 * via the PWM lookup table.
344
	 */
345 346
	pwm_get_args(pb->pwm, &pargs);
	pb->period = pargs.period;
347
	if (!pb->period && (data->pwm_period_ns > 0))
348
		pb->period = data->pwm_period_ns;
349

350
	pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
351

352
	memset(&props, 0, sizeof(struct backlight_properties));
M
Matthew Garrett 已提交
353
	props.type = BACKLIGHT_RAW;
354 355 356
	props.max_brightness = data->max_brightness;
	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
				       &pwm_backlight_ops, &props);
357 358
	if (IS_ERR(bl)) {
		dev_err(&pdev->dev, "failed to register backlight\n");
359
		ret = PTR_ERR(bl);
360 361
		if (pb->legacy)
			pwm_free(pb->pwm);
362
		goto err_alloc;
363 364
	}

365 366 367 368 369 370 371
	if (data->dft_brightness > data->max_brightness) {
		dev_warn(&pdev->dev,
			 "invalid default brightness level: %u, using %u\n",
			 data->dft_brightness, data->max_brightness);
		data->dft_brightness = data->max_brightness;
	}

372
	bl->props.brightness = data->dft_brightness;
373
	bl->props.power = pwm_backlight_initial_power_state(pb);
374 375 376 377
	backlight_update_status(bl);

	platform_set_drvdata(pdev, bl);
	return 0;
378 379 380 381 382

err_alloc:
	if (data->exit)
		data->exit(&pdev->dev);
	return ret;
383 384 385 386 387
}

static int pwm_backlight_remove(struct platform_device *pdev)
{
	struct backlight_device *bl = platform_get_drvdata(pdev);
388
	struct pwm_bl_data *pb = bl_get_data(bl);
389 390

	backlight_device_unregister(bl);
391
	pwm_backlight_power_off(pb);
392

393 394
	if (pb->exit)
		pb->exit(&pdev->dev);
395 396
	if (pb->legacy)
		pwm_free(pb->pwm);
397

398 399 400
	return 0;
}

401 402 403 404 405 406 407 408
static void pwm_backlight_shutdown(struct platform_device *pdev)
{
	struct backlight_device *bl = platform_get_drvdata(pdev);
	struct pwm_bl_data *pb = bl_get_data(bl);

	pwm_backlight_power_off(pb);
}

409
#ifdef CONFIG_PM_SLEEP
410
static int pwm_backlight_suspend(struct device *dev)
411
{
412
	struct backlight_device *bl = dev_get_drvdata(dev);
413
	struct pwm_bl_data *pb = bl_get_data(bl);
414

415
	if (pb->notify)
416
		pb->notify(pb->dev, 0);
417

418
	pwm_backlight_power_off(pb);
419

420 421
	if (pb->notify_after)
		pb->notify_after(pb->dev, 0);
422

423 424 425
	return 0;
}

426
static int pwm_backlight_resume(struct device *dev)
427
{
428
	struct backlight_device *bl = dev_get_drvdata(dev);
429 430

	backlight_update_status(bl);
431

432 433
	return 0;
}
434
#endif
435

436 437 438 439 440 441 442 443
static const struct dev_pm_ops pwm_backlight_pm_ops = {
#ifdef CONFIG_PM_SLEEP
	.suspend = pwm_backlight_suspend,
	.resume = pwm_backlight_resume,
	.poweroff = pwm_backlight_suspend,
	.restore = pwm_backlight_resume,
#endif
};
444

445 446
static struct platform_driver pwm_backlight_driver = {
	.driver		= {
447 448 449
		.name		= "pwm-backlight",
		.pm		= &pwm_backlight_pm_ops,
		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
450 451 452
	},
	.probe		= pwm_backlight_probe,
	.remove		= pwm_backlight_remove,
453
	.shutdown	= pwm_backlight_shutdown,
454 455
};

456
module_platform_driver(pwm_backlight_driver);
457 458 459

MODULE_DESCRIPTION("PWM based Backlight Driver");
MODULE_LICENSE("GPL");
460
MODULE_ALIAS("platform:pwm-backlight");