• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
3  * Author:Mark Yao <mark.yao@rock-chips.com>
4  *
5  * This software is licensed under the terms of the GNU General Public
6  * License version 2, as published by the Free Software Foundation, and
7  * may be copied, distributed, and modified under those terms.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 
15 #include <drm/drm.h>
16 #include <drm/drmP.h>
17 #include <drm/drm_panel.h>
18 
19 #include <linux/device.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/gpio/consumer.h>
23 #include <linux/gpio.h>
24 #include <linux/regulator/consumer.h>
25 #include <linux/of.h>
26 #include <linux/of_device.h>
27 #include <linux/pinctrl/consumer.h>
28 #include <linux/platform_device.h>
29 #include <linux/pwm.h>
30 #include <linux/pwm_backlight.h>
31 #include <uapi/drm/rockchip_drm.h>
32 
33 #include "rockchip_drm_drv.h"
34 #include "rockchip_drm_backlight.h"
35 
36 struct sub_backlight {
37 	struct pwm_device *pwm;
38 	struct pinctrl *pinctrl;
39 	struct pinctrl_state *dummy_state;
40 	struct pinctrl_state *active_state;
41 	struct drm_crtc *crtc;
42 	struct device *dev;
43 
44 	const struct rockchip_sub_backlight_ops *ops;
45 	struct list_head list;
46 };
47 
48 #define to_rockchip_backlight_device(x) \
49 		container_of((x), struct rockchip_drm_backlight, pwm_pdev)
50 
51 static LIST_HEAD(backlight_list);
52 
compute_duty_cycle(struct rockchip_drm_backlight * bl,int brightness,int period)53 static int compute_duty_cycle(struct rockchip_drm_backlight *bl,
54 			      int brightness, int period)
55 {
56 	unsigned int lth = bl->lth_brightness;
57 	int duty_cycle;
58 
59 	duty_cycle = bl->levels[brightness];
60 
61 	return (duty_cycle * (period - lth) / bl->scale) + lth;
62 }
63 
rockchip_pwm_power_on(struct rockchip_drm_backlight * bl,struct pwm_device * pwm,int brightness)64 static void rockchip_pwm_power_on(struct rockchip_drm_backlight *bl,
65 				  struct pwm_device *pwm, int brightness)
66 {
67 	struct pwm_args pargs;
68 	int duty_cycle;
69 
70 	pwm_get_args(pwm, &pargs);
71 	duty_cycle = compute_duty_cycle(bl, brightness, pargs.period);
72 	pwm_config(pwm, duty_cycle, pargs.period);
73 	pwm_enable(pwm);
74 }
75 
rockchip_pwm_power_off(struct rockchip_drm_backlight * bl,struct pwm_device * pwm)76 static void rockchip_pwm_power_off(struct rockchip_drm_backlight *bl,
77 				   struct pwm_device *pwm)
78 {
79 	struct pwm_args pargs;
80 	struct pwm_state state;
81 
82 	pwm_get_state(pwm, &state);
83 	if (!state.enabled)
84 		return;
85 
86 	pwm_get_args(pwm, &pargs);
87 	pwm_config(pwm, 0, pargs.period);
88 	pwm_disable(pwm);
89 }
90 
rockchip_backlight_power_on(struct rockchip_drm_backlight * bl)91 static void rockchip_backlight_power_on(struct rockchip_drm_backlight *bl)
92 {
93 	int err;
94 
95 	if (bl->enabled)
96 		return;
97 
98 	err = regulator_enable(bl->power_supply);
99 	if (err < 0)
100 		dev_err(bl->dev, "failed to enable power supply\n");
101 
102 	if (bl->enable_gpio)
103 		gpiod_set_value(bl->enable_gpio, 1);
104 
105 	bl->enabled = true;
106 }
107 
rockchip_backlight_power_off(struct rockchip_drm_backlight * bl)108 static void rockchip_backlight_power_off(struct rockchip_drm_backlight *bl)
109 {
110 	if (!bl->enabled)
111 		return;
112 
113 	if (bl->enable_gpio)
114 		gpiod_set_value(bl->enable_gpio, 0);
115 
116 	regulator_disable(bl->power_supply);
117 	bl->enabled = false;
118 }
119 
backlight_parse_dt(struct rockchip_drm_backlight * bl)120 static int backlight_parse_dt(struct rockchip_drm_backlight *bl)
121 {
122 	struct device_node *node = bl->dev->of_node;
123 	struct device *dev = bl->dev;
124 	struct property *prop;
125 	size_t size;
126 	int length;
127 	u32 value;
128 	int ret, i;
129 
130 	if (!node)
131 		return -ENODEV;
132 
133 	bl->pwm = devm_pwm_get(dev, NULL);
134 	if (IS_ERR(bl->pwm)) {
135 		dev_err(dev, "unable to request PWM: %ld\n",
136 			PTR_ERR(bl->pwm));
137 		return PTR_ERR(bl->pwm);
138 	}
139 
140 	if (!bl->pwm->chip->dev->pins) {
141 		dev_err(dev, "failed to find pwm pinctrl\n");
142 		return -ENODEV;
143 	}
144 	bl->dummy_state = pinctrl_lookup_state(bl->pwm->chip->dev->pins->p,
145 					       "dummy");
146 	if (IS_ERR_OR_NULL(bl->dummy_state)) {
147 		dev_err(dev, "failed to find pwm dummy state\n");
148 		return -ENODEV;
149 	}
150 
151 	bl->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS);
152 	if (IS_ERR(bl->enable_gpio)) {
153 		dev_err(dev, "unable to request enable gpio: %ld\n",
154 			PTR_ERR(bl->enable_gpio));
155 		return PTR_ERR(bl->enable_gpio);
156 	}
157 
158 	bl->power_supply = devm_regulator_get(dev, "power");
159 	if (IS_ERR(bl->power_supply)) {
160 		dev_err(dev, "unable to request power supply: %ld\n",
161 			PTR_ERR(bl->power_supply));
162 		return PTR_ERR(bl->power_supply);
163 	}
164 
165 	/* determine the number of brightness levels */
166 	prop = of_find_property(node, "brightness-levels", &length);
167 	if (!prop)
168 		return -EINVAL;
169 
170 	bl->max_brightness = length / sizeof(u32);
171 
172 	if (bl->max_brightness <= 0)
173 		return -EINVAL;
174 
175 	/* read brightness levels from DT property */
176 	size = sizeof(*bl->levels) * bl->max_brightness;
177 
178 	bl->levels = devm_kzalloc(dev, size, GFP_KERNEL);
179 	if (!bl->levels)
180 		return -ENOMEM;
181 
182 	ret = of_property_read_u32_array(node, "brightness-levels",
183 					 bl->levels,
184 					 bl->max_brightness);
185 	if (ret < 0)
186 		return ret;
187 
188 	ret = of_property_read_u32(node, "default-brightness-level", &value);
189 	if (ret < 0)
190 		return ret;
191 
192 	bl->dft_brightness = value;
193 	bl->max_brightness--;
194 
195 	for (i = 0; i <= bl->max_brightness; i++)
196 		if (bl->levels[i] > bl->scale)
197 			bl->scale = bl->levels[i];
198 
199 	return 0;
200 }
201 
rockchip_drm_backlight_update(struct drm_device * drm)202 void rockchip_drm_backlight_update(struct drm_device *drm)
203 {
204 	struct rockchip_drm_private *private = drm->dev_private;
205 	struct rockchip_drm_backlight *bl = private->backlight;
206 	struct drm_connector *connector;
207 	struct sub_backlight *sub;
208 	struct rockchip_crtc_state *s;
209 	struct drm_crtc *crtc;
210 	bool backlight_changed = false;
211 
212 	if (!bl || !bl->connector)
213 		return;
214 
215 	sub = bl->sub;
216 	connector = bl->connector;
217 	crtc = connector->state->crtc;
218 	if (!crtc) {
219 		if (sub) {
220 			bl->sub = NULL;
221 			backlight_changed = true;
222 		}
223 	} else if (!sub || sub->dev->of_node != crtc->port) {
224 		s = to_rockchip_crtc_state(crtc->state);
225 		if (s->cabc_mode != ROCKCHIP_DRM_CABC_MODE_DISABLE) {
226 			list_for_each_entry(sub, &backlight_list, list) {
227 				if (sub->crtc == crtc) {
228 					bl->sub = sub;
229 					backlight_changed = true;
230 					break;
231 				}
232 			}
233 		} else if (bl->sub) {
234 			bl->sub = NULL;
235 			backlight_changed = true;
236 		}
237 	}
238 
239 	if (backlight_changed)
240 		backlight_update_status(bl->bd);
241 }
242 EXPORT_SYMBOL(rockchip_drm_backlight_update);
243 
of_rockchip_drm_sub_backlight_register(struct device * dev,struct drm_crtc * crtc,const struct rockchip_sub_backlight_ops * ops)244 int of_rockchip_drm_sub_backlight_register(struct device *dev,
245 				struct drm_crtc *crtc,
246 				const struct rockchip_sub_backlight_ops *ops)
247 {
248 	struct sub_backlight *sub;
249 	struct pwm_device *pwm;
250 
251 	pwm = devm_pwm_get(dev, NULL);
252 	if (IS_ERR(pwm)) {
253 		dev_dbg(dev, "unable to request PWM\n");
254 		return PTR_ERR(pwm);
255 	}
256 
257 	sub = devm_kzalloc(dev, sizeof(*sub), GFP_KERNEL);
258 	if (!sub)
259 		return -ENOMEM;
260 
261 	sub->pinctrl = devm_pinctrl_get(pwm->chip->dev);
262 	if (IS_ERR(sub->pinctrl)) {
263 		dev_err(dev, "failed to find pwm pinctrl\n");
264 		return PTR_ERR(sub->pinctrl);
265 	}
266 
267 	sub->dummy_state = pinctrl_lookup_state(sub->pinctrl, "dummy");
268 	if (IS_ERR_OR_NULL(sub->dummy_state)) {
269 		dev_err(dev, "failed to find pwm dummy state\n");
270 		return -ENODEV;
271 	}
272 
273 	sub->active_state = pinctrl_lookup_state(sub->pinctrl, "active");
274 	if (IS_ERR_OR_NULL(sub->active_state)) {
275 		dev_err(dev, "failed to find pwm active state\n");
276 		return -ENODEV;
277 	}
278 
279 	pwm_adjust_config(pwm);
280 
281 	sub->pwm = pwm;
282 	sub->dev = dev;
283 	sub->crtc = crtc;
284 	sub->ops = ops;
285 
286 	INIT_LIST_HEAD(&sub->list);
287 	list_add_tail(&sub->list, &backlight_list);
288 
289 	return 0;
290 }
291 EXPORT_SYMBOL(of_rockchip_drm_sub_backlight_register);
292 
rockchip_drm_backlight_bind(struct device * dev,struct device * master,void * data)293 static int rockchip_drm_backlight_bind(struct device *dev,
294 				       struct device *master, void *data)
295 {
296 	struct drm_device *drm_dev = data;
297 	struct rockchip_drm_private *private = drm_dev->dev_private;
298 	struct rockchip_drm_backlight *bl = dev_get_drvdata(dev);
299 	struct drm_connector *connector;
300 	struct drm_panel *panel;
301 	struct device_node *backlight_np;
302 
303 	private->backlight = bl;
304 
305 	mutex_lock(&drm_dev->mode_config.mutex);
306 
307 	drm_for_each_connector(connector, drm_dev) {
308 		panel = drm_find_panel_by_connector(connector);
309 		if (!panel || !panel->dev)
310 			continue;
311 		backlight_np = of_parse_phandle(panel->dev->of_node,
312 						"backlight", 0);
313 		if (backlight_np == dev->of_node) {
314 			bl->connector = connector;
315 			break;
316 		}
317 	}
318 
319 	mutex_unlock(&drm_dev->mode_config.mutex);
320 
321 	if (!bl->connector) {
322 		dev_warn(dev, "failed to bind drm backlight\n");
323 		return -ENODEV;
324 	}
325 
326 	return 0;
327 }
328 
rockchip_drm_backlight_unbind(struct device * dev,struct device * master,void * data)329 static void rockchip_drm_backlight_unbind(struct device *dev,
330 					  struct device *master, void *data)
331 {
332 	struct drm_device *drm_dev = data;
333 	struct rockchip_drm_private *private = drm_dev->dev_private;
334 	struct rockchip_drm_backlight *bl = dev_get_drvdata(dev);
335 
336 	private->backlight = NULL;
337 	bl->connector = NULL;
338 }
339 
340 static const struct component_ops rockchip_drm_backlight_component_ops = {
341 	.bind = rockchip_drm_backlight_bind,
342 	.unbind = rockchip_drm_backlight_unbind,
343 };
344 
345 static int
rockchip_drm_backlight_update_status(struct backlight_device * backlight)346 rockchip_drm_backlight_update_status(struct backlight_device *backlight)
347 {
348 	int brightness = backlight->props.brightness;
349 	struct rockchip_drm_backlight *bl = bl_get_data(backlight);
350 	struct sub_backlight *sub = bl->sub;
351 	struct sub_backlight *current_sub = bl->current_sub;
352 	bool async;
353 
354 	if (backlight->props.power != FB_BLANK_UNBLANK ||
355 	    backlight->props.fb_blank != FB_BLANK_UNBLANK ||
356 	    backlight->props.state & BL_CORE_FBBLANK)
357 		brightness = 0;
358 
359 	if (!sub || brightness <= 0) {
360 		if (current_sub)
361 			pinctrl_select_state(current_sub->pinctrl,
362 					     current_sub->dummy_state);
363 
364 		if (brightness > 0) {
365 			rockchip_pwm_power_on(bl, bl->pwm, brightness);
366 			rockchip_backlight_power_on(bl);
367 		} else {
368 			rockchip_backlight_power_off(bl);
369 			rockchip_pwm_power_off(bl, bl->pwm);
370 		}
371 
372 		pinctrl_pm_select_default_state(bl->pwm->chip->dev);
373 		if (current_sub) {
374 			rockchip_pwm_power_off(bl, current_sub->pwm);
375 
376 			if (current_sub->ops->config_done)
377 				current_sub->ops->config_done(current_sub->dev,
378 							      true);
379 		}
380 
381 		return 0;
382 	}
383 
384 	pinctrl_select_state(bl->pwm->chip->dev->pins->p, bl->dummy_state);
385 
386 	async = !!current_sub;
387 	if (current_sub && sub != current_sub) {
388 		pinctrl_select_state(current_sub->pinctrl,
389 				     current_sub->dummy_state);
390 		async = false;
391 	}
392 
393 	rockchip_pwm_power_on(bl, sub->pwm, brightness);
394 
395 	if (current_sub && sub != current_sub) {
396 		rockchip_pwm_power_on(bl, current_sub->pwm, brightness);
397 		if (current_sub->ops->config_done)
398 			current_sub->ops->config_done(current_sub->dev, true);
399 	}
400 
401 	if (sub->ops->config_done)
402 		sub->ops->config_done(sub->dev, async);
403 
404 	pinctrl_select_state(sub->pinctrl, sub->active_state);
405 
406 	rockchip_backlight_power_on(bl);
407 
408 	bl->current_sub = sub;
409 
410 	return 0;
411 }
412 
413 static const struct backlight_ops rockchip_drm_backlight_ops = {
414 	.update_status = rockchip_drm_backlight_update_status,
415 };
416 
rockchip_drm_backlight_probe(struct platform_device * pdev)417 static int rockchip_drm_backlight_probe(struct platform_device *pdev)
418 {
419 	struct rockchip_drm_backlight *bl;
420 	struct backlight_properties props;
421 	struct device *dev = &pdev->dev;
422 	struct device_node *node = dev->of_node;
423 	int initial_blank = FB_BLANK_UNBLANK;
424 	int ret;
425 
426 	bl = devm_kzalloc(dev, sizeof(*bl), GFP_KERNEL);
427 	if (!bl)
428 		return -ENOMEM;
429 
430 	bl->dev = dev;
431 	ret = backlight_parse_dt(bl);
432 	if (ret < 0) {
433 		dev_err(&pdev->dev, "failed to find parse backlight dts\n");
434 		return ret;
435 	}
436 
437 	bl->enabled = false;
438 
439 	if (bl->enable_gpio) {
440 		/*
441 		 * If the driver is probed from the device tree and there is a
442 		 * phandle link pointing to the backlight node, it is safe to
443 		 * assume that another driver will enable the backlight at the
444 		 * appropriate time. Therefore, if it is disabled, keep it so.
445 		 */
446 		if (node && node->phandle &&
447 		    gpiod_get_direction(bl->enable_gpio) == GPIOF_DIR_OUT &&
448 		    gpiod_get_value(bl->enable_gpio) == 0)
449 			initial_blank = FB_BLANK_POWERDOWN;
450 		else
451 			gpiod_direction_output(bl->enable_gpio, 1);
452 	}
453 
454 	if (node && node->phandle && !regulator_is_enabled(bl->power_supply))
455 		initial_blank = FB_BLANK_POWERDOWN;
456 
457 	pwm_adjust_config(bl->pwm);
458 
459 	memset(&props, 0, sizeof(props));
460 	props.type = BACKLIGHT_RAW;
461 	props.max_brightness = bl->max_brightness;
462 	bl->bd = backlight_device_register(dev_name(dev), dev, bl,
463 					   &rockchip_drm_backlight_ops, &props);
464 	if (IS_ERR(bl->bd)) {
465 		dev_err(&pdev->dev, "failed to register backlight\n");
466 		ret = PTR_ERR(bl->bd);
467 		return ret;
468 	}
469 
470 	bl->bd->props.brightness = bl->dft_brightness;
471 	bl->bd->props.power = initial_blank;
472 	backlight_update_status(bl->bd);
473 
474 	platform_set_drvdata(pdev, bl);
475 
476 	ret = component_add(dev, &rockchip_drm_backlight_component_ops);
477 	if (ret)
478 		backlight_device_unregister(bl->bd);
479 
480 	return ret;
481 }
482 
rockchip_drm_backlight_remove(struct platform_device * pdev)483 static int rockchip_drm_backlight_remove(struct platform_device *pdev)
484 {
485 	struct rockchip_drm_backlight *bl = platform_get_drvdata(pdev);
486 
487 	backlight_device_unregister(bl->bd);
488 	component_del(&pdev->dev, &rockchip_drm_backlight_component_ops);
489 
490 	return 0;
491 }
492 
493 static const struct of_device_id rockchip_drm_backlight_dt_ids[] = {
494 	{.compatible = "rockchip,drm-backlight",
495 	 .data = NULL },
496 	{}
497 };
498 MODULE_DEVICE_TABLE(of, rockchip_drm_backlight_dt_ids);
499 
500 static struct platform_driver rockchip_drm_backlight_driver = {
501 	.probe = rockchip_drm_backlight_probe,
502 	.remove = rockchip_drm_backlight_remove,
503 	.driver = {
504 		   .name = "drm-backlight",
505 		   .of_match_table =
506 			of_match_ptr(rockchip_drm_backlight_dt_ids),
507 	},
508 };
509 
510 module_platform_driver(rockchip_drm_backlight_driver);
511 
512 MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
513 MODULE_DESCRIPTION("Rockchip Drm Backlight Driver");
514 MODULE_LICENSE("GPL v2");
515