pwm_bl.c 9.5 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 14
#include <linux/gpio.h>
#include <linux/of_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 36
	int			enable_gpio;
	unsigned long		enable_gpio_flags;
37
	unsigned int		scale;
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
	unsigned int lth = pb->lth_brightness;
49 50
	int duty_cycle, err;

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

54
	if (pb->levels)
55
		duty_cycle = pb->levels[brightness];
56
	else
57 58
		duty_cycle = brightness;

59
	duty_cycle = (duty_cycle * (pb->period - lth) / pb->scale) + lth;
60 61

	pwm_config(pb->pwm, duty_cycle, pb->period);
62

63 64 65 66
	err = regulator_enable(pb->power_supply);
	if (err < 0)
		dev_err(pb->dev, "failed to enable power supply\n");

67 68 69 70 71 72 73
	if (gpio_is_valid(pb->enable_gpio)) {
		if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
			gpio_set_value(pb->enable_gpio, 0);
		else
			gpio_set_value(pb->enable_gpio, 1);
	}

74
	pwm_enable(pb->pwm);
T
Thierry Reding 已提交
75
	pb->enabled = true;
76 77 78 79
}

static void pwm_backlight_power_off(struct pwm_bl_data *pb)
{
T
Thierry Reding 已提交
80 81 82
	if (!pb->enabled)
		return;

83 84
	pwm_config(pb->pwm, 0, pb->period);
	pwm_disable(pb->pwm);
T
Thierry Reding 已提交
85

86 87 88 89 90 91 92
	if (gpio_is_valid(pb->enable_gpio)) {
		if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
			gpio_set_value(pb->enable_gpio, 1);
		else
			gpio_set_value(pb->enable_gpio, 0);
	}

93
	regulator_disable(pb->power_supply);
T
Thierry Reding 已提交
94
	pb->enabled = false;
95 96
}

97 98
static int pwm_backlight_update_status(struct backlight_device *bl)
{
99
	struct pwm_bl_data *pb = bl_get_data(bl);
100 101
	int brightness = bl->props.brightness;

102 103 104
	if (bl->props.power != FB_BLANK_UNBLANK ||
	    bl->props.fb_blank != FB_BLANK_UNBLANK ||
	    bl->props.state & BL_CORE_FBBLANK)
105 106
		brightness = 0;

107
	if (pb->notify)
108
		brightness = pb->notify(pb->dev, brightness);
109

110
	if (brightness > 0)
111
		pwm_backlight_power_on(pb, brightness);
112 113
	else
		pwm_backlight_power_off(pb);
114 115 116 117

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

118 119 120 121 122 123 124 125
	return 0;
}

static int pwm_backlight_get_brightness(struct backlight_device *bl)
{
	return bl->props.brightness;
}

126 127 128
static int pwm_backlight_check_fb(struct backlight_device *bl,
				  struct fb_info *info)
{
129
	struct pwm_bl_data *pb = bl_get_data(bl);
130 131 132 133

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

134
static const struct backlight_ops pwm_backlight_ops = {
135 136
	.update_status	= pwm_backlight_update_status,
	.get_brightness	= pwm_backlight_get_brightness,
137
	.check_fb	= pwm_backlight_check_fb,
138 139
};

140 141 142 143 144
#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;
145
	enum of_gpio_flags flags;
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 177 178 179 180 181 182 183 184 185
	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--;
	}

186 187 188 189 190 191 192
	data->enable_gpio = of_get_named_gpio_flags(node, "enable-gpios", 0,
						    &flags);
	if (data->enable_gpio == -EPROBE_DEFER)
		return -EPROBE_DEFER;

	if (gpio_is_valid(data->enable_gpio) && (flags & OF_GPIO_ACTIVE_LOW))
		data->enable_gpio_flags |= PWM_BACKLIGHT_GPIO_ACTIVE_LOW;
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

	return 0;
}

static struct of_device_id pwm_backlight_of_match[] = {
	{ .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

211 212 213
static int pwm_backlight_probe(struct platform_device *pdev)
{
	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
214 215
	struct platform_pwm_backlight_data defdata;
	struct backlight_properties props;
216 217
	struct backlight_device *bl;
	struct pwm_bl_data *pb;
218
	int ret;
219

220
	if (!data) {
221 222 223 224 225 226 227
		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;
228
	}
229

230 231 232 233 234 235
	if (data->init) {
		ret = data->init(&pdev->dev);
		if (ret < 0)
			return ret;
	}

236
	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
237
	if (!pb) {
238
		dev_err(&pdev->dev, "no memory for state\n");
239 240 241
		ret = -ENOMEM;
		goto err_alloc;
	}
242

243
	if (data->levels) {
244 245 246 247 248 249
		unsigned int i;

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

250 251
		pb->levels = data->levels;
	} else
252
		pb->scale = data->max_brightness;
253

254 255
	pb->enable_gpio = data->enable_gpio;
	pb->enable_gpio_flags = data->enable_gpio_flags;
256
	pb->notify = data->notify;
257
	pb->notify_after = data->notify_after;
258
	pb->check_fb = data->check_fb;
259
	pb->exit = data->exit;
260
	pb->dev = &pdev->dev;
T
Thierry Reding 已提交
261
	pb->enabled = false;
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
	if (gpio_is_valid(pb->enable_gpio)) {
		unsigned long flags;

		if (pb->enable_gpio_flags & PWM_BACKLIGHT_GPIO_ACTIVE_LOW)
			flags = GPIOF_OUT_INIT_HIGH;
		else
			flags = GPIOF_OUT_INIT_LOW;

		ret = gpio_request_one(pb->enable_gpio, flags, "enable");
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
				pb->enable_gpio, ret);
			goto err_alloc;
		}
	}

279 280 281 282 283 284
	pb->power_supply = devm_regulator_get(&pdev->dev, "power");
	if (IS_ERR(pb->power_supply)) {
		ret = PTR_ERR(pb->power_supply);
		goto err_gpio;
	}

285
	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
286
	if (IS_ERR(pb->pwm)) {
287 288 289 290 291 292
		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");

		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
		if (IS_ERR(pb->pwm)) {
			dev_err(&pdev->dev, "unable to request legacy PWM\n");
			ret = PTR_ERR(pb->pwm);
293
			goto err_gpio;
294 295 296 297 298 299 300 301 302 303 304 305 306 307
		}
	}

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

	/*
	 * 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,
	 * set the period from platform data.
	 */
	if (data->pwm_period_ns > 0)
		pwm_set_period(pb->pwm, data->pwm_period_ns);

	pb->period = pwm_get_period(pb->pwm);
308
	pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
309

310
	memset(&props, 0, sizeof(struct backlight_properties));
M
Matthew Garrett 已提交
311
	props.type = BACKLIGHT_RAW;
312 313 314
	props.max_brightness = data->max_brightness;
	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
				       &pwm_backlight_ops, &props);
315 316
	if (IS_ERR(bl)) {
		dev_err(&pdev->dev, "failed to register backlight\n");
317
		ret = PTR_ERR(bl);
318
		goto err_gpio;
319 320
	}

321 322 323 324 325 326 327
	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;
	}

328 329 330 331 332
	bl->props.brightness = data->dft_brightness;
	backlight_update_status(bl);

	platform_set_drvdata(pdev, bl);
	return 0;
333

334 335 336
err_gpio:
	if (gpio_is_valid(pb->enable_gpio))
		gpio_free(pb->enable_gpio);
337 338 339 340
err_alloc:
	if (data->exit)
		data->exit(&pdev->dev);
	return ret;
341 342 343 344 345
}

static int pwm_backlight_remove(struct platform_device *pdev)
{
	struct backlight_device *bl = platform_get_drvdata(pdev);
346
	struct pwm_bl_data *pb = bl_get_data(bl);
347 348

	backlight_device_unregister(bl);
349
	pwm_backlight_power_off(pb);
350

351 352
	if (pb->exit)
		pb->exit(&pdev->dev);
353

354 355 356
	return 0;
}

357
#ifdef CONFIG_PM_SLEEP
358
static int pwm_backlight_suspend(struct device *dev)
359
{
360
	struct backlight_device *bl = dev_get_drvdata(dev);
361
	struct pwm_bl_data *pb = bl_get_data(bl);
362

363
	if (pb->notify)
364
		pb->notify(pb->dev, 0);
365

366
	pwm_backlight_power_off(pb);
367

368 369
	if (pb->notify_after)
		pb->notify_after(pb->dev, 0);
370

371 372 373
	return 0;
}

374
static int pwm_backlight_resume(struct device *dev)
375
{
376
	struct backlight_device *bl = dev_get_drvdata(dev);
377 378

	backlight_update_status(bl);
379

380 381
	return 0;
}
382
#endif
383 384 385 386

static SIMPLE_DEV_PM_OPS(pwm_backlight_pm_ops, pwm_backlight_suspend,
			 pwm_backlight_resume);

387 388
static struct platform_driver pwm_backlight_driver = {
	.driver		= {
389 390 391 392
		.name		= "pwm-backlight",
		.owner		= THIS_MODULE,
		.pm		= &pwm_backlight_pm_ops,
		.of_match_table	= of_match_ptr(pwm_backlight_of_match),
393 394 395 396 397
	},
	.probe		= pwm_backlight_probe,
	.remove		= pwm_backlight_remove,
};

398
module_platform_driver(pwm_backlight_driver);
399 400 401

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