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