1 /*
2 * Allwinner SoCs display driver.
3 *
4 * This file is licensed under the terms of the GNU General Public
5 * License version 2. This program is licensed "as is" without any
6 * warranty of any kind, whether express or implied.
7 */
8
9 /**
10 * All Winner Tech, All Right Reserved. 2014-2015 Copyright (c)
11 *
12 * File name : de_smbl.c
13 *
14 * Description : display engine 2.0 smbl basic function definition
15 *
16 * History : 2014/05/13 vito cheng v0.1 Initial version
17 *
18 */
19 #include "de_feat.h"
20 #include "de_smbl_type.h"
21 #include "de_smbl.h"
22 #include "de_rtmx.h"
23
24 #if defined(SUPPORT_SMBL)
25
26 #include "de_smbl_tab.h"
27 /* SMBL offset based on RTMX */
28 #define SMBL_OFST 0xa0000
29
30 enum {
31 SMBL_EN_REG_BLK = 0,
32 SMBL_CTL_REG_BLK,
33 SMBL_HIST_REG_BLK,
34 SMBL_CSC_REG_BLK,
35 SMBL_FILTER_REG_BLK,
36 SMBL_LUT_REG_BLK,
37 SMBL_REG_BLK_NUM,
38 };
39
40 struct de_smbl_private {
41 struct de_reg_mem_info reg_mem_info;
42 u32 reg_blk_num;
43 struct de_reg_block reg_blks[SMBL_REG_BLK_NUM];
44
45 void (*set_blk_dirty)(struct de_smbl_private *priv,
46 u32 blk_id, u32 dirty);
47 };
48
49 static struct de_smbl_private smbl_priv[DE_NUM];
50
get_smbl_reg(struct de_smbl_private * priv)51 static inline struct smbl_reg *get_smbl_reg(struct de_smbl_private *priv)
52 {
53 return (struct smbl_reg *)(priv->reg_blks[0].vir_addr);
54 }
55
smbl_set_block_dirty(struct de_smbl_private * priv,u32 blk_id,u32 dirty)56 static void smbl_set_block_dirty(
57 struct de_smbl_private *priv, u32 blk_id, u32 dirty)
58 {
59 priv->reg_blks[blk_id].dirty = dirty;
60 }
61
smbl_set_rcq_head_dirty(struct de_smbl_private * priv,u32 blk_id,u32 dirty)62 static void smbl_set_rcq_head_dirty(
63 struct de_smbl_private *priv, u32 blk_id, u32 dirty)
64 {
65 if (priv->reg_blks[blk_id].rcq_hd) {
66 priv->reg_blks[blk_id].rcq_hd->dirty.dwval = dirty;
67 } else {
68 DE_WRN("rcq_head is null ! blk_id=%d\n", blk_id);
69 }
70 }
71
72 static struct __smbl_status_t *g_smbl_status[DE_NUM];
73 /* POINTER of LGC tab */
74 static u16 *pttab[DE_NUM];
75
76 /* when bsp_disp_lcd_get_bright() exceed PWRSAVE_PROC_THRES, STOP PWRSAVE */
77 static u32 PWRSAVE_PROC_THRES = 85;
78
smbl_init(u32 disp,uintptr_t reg_base)79 static s32 smbl_init(u32 disp, uintptr_t reg_base)
80 {
81 struct de_smbl_private *priv = &smbl_priv[disp];
82 struct de_reg_mem_info *reg_mem_info = &(priv->reg_mem_info);
83 struct de_reg_block *reg_blk;
84 uintptr_t base;
85 u32 rcq_used = de_feat_is_using_rcq(disp);
86
87 u32 lcdgamma;
88 s32 value = 1;
89 s8 primary_key[20];
90 s32 ret;
91
92 base = reg_base + DE_DISP_OFFSET(disp) + DISP_DEP_OFFSET;
93
94 reg_mem_info->size = sizeof(struct smbl_reg);
95 reg_mem_info->vir_addr = (u8 *)de_top_reg_memory_alloc(
96 reg_mem_info->size, (void *)&(reg_mem_info->phy_addr),
97 rcq_used);
98 if (reg_mem_info->vir_addr == NULL) {
99 DE_WRN("alloc smbl[%d] mm fail!size=0x%x\n",
100 disp, reg_mem_info->size);
101 return -1;
102 }
103
104 priv->reg_blk_num = SMBL_REG_BLK_NUM;
105
106 reg_blk = &(priv->reg_blks[SMBL_EN_REG_BLK]);
107 reg_blk->phy_addr = reg_mem_info->phy_addr;
108 reg_blk->vir_addr = reg_mem_info->vir_addr;
109 reg_blk->size = 0x4;
110 reg_blk->reg_addr = (u8 __iomem *)base;
111
112 reg_blk = &(priv->reg_blks[SMBL_CTL_REG_BLK]);
113 reg_blk->phy_addr = reg_mem_info->phy_addr + 0x4;
114 reg_blk->vir_addr = reg_mem_info->vir_addr + 0x4;
115 reg_blk->size = 0x38;
116 reg_blk->reg_addr = (u8 __iomem *)(base + 0x4);
117
118 reg_blk = &(priv->reg_blks[SMBL_HIST_REG_BLK]);
119 reg_blk->phy_addr = reg_mem_info->phy_addr + 0x60;
120 reg_blk->vir_addr = reg_mem_info->vir_addr + 0x60;
121 reg_blk->size = 0x20;
122 reg_blk->reg_addr = (u8 __iomem *)(base + 0x60);
123
124 reg_blk = &(priv->reg_blks[SMBL_CSC_REG_BLK]);
125 reg_blk->phy_addr = reg_mem_info->phy_addr + 0xc0;
126 reg_blk->vir_addr = reg_mem_info->vir_addr + 0xc0;
127 reg_blk->size = 0x30;
128 reg_blk->reg_addr = (u8 __iomem *)(base + 0xc0);
129
130 reg_blk = &(priv->reg_blks[SMBL_FILTER_REG_BLK]);
131 reg_blk->phy_addr = reg_mem_info->phy_addr + 0xf0;
132 reg_blk->vir_addr = reg_mem_info->vir_addr + 0xf0;
133 reg_blk->size = 0x110;
134 reg_blk->reg_addr = (u8 __iomem *)(base + 0xf0);
135
136 reg_blk = &(priv->reg_blks[SMBL_LUT_REG_BLK]);
137 reg_blk->phy_addr = reg_mem_info->phy_addr + 0x200;
138 reg_blk->vir_addr = reg_mem_info->vir_addr + 0x200;
139 reg_blk->size = 0x200;
140 reg_blk->reg_addr = (u8 __iomem *)(base + 0x200);
141
142 if (rcq_used)
143 priv->set_blk_dirty = smbl_set_rcq_head_dirty;
144 else
145 priv->set_blk_dirty = smbl_set_block_dirty;
146
147 g_smbl_status[disp] = kmalloc(sizeof(struct __smbl_status_t),
148 GFP_KERNEL | __GFP_ZERO);
149 if (g_smbl_status[disp] == NULL) {
150 __wrn("malloc g_smbl_status[%d] memory fail! size=0x%x\n", disp,
151 (u32)sizeof(struct __smbl_status_t));
152 return -1;
153 }
154 g_smbl_status[disp]->isenable = 0;
155 g_smbl_status[disp]->runtime = 0;
156 g_smbl_status[disp]->dimming = 256;
157
158
159 sprintf(primary_key, "lcd%d", disp);
160 ret = disp_sys_script_get_item(primary_key, "lcdgamma4iep", &value, 1);
161 if (ret != 1) {
162 /* default gamma = 2.2 */
163 lcdgamma = 6;
164 } else {
165 /* DE_INF("lcdgamma4iep for lcd%d = %d.\n", disp, value); */
166 if (value > 30 || value < 10) {
167 /* DE_WRN("lcdgamma4iep for lcd%d too small or too
168 * large. default value 22 will be set. please set it
169 * between 10 and 30 to make it valid.\n",disp);
170 */
171 lcdgamma = 6;
172 } else {
173 lcdgamma = (value - 10) / 2;
174 }
175 }
176 pttab[disp] = pwrsv_lgc_tab[128 * lcdgamma];
177
178 ret = disp_sys_script_get_item(primary_key,
179 "smartbl_low_limit", &value, 1);
180 if (ret != 1) {
181 /* DE_INF("smartbl_low_limit for lcd%d not exist.\n", disp); */
182 } else {
183 /* DE_INF("smartbl_low_limit for lcd%d = %d.\n", disp, value); */
184 if (value > 255 || value < 20)
185 __inf("smartbl_low_limit of lcd%d must be in<20,255>\n",
186 disp);
187 else
188 PWRSAVE_PROC_THRES = value;
189 }
190
191 ret = disp_sys_script_get_item(primary_key, "lcd_backlight", &value, 1);
192 if (ret == 1)
193 g_smbl_status[disp]->backlight = value;
194
195 return 0;
196 }
197
de_smbl_init(uintptr_t reg_base)198 s32 de_smbl_init(uintptr_t reg_base)
199 {
200 u32 disp;
201 u32 disp_num = de_feat_get_num_screens();
202
203 for (disp = 0; disp < disp_num; ++disp) {
204 if (de_feat_is_support_smbl(disp))
205 smbl_init(disp, reg_base);
206 }
207
208 return 0;
209 }
210
de_smbl_exit(void)211 s32 de_smbl_exit(void)
212 {
213 /* todo */
214 return 0;
215 }
216
de_smbl_get_reg_blocks(u32 disp,struct de_reg_block ** blks,u32 * blk_num)217 s32 de_smbl_get_reg_blocks(u32 disp,
218 struct de_reg_block **blks, u32 *blk_num)
219 {
220 struct de_smbl_private *priv = &(smbl_priv[disp]);
221 u32 i, num;
222
223 if (blks == NULL) {
224 *blk_num = priv->reg_blk_num;
225 return 0;
226 }
227
228 if (*blk_num >= priv->reg_blk_num) {
229 num = priv->reg_blk_num;
230 } else {
231 num = *blk_num;
232 DE_WRN("should not happen\n");
233 }
234 for (i = 0; i < num; ++i)
235 blks[i] = priv->reg_blks + i;
236
237 *blk_num = i;
238 return 0;
239
240 }
241
de_smbl_get_hist(u32 disp,u32 * cnt)242 static s32 de_smbl_get_hist(u32 disp, u32 *cnt)
243 {
244 /* Read histogram */
245 u8 *reg_addr = (u8 *)
246 (smbl_priv[disp].reg_blks[SMBL_HIST_REG_BLK].reg_addr);
247 memcpy((u8 *)cnt, reg_addr, sizeof(u32) * IEP_LH_INTERVAL_NUM);
248
249 return 0;
250 }
251
252 /**
253 * function : PWRSAVE_CORE(u32 disp)
254 *
255 * description : Power Save alg core
256 * Dynamic adjust backlight and lgc gain through screen
257 * content and user backlight setting
258 * parameters :
259 * disp <screen index>
260 * return :
261 * lgcaddr <LGC table pointer>
262 * history :
263 * Add HANG-UP DETECT: When use PWRSAVE_CORE in LOW
264 * referential backlight condiction, backlight will
265 * flicker. So STOP use PWRSAVE_CORE.
266 */
PWRSAVE_CORE(u32 disp)267 static u16 *PWRSAVE_CORE(u32 disp)
268 {
269 s32 i;
270 u32 hist_region_num = 8;
271 u32 histcnt[IEP_LH_INTERVAL_NUM];
272 u32 hist[IEP_LH_INTERVAL_NUM], p95;
273 u32 size = 0;
274 u32 min_adj_index;
275 u16 *lgcaddr;
276 u32 drc_filter_tmp = 0;
277 u32 backlight, thres_low, thres_high;
278 static u32 printf_cnt;
279
280 printf_cnt = 0;
281 backlight = g_smbl_status[disp]->backlight;
282
283 if (backlight < PWRSAVE_PROC_THRES) {
284 /* if current backlight lt PWRSAVE_PROC_THRES, close smart */
285 /* backlight function */
286 memset(g_smbl_status[disp]->min_adj_index_hist, 255,
287 sizeof(u8) * IEP_LH_PWRSV_NUM);
288 lgcaddr = (u16 *)((uintptr_t) pttab[disp] +
289 ((128 - 1) << 9));
290
291 g_smbl_status[disp]->dimming = 256;
292 } else {
293 /* if current backlight ge PWRSAVE_PROC_THRES, */
294 /* open smart backlight function */
295 p95 = 0;
296
297 hist_region_num =
298 (hist_region_num > 8) ? 8 : IEP_LH_INTERVAL_NUM;
299
300 /* read histogram result */
301 de_smbl_get_hist(disp, histcnt);
302
303 /*for (i=0; i<IEP_LH_INTERVAL_NUM; i++) {
304 * size += histcnt[i];
305 *}
306 *size = (size==0) ? 1 : size;
307 *
308 *calculate some var
309 *hist[0] = (histcnt[0]*100)/size;
310 *for (i = 1; i < hist_region_num; i++) {
311 * hist[i] = (histcnt[i]*100)/size + hist[i-1];
312 *}
313 */
314 size = g_smbl_status[disp]->size;
315
316 /* calculate some var */
317 hist[0] = (histcnt[0]) / size;
318 for (i = 1; i < hist_region_num; i++)
319 hist[i] = (histcnt[i]) / size + hist[i - 1];
320
321 #if 0
322 for (i = 0; i < hist_region_num; i++)
323 if (hist[i] >= 95) {
324 p95 = hist_thres_pwrsv[i];
325 break;
326 }
327
328 /* sometime, hist[hist_region_num - 1] may less than 95 */
329 /* due to integer calc */
330 if (i == hist_region_num)
331 p95 = hist_thres_pwrsv[7];
332
333 #else /* fix bug of some thing bright appear in a dark background */
334 for (i = hist_region_num - 1; i >= 0; i--)
335 if (hist[i] < 95)
336 break;
337
338 /* sometime, hist[hist_region_num - 1] may less than 95 */
339 /* due to integer calc */
340 if (i == hist_region_num - 1)
341 p95 = hist_thres_pwrsv[7];
342 else if (i == 0)
343 p95 = hist_thres_pwrsv[0];
344 else
345 p95 = hist_thres_pwrsv[i + 1];
346 #endif
347 min_adj_index = p95;
348
349 /* __inf("min_adj_index: %d\n", min_adj_index); */
350 for (i = 0; i < IEP_LH_PWRSV_NUM - 1; i++) {
351 g_smbl_status[disp]->min_adj_index_hist[i] =
352 g_smbl_status[disp]->min_adj_index_hist[i + 1];
353 }
354 g_smbl_status[disp]->min_adj_index_hist[IEP_LH_PWRSV_NUM - 1] =
355 min_adj_index;
356
357 for (i = 0; i < IEP_LH_PWRSV_NUM; i++) {
358 /* drc_filter_total += drc_filter[i]; */
359 drc_filter_tmp +=
360 drc_filter[i] *
361 g_smbl_status[disp]->min_adj_index_hist[i];
362 }
363 /* min_adj_index = drc_filter_tmp/drc_filter_total; */
364 min_adj_index = drc_filter_tmp >> 10;
365
366 thres_low = PWRSAVE_PROC_THRES;
367 thres_high =
368 PWRSAVE_PROC_THRES + ((255 - PWRSAVE_PROC_THRES) / 5);
369 if (backlight < thres_high) {
370 min_adj_index =
371 min_adj_index + (255 - min_adj_index) *
372 (thres_high - backlight) / (thres_high - thres_low);
373 }
374 min_adj_index =
375 (min_adj_index >= 255) ?
376 255 : ((min_adj_index < hist_thres_pwrsv[0]) ?
377 hist_thres_pwrsv[0] : min_adj_index);
378
379 g_smbl_status[disp]->dimming = min_adj_index + 1;
380
381 lgcaddr =
382 (u16 *)((uintptr_t) pttab[disp] +
383 ((min_adj_index - 128) << 9));
384
385 if (printf_cnt == 600) {
386 __inf("save backlight power: %d percent\n",
387 (256 - (u32) min_adj_index) * 100 / 256);
388 printf_cnt = 0;
389 } else {
390 printf_cnt++;
391 }
392
393 }
394 return lgcaddr;
395 }
396
de_smbl_set_lut(u32 disp,u16 * lut)397 static s32 de_smbl_set_lut(u32 disp, u16 *lut)
398 {
399 struct de_smbl_private *priv = &(smbl_priv[disp]);
400 struct smbl_reg *reg = get_smbl_reg(priv);
401
402 /* set lut to smbl lut SRAM */
403 memcpy((void *)reg->drclgcoff, (void *)lut, sizeof(reg->drclgcoff));
404 priv->set_blk_dirty(priv, SMBL_LUT_REG_BLK, 1);
405 return 0;
406 }
407
de_smbl_tasklet(u32 disp)408 s32 de_smbl_tasklet(u32 disp)
409 {
410 static u32 smbl_frame_cnt[DE_NUM];
411
412 u16 *lut;
413
414 if (g_smbl_status[disp]->isenable
415 && ((SMBL_FRAME_MASK == (smbl_frame_cnt[disp] % 2))
416 || (SMBL_FRAME_MASK == 0x2))) {
417 if (g_smbl_status[disp]->runtime > 0) {
418 /* POWER SAVE ALG */
419 lut = (u16 *)PWRSAVE_CORE(disp);
420 } else {
421 lut = (u16 *)pwrsv_lgc_tab[128 - 1];
422 }
423
424 de_smbl_set_lut(disp, lut);
425
426 if (g_smbl_status[disp]->runtime == 0)
427 g_smbl_status[disp]->runtime++;
428 }
429 smbl_frame_cnt[disp]++;
430
431 return 0;
432 }
433
de_smbl_enable(u32 disp,u32 en)434 static s32 de_smbl_enable(u32 disp, u32 en)
435 {
436 struct de_smbl_private *priv = &(smbl_priv[disp]);
437 struct smbl_reg *reg = get_smbl_reg(priv);
438
439 reg->gnectl.bits.en = en;
440 priv->set_blk_dirty(priv, SMBL_EN_REG_BLK, 1);
441 return 0;
442 }
443
de_smbl_set_para(u32 disp,u32 width,u32 height)444 static s32 de_smbl_set_para(u32 disp, u32 width, u32 height)
445 {
446 struct de_smbl_private *priv = &(smbl_priv[disp]);
447 struct smbl_reg *reg = get_smbl_reg(priv);
448
449 reg->gnectl.bits.mod = 2;
450 reg->drcsize.dwval = (height - 1) << 16 | (width - 1);
451 reg->drcctl.bits.hsv_en = 1;
452 reg->drcctl.bits.db_en = 1;
453 reg->drc_set.dwval = 0;
454 reg->lhctl.dwval = 0;
455
456 reg->lhthr0.dwval =
457 (hist_thres_drc[3] << 24) | (hist_thres_drc[2] << 16) |
458 (hist_thres_drc[1] << 8) | (hist_thres_drc[0]);
459 reg->lhthr1.dwval =
460 (hist_thres_drc[6] << 16) | (hist_thres_drc[5] << 8) |
461 (hist_thres_drc[4]);
462
463 /* out_csc coeff */
464 memcpy((void *)(priv->reg_blks[SMBL_CSC_REG_BLK].reg_addr),
465 (u8 *)csc_bypass_coeff, sizeof(u32) * 12);
466
467 /* filter coeff */
468 memcpy((void *)(priv->reg_blks[SMBL_FILTER_REG_BLK].reg_addr),
469 (u8 *)smbl_filter_coeff, sizeof(u8) * 272);
470
471 priv->set_blk_dirty(priv, SMBL_EN_REG_BLK, 1);
472 priv->set_blk_dirty(priv, SMBL_CTL_REG_BLK, 1);
473
474 g_smbl_status[disp]->size = width * height / 100;
475
476 return 0;
477 }
478
de_smbl_set_window(u32 disp,u32 win_enable,struct disp_rect window)479 static s32 de_smbl_set_window(u32 disp, u32 win_enable,
480 struct disp_rect window)
481 {
482 struct de_smbl_private *priv = &(smbl_priv[disp]);
483 struct smbl_reg *reg = get_smbl_reg(priv);
484
485 reg->drcctl.bits.win_en = win_enable;
486
487 if (win_enable) {
488 reg->drc_wp0.bits.win_left = window.x;
489 reg->drc_wp0.bits.win_top = window.y;
490 reg->drc_wp1.bits.win_right =
491 window.x + window.width - 1;
492 reg->drc_wp1.bits.win_bottom =
493 window.y + window.height - 1;
494 }
495 priv->set_blk_dirty(priv, SMBL_CTL_REG_BLK, 1);
496 return 0;
497 }
498
de_smbl_apply(u32 disp,struct disp_smbl_info * info)499 s32 de_smbl_apply(u32 disp, struct disp_smbl_info *info)
500 {
501 __inf("disp%d, en=%d, win=<%d,%d,%d,%d>\n", disp, info->enable,
502 info->window.x, info->window.y, info->window.width,
503 info->window.height);
504 g_smbl_status[disp]->backlight = info->backlight;
505 if (info->flags & SMBL_DIRTY_ENABLE) {
506 g_smbl_status[disp]->isenable = info->enable;
507 de_smbl_enable(disp, g_smbl_status[disp]->isenable);
508
509 if (g_smbl_status[disp]->isenable) {
510 g_smbl_status[disp]->runtime = 0;
511 memset(g_smbl_status[disp]->min_adj_index_hist, 255,
512 sizeof(u8) * IEP_LH_PWRSV_NUM);
513 de_smbl_set_para(disp, info->size.width,
514 info->size.height);
515 } else {
516 }
517 }
518 g_smbl_status[disp]->backlight = info->backlight;
519
520 if (info->flags & SMBL_DIRTY_WINDOW)
521 de_smbl_set_window(disp, 1, info->window);
522
523 return 0;
524
525 }
526
de_smbl_update_regs(u32 disp)527 s32 de_smbl_update_regs(u32 disp)
528 {
529 return 0;
530 }
531
de_smbl_get_status(u32 disp)532 s32 de_smbl_get_status(u32 disp)
533 {
534 return g_smbl_status[disp]->dimming;
535 }
536
537 #else
538
de_smbl_update_regs(u32 disp)539 s32 de_smbl_update_regs(u32 disp)
540 {
541 return 0;
542 }
543
de_smbl_tasklet(u32 disp)544 s32 de_smbl_tasklet(u32 disp)
545 {
546 return 0;
547 }
548
de_smbl_apply(u32 disp,struct disp_smbl_info * info)549 s32 de_smbl_apply(u32 disp, struct disp_smbl_info *info)
550 {
551 return 0;
552 }
553
de_smbl_sync(u32 disp)554 s32 de_smbl_sync(u32 disp)
555 {
556 return 0;
557 }
558
de_smbl_init(uintptr_t reg_base)559 s32 de_smbl_init(uintptr_t reg_base)
560 {
561 return 0;
562 }
563
de_smbl_get_reg_blocks(u32 disp,struct de_reg_block ** blks,u32 * blk_num)564 s32 de_smbl_get_reg_blocks(u32 disp,
565 struct de_reg_block **blks, u32 *blk_num)
566 {
567 *blk_num = 0;
568 return 0;
569 }
570
571
de_smbl_get_status(u32 disp)572 s32 de_smbl_get_status(u32 disp)
573 {
574 return 0;
575 }
576 #endif
577