pwm_bl.c 9.51 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;
33
	bool			enabled;
34
	struct regulator	*power_supply;
35
	struct gpio_desc	*enable_gpio;
36
	unsigned int		scale;
37
38
	int			(*notify)(struct device *,
					  int brightness);
39
40
	void			(*notify_after)(struct device *,
					int brightness);
41
	int			(*check_fb)(struct device *, struct fb_info *);
42
	void			(*exit)(struct device *);
43
44
};

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

49
50
51
	if (pb->enabled)
		return;

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

56
57
	if (pb->enable_gpio)
		gpiod_set_value(pb->enable_gpio, 1);
58

59
	pwm_enable(pb->pwm);
60
	pb->enabled = true;
61
62
63
64
}

static void pwm_backlight_power_off(struct pwm_bl_data *pb)
{
65
66
67
	if (!pb->enabled)
		return;

68
69
	pwm_config(pb->pwm, 0, pb->period);
	pwm_disable(pb->pwm);
70

71
72
	if (pb->enable_gpio)
		gpiod_set_value(pb->enable_gpio, 0);
73

74
	regulator_disable(pb->power_supply);
75
	pb->enabled = false;
76
77
}

78
79
80
81
82
83
84
85
86
87
88
89
90
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;
}

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

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

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

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

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

115
116
117
	return 0;
}

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

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

126
static const struct backlight_ops pwm_backlight_ops = {
127
	.update_status	= pwm_backlight_update_status,
128
	.check_fb	= pwm_backlight_check_fb,
129
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
#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--;
	}

176
	data->enable_gpio = -EINVAL;
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
	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

194
195
static int pwm_backlight_probe(struct platform_device *pdev)
{
Jingoo Han's avatar
Jingoo Han committed
196
	struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
197
198
	struct platform_pwm_backlight_data defdata;
	struct backlight_properties props;
199
200
	struct backlight_device *bl;
	struct pwm_bl_data *pb;
201
	int ret;
202

203
	if (!data) {
204
205
206
207
208
209
210
		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;
211
	}
212

213
214
215
216
217
218
	if (data->init) {
		ret = data->init(&pdev->dev);
		if (ret < 0)
			return ret;
	}

219
	pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
220
221
222
223
	if (!pb) {
		ret = -ENOMEM;
		goto err_alloc;
	}
224

225
	if (data->levels) {
226
227
228
229
230
231
		unsigned int i;

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

232
233
		pb->levels = data->levels;
	} else
234
		pb->scale = data->max_brightness;
235

236
	pb->notify = data->notify;
237
	pb->notify_after = data->notify_after;
238
	pb->check_fb = data->check_fb;
239
	pb->exit = data->exit;
240
	pb->dev = &pdev->dev;
241
	pb->enabled = false;
242

243
	pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable");
244
245
	if (IS_ERR(pb->enable_gpio)) {
		ret = PTR_ERR(pb->enable_gpio);
246
		goto err_alloc;
247
	}
248

249
250
251
252
253
254
255
	/*
	 * 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");
256
257
		if (ret < 0) {
			dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
258
				data->enable_gpio, ret);
259
260
			goto err_alloc;
		}
261
262

		pb->enable_gpio = gpio_to_desc(data->enable_gpio);
263
264
	}

265
266
267
	if (pb->enable_gpio)
		gpiod_direction_output(pb->enable_gpio, 1);

268
269
270
	pb->power_supply = devm_regulator_get(&pdev->dev, "power");
	if (IS_ERR(pb->power_supply)) {
		ret = PTR_ERR(pb->power_supply);
271
		goto err_alloc;
272
	}
273

274
	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
275
	if (IS_ERR(pb->pwm)) {
276
277
278
279
280
281
		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);
282
			goto err_alloc;
283
284
285
286
287
288
289
290
		}
	}

	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,
291
292
	 * set the period from platform data if it has not already been set
	 * via the PWM lookup table.
293
	 */
294
295
296
	pb->period = pwm_get_period(pb->pwm);
	if (!pb->period && (data->pwm_period_ns > 0)) {
		pb->period = data->pwm_period_ns;
297
		pwm_set_period(pb->pwm, data->pwm_period_ns);
298
	}
299

300
	pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
301

302
	memset(&props, 0, sizeof(struct backlight_properties));
303
	props.type = BACKLIGHT_RAW;
304
305
306
	props.max_brightness = data->max_brightness;
	bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
				       &pwm_backlight_ops, &props);
307
308
	if (IS_ERR(bl)) {
		dev_err(&pdev->dev, "failed to register backlight\n");
309
		ret = PTR_ERR(bl);
310
		goto err_alloc;
311
312
	}

313
314
315
316
317
318
319
	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;
	}

320
321
322
323
324
	bl->props.brightness = data->dft_brightness;
	backlight_update_status(bl);

	platform_set_drvdata(pdev, bl);
	return 0;
325
326
327
328
329

err_alloc:
	if (data->exit)
		data->exit(&pdev->dev);
	return ret;
330
331
332
333
334
}

static int pwm_backlight_remove(struct platform_device *pdev)
{
	struct backlight_device *bl = platform_get_drvdata(pdev);
335
	struct pwm_bl_data *pb = bl_get_data(bl);
336
337

	backlight_device_unregister(bl);
338
	pwm_backlight_power_off(pb);
339

340
341
	if (pb->exit)
		pb->exit(&pdev->dev);
342

343
344
345
	return 0;
}

346
347
348
349
350
351
352
353
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);
}

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

360
	if (pb->notify)
361
		pb->notify(pb->dev, 0);
362

363
	pwm_backlight_power_off(pb);
364

365
366
	if (pb->notify_after)
		pb->notify_after(pb->dev, 0);
367

368
369
370
	return 0;
}

371
static int pwm_backlight_resume(struct device *dev)
372
{
373
	struct backlight_device *bl = dev_get_drvdata(dev);
374
375

	backlight_update_status(bl);
376

377
378
	return 0;
}
379
#endif
380

381
382
383
384
385
386
387
388
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
};
389

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

402
module_platform_driver(pwm_backlight_driver);
403
404
405

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