• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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