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