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