1 /*
2  * Allwinner SoCs display driver.
3  *
4  * Copyright (C) 2016 Allwinner.
5  *
6  * This file is licensed under the terms of the GNU General Public
7  * License version 2.  This program is licensed "as is" without any
8  * warranty of any kind, whether express or implied.
9  */
10 
11 #include "disp_smart_backlight.h"
12 #include <hal_atomic.h>
13 
14 struct disp_smbl_private_data {
15     struct disp_smbl_info info;
16     bool applied;
17 
18      s32 (*shadow_protect)(u32 sel, bool protect);
19 
20     u32 enabled;
21     hal_sem_t mlock;
22 };
23 static u32 smbl_data_lock;
24 
25 /* #define SMBL_NO_AL */
26 static struct disp_smbl *smbls;
27 static struct disp_smbl_private_data *smbl_private;
28 
disp_get_smbl(u32 disp)29 struct disp_smbl *disp_get_smbl(u32 disp)
30 {
31     u32 num_screens;
32 
33     num_screens = bsp_disp_feat_get_num_screens();
34     if (disp >= num_screens) {
35         DE_WRN("disp %d out of range\n", disp);
36         return NULL;
37     }
38     DE_INF("get smbl%d ok\n", disp);
39 
40     if (bsp_disp_feat_is_support_smbl(disp))
41         return &smbls[disp];
42     else
43         return NULL;
44 }
disp_smbl_get_priv(struct disp_smbl * smbl)45 static struct disp_smbl_private_data *disp_smbl_get_priv(struct disp_smbl *smbl)
46 {
47     if (smbl == NULL) {
48         DE_INF("NULL hdl!\n");
49         return NULL;
50     }
51 
52     return &smbl_private[smbl->disp];
53 }
54 
disp_smbl_update_regs(struct disp_smbl * smbl)55 static s32 disp_smbl_update_regs(struct disp_smbl *smbl)
56 {
57     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
58     bool applied = false;
59 
60     if ((smbl == NULL) || (smblp == NULL)) {
61         DE_INF("NULL hdl!\n");
62         return -1;
63     }
64     smbl_data_lock = hal_spin_lock_irqsave(&disp_lock);
65     if (true == smblp->applied) {
66         applied = true;
67         smblp->applied = false;
68     }
69     hal_spin_unlock_irqrestore(&disp_lock, smbl_data_lock);
70 
71     disp_al_smbl_update_regs(smbl->disp);
72 
73     return 0;
74 }
75 
76 /* should protect width @mlock */
disp_smbl_update_backlight(struct disp_smbl * smbl,unsigned int bl)77 static s32 disp_smbl_update_backlight(struct disp_smbl *smbl, unsigned int bl)
78 {
79     if (smbl == NULL) {
80         DE_INF("NULL hdl!\n");
81         return -1;
82     }
83 
84     smbl->backlight = bl;
85     smbl->apply(smbl);
86 
87     return 0;
88 }
89 
90 /* should protect width @mlock */
disp_smbl_apply(struct disp_smbl * smbl)91 static s32 disp_smbl_apply(struct disp_smbl *smbl)
92 {
93     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
94     struct disp_smbl_info info;
95 
96     if ((smbl == NULL) || (smblp == NULL)) {
97         DE_INF("NULL hdl!\n");
98         return -1;
99     }
100 
101     memset(&info, 0, sizeof(struct disp_smbl_info));
102 
103     /* disp_sys_mutex_lock(&smblp->mlock); */
104     if (smbl->backlight != smblp->info.backlight) {
105         smblp->info.backlight = smbl->backlight;
106         smblp->info.flags |= SMBL_DIRTY_BL;
107     }
108     if (smblp->info.flags != SMBL_DIRTY_NONE) {
109         memcpy(&info, &smblp->info, sizeof(struct disp_smbl_info));
110         smblp->info.flags = SMBL_DIRTY_NONE;
111         disp_smbl_shadow_protect(smbl, true);
112         disp_al_smbl_apply(smbl->disp, &info);
113         disp_smbl_shadow_protect(smbl, false);
114         smbl_data_lock = hal_spin_lock_irqsave(&disp_lock);
115         smblp->applied = true;
116         hal_spin_unlock_irqrestore(&disp_lock, smbl_data_lock);
117     }
118     /* disp_sys_mutex_unlock(&smblp->mlock); */
119 
120     return 0;
121 }
122 
disp_smbl_force_apply(struct disp_smbl * smbl)123 static s32 disp_smbl_force_apply(struct disp_smbl *smbl)
124 {
125     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
126 
127     if ((smbl == NULL) || (smblp == NULL)) {
128         DE_INF("NULL hdl!\n");
129         return -1;
130     }
131 
132     disp_sys_mutex_lock(&smblp->mlock);
133     smblp->info.flags = SMBL_DIRTY_ALL;
134     disp_smbl_apply(smbl);
135     disp_smbl_update_regs(smbl);
136     disp_sys_mutex_unlock(&smblp->mlock);
137 
138     return 0;
139 }
140 
disp_smbl_sync(struct disp_smbl * smbl)141 static s32 disp_smbl_sync(struct disp_smbl *smbl)
142 {
143     if (smbl == NULL) {
144         DE_INF("NULL hdl!\n");
145         return -1;
146     }
147 
148     disp_smbl_update_regs(smbl);
149 
150     return 0;
151 }
152 
disp_smbl_tasklet(struct disp_smbl * smbl)153 static s32 disp_smbl_tasklet(struct disp_smbl *smbl)
154 {
155     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
156     struct disp_manager *mgr;
157     unsigned int dimming;
158     bool dimming_update = false;
159 
160     if ((smbl == NULL) || (smblp == NULL)) {
161         DE_INF("NULL hdl!\n");
162         return -1;
163     }
164 
165     disp_al_smbl_tasklet(smbl->disp);
166     dimming = disp_al_smbl_get_status(smbl->disp);
167     if (smblp->info.backlight_dimming != dimming) {
168         smblp->info.backlight_dimming = dimming;
169         dimming_update = true;
170     }
171 
172     mgr = smbl->manager;
173     if (mgr && mgr->device) {
174         struct disp_device *dispdev = mgr->device;
175 
176         if (dispdev->set_bright_dimming && dimming_update) {
177             if (dispdev->set_bright_dimming)
178                 dispdev->set_bright_dimming(dispdev,
179                         smblp->info.backlight_dimming);
180         }
181     }
182 
183     return 0;
184 }
185 
disp_smbl_is_enabled(struct disp_smbl * smbl)186 static bool disp_smbl_is_enabled(struct disp_smbl *smbl)
187 {
188     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
189 
190     if ((smbl == NULL) || (smblp == NULL)) {
191         DE_INF("NULL hdl!\n");
192         return false;
193     }
194 
195     return (smblp->info.enable == 1);
196 }
197 
disp_smbl_enable(struct disp_smbl * smbl)198 static s32 disp_smbl_enable(struct disp_smbl *smbl)
199 {
200     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
201     struct disp_device *dispdev = NULL;
202 
203     if ((smbl == NULL) || (smblp == NULL)) {
204         DE_INF("NULL hdl!\n");
205         return -1;
206     }
207     if (smbl->manager)
208         dispdev = smbl->manager->device;
209     if (dispdev)
210         dispdev->get_resolution(dispdev, &smblp->info.size.width,
211                     &smblp->info.size.height);
212 
213     if ((smblp->info.window.width == 0)
214         || (smblp->info.window.height == 0)) {
215         smblp->info.window.width = smblp->info.size.width;
216         smblp->info.window.height = smblp->info.size.height;
217     }
218 
219     DE_INF("smbl %d enable\n", smbl->disp);
220     disp_sys_mutex_lock(&smblp->mlock);
221     smblp->info.enable = 1;
222     smblp->info.flags |= SMBL_DIRTY_ENABLE;
223     disp_smbl_apply(smbl);
224     disp_sys_mutex_unlock(&smblp->mlock);
225 
226     return 0;
227 }
228 
disp_smbl_disable(struct disp_smbl * smbl)229 static s32 disp_smbl_disable(struct disp_smbl *smbl)
230 {
231     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
232 
233     if ((smbl == NULL) || (smblp == NULL)) {
234         DE_INF("NULL hdl!\n");
235         return -1;
236     }
237     DE_INF("smbl %d disable\n", smbl->disp);
238 
239     disp_sys_mutex_lock(&smblp->mlock);
240     smblp->info.enable = 0;
241     smblp->info.flags |= SMBL_DIRTY_ENABLE;
242     disp_smbl_apply(smbl);
243     disp_sys_mutex_unlock(&smblp->mlock);
244 
245     return 0;
246 }
247 
disp_smbl_shadow_protect(struct disp_smbl * smbl,bool protect)248 s32 disp_smbl_shadow_protect(struct disp_smbl *smbl, bool protect)
249 {
250     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
251 
252     if ((smbl == NULL) || (smblp == NULL)) {
253         DE_INF("NULL hdl!\n");
254         return -1;
255     }
256 
257     if (smblp->shadow_protect)
258         return smblp->shadow_protect(smbl->disp, protect);
259 
260     return -1;
261 }
262 
disp_smbl_set_window(struct disp_smbl * smbl,struct disp_rect * window)263 static s32 disp_smbl_set_window(struct disp_smbl *smbl,
264                 struct disp_rect *window)
265 {
266     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
267 
268     if ((smbl == NULL) || (smblp == NULL)) {
269         DE_INF("NULL hdl!\n");
270         return -1;
271     }
272     disp_sys_mutex_lock(&smblp->mlock);
273     memcpy(&smblp->info.window, window, sizeof(struct disp_rect));
274     smblp->info.flags |= SMBL_DIRTY_WINDOW;
275     disp_smbl_apply(smbl);
276     disp_sys_mutex_unlock(&smblp->mlock);
277 
278     return 0;
279 }
280 
disp_smbl_get_window(struct disp_smbl * smbl,struct disp_rect * window)281 static s32 disp_smbl_get_window(struct disp_smbl *smbl,
282                 struct disp_rect *window)
283 {
284     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
285 
286     if ((smbl == NULL) || (smblp == NULL)) {
287         DE_INF("NULL hdl!\n");
288         return -1;
289     }
290     disp_sys_mutex_lock(&smblp->mlock);
291     memcpy(window, &smblp->info.window, sizeof(struct disp_rect));
292     disp_sys_mutex_unlock(&smblp->mlock);
293 
294     return 0;
295 }
296 
disp_smbl_set_manager(struct disp_smbl * smbl,struct disp_manager * mgr)297 static s32 disp_smbl_set_manager(struct disp_smbl *smbl,
298                  struct disp_manager *mgr)
299 {
300     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
301 
302     if ((smbl == NULL) || (mgr == NULL) || (smblp == NULL)) {
303         DE_INF("NULL hdl!\n");
304         return -1;
305     }
306 
307     DE_INF("smbl %d -> mgr %d\n", smbl->disp, mgr->disp);
308     disp_sys_mutex_lock(&smblp->mlock);
309     smbl->manager = mgr;
310     mgr->smbl = smbl;
311     disp_sys_mutex_unlock(&smblp->mlock);
312 
313     return 0;
314 }
315 
disp_smbl_unset_manager(struct disp_smbl * smbl)316 static s32 disp_smbl_unset_manager(struct disp_smbl *smbl)
317 {
318     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
319 
320     if ((smbl == NULL) || (smblp == NULL)) {
321         DE_INF("NULL hdl!\n");
322         return -1;
323     }
324     disp_sys_mutex_lock(&smblp->mlock);
325     if (smbl->manager)
326         smbl->manager->smbl = NULL;
327     smbl->manager = NULL;
328     disp_sys_mutex_unlock(&smblp->mlock);
329 
330     return 0;
331 }
332 
disp_smbl_dump(struct disp_smbl * smbl,char * buf)333 static s32 disp_smbl_dump(struct disp_smbl *smbl, char *buf)
334 {
335     struct disp_smbl_info info;
336     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
337     u32 count = 0;
338 
339     if ((smbl == NULL) || (smblp == NULL)) {
340         DE_INF("NULL hdl!\n");
341         return -1;
342     }
343 
344     memcpy(&info, &smblp->info, sizeof(struct disp_smbl_info));
345 
346     count +=
347         sprintf(buf + count,
348             "smart_backlight %d: %s, window<%d,%d,%d,%d>, backlight=%d, save_power=%d percent\n",
349             smbl->disp, (info.enable == 1) ? "enable" : "disable",
350             info.window.x, info.window.y, info.window.width,
351             info.window.height, smbl->backlight,
352             100 - info.backlight_dimming * 100 / 256);
353 
354     return count;
355 }
356 
disp_smbl_init(struct disp_smbl * smbl)357 static s32 disp_smbl_init(struct disp_smbl *smbl)
358 {
359     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
360 
361     if ((smbl == NULL) || (smblp == NULL)) {
362         DE_INF("NULL hdl!\n");
363         return -1;
364     }
365 
366     return 0;
367 }
368 
disp_smbl_exit(struct disp_smbl * smbl)369 static s32 disp_smbl_exit(struct disp_smbl *smbl)
370 {
371     struct disp_smbl_private_data *smblp = disp_smbl_get_priv(smbl);
372 
373     if ((smbl == NULL) || (smblp == NULL)) {
374         DE_INF("NULL hdl!\n");
375         return -1;
376     }
377 
378     return 0;
379 }
380 
disp_init_smbl(struct disp_bsp_init_para * para)381 s32 disp_init_smbl(struct disp_bsp_init_para *para)
382 {
383     u32 num_smbls;
384     u32 disp;
385     struct disp_smbl *smbl;
386     struct disp_smbl_private_data *smblp;
387 
388     DE_INF("disp_init_smbl\n");
389 
390     smbl_data_lock = 0;
391     num_smbls = bsp_disp_feat_get_num_screens();
392     smbls =
393         disp_sys_malloc(num_smbls * sizeof(struct disp_smbl));
394     if (smbls == NULL) {
395         DE_WRN("malloc memory fail!\n");
396         goto malloc_err;
397     }
398     smbl_private =
399         (struct disp_smbl_private_data *)
400         disp_sys_malloc(sizeof(struct disp_smbl_private_data)
401             * num_smbls);
402     if (smbl_private == NULL) {
403         DE_WRN("malloc memory fail!\n");
404         goto malloc_err;
405     }
406 
407     for (disp = 0; disp < num_smbls; disp++) {
408         if (!bsp_disp_feat_is_support_smbl(disp))
409             continue;
410 
411         smbl = &smbls[disp];
412         smblp = &smbl_private[disp];
413         disp_sys_mutex_init(&smblp->mlock);
414 
415         switch (disp) {
416         case 0:
417             smbl->name = "smbl0";
418             smbl->disp = 0;
419 
420             break;
421         case 1:
422             smbl->name = "smbl1";
423             smbl->disp = 1;
424 
425             break;
426         case 2:
427             smbl->name = "smbl2";
428             smbl->disp = 2;
429 
430             break;
431         }
432         smblp->shadow_protect = para->shadow_protect;
433 
434         smbl->enable = disp_smbl_enable;
435         smbl->disable = disp_smbl_disable;
436         smbl->is_enabled = disp_smbl_is_enabled;
437         smbl->init = disp_smbl_init;
438         smbl->exit = disp_smbl_exit;
439         smbl->apply = disp_smbl_apply;
440         smbl->force_apply = disp_smbl_force_apply;
441         smbl->update_regs = disp_smbl_update_regs;
442         smbl->sync = disp_smbl_sync;
443         smbl->tasklet = disp_smbl_tasklet;
444         smbl->set_manager = disp_smbl_set_manager;
445         smbl->unset_manager = disp_smbl_unset_manager;
446         smbl->set_window = disp_smbl_set_window;
447         smbl->get_window = disp_smbl_get_window;
448         smbl->update_backlight = disp_smbl_update_backlight;
449         smbl->dump = disp_smbl_dump;
450 
451         smbl->init(smbl);
452     }
453 
454     return 0;
455 
456 malloc_err:
457     disp_sys_free(smbl_private);
458     disp_sys_free(smbls);
459     smbl_private = NULL;
460     smbls = NULL;
461 
462     return -1;
463 }
464 
disp_exit_smbl(void)465 s32 disp_exit_smbl(void)
466 {
467     u32 num_smbls;
468     u32 disp;
469     struct disp_smbl *smbl;
470 
471     if (!smbls)
472         return 0;
473 
474     num_smbls = bsp_disp_feat_get_num_screens();
475     for (disp = 0; disp < num_smbls; disp++) {
476         if (!bsp_disp_feat_is_support_smbl(disp))
477             continue;
478 
479         smbl = &smbls[disp];
480         smbl->exit(smbl);
481     }
482 
483     disp_sys_free(smbl_private);
484     disp_sys_free(smbls);
485     smbl_private = NULL;
486     smbls = NULL;
487 
488     return 0;
489 }
490 
491