huangcm
2025-07-03 571fede27a127398697e783a06a833e37b5b58c3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*
 * Allwinner SoCs display driver.
 *
 * Copyright (C) 2016 Allwinner.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
 
/*
Description: awf waveform file decoder
Version    : v0.1
Author    : oujunxi
Date    : 2015/07/10
*/
 
#include "disp_waveform.h"
 
#define DEBUG_WAVEFILE
#ifdef DEBUG_WAVEFILE
#define WF_DBG(msg, fmt...)        printk(KERN_WARNING msg, ##fmt)
#define WF_INFO(msg, fmt...)        printk(KERN_WARNING msg, ##fmt)
#define WF_WRN(msg, fmt...)        printk(KERN_WARNING msg, ##fmt)
#define WF_ERR(msg, fmt...)        printk(KERN_ERR msg, ##fmt)
#else
#define WF_DBG(msg, fmt...)
#define WF_INFO(msg, fmt...)
#define WF_WRN(msg, fmt...)        printk(KERN_WARNING msg, ##fmt)
#define WF_ERR(msg, fmt...)        printk(KERN_ERR msg, ##fmt)
#endif
 
 
#define    C_HEADER_INFO_OFFSET        0
#define    C_HEADER_TYPE_ID_OFFSET    0        /* eink type(eink id) */
#define    C_HEADER_VERSION_STR_OFFSET    1
#define    C_HEADER_INFO_SIZE    128        /* size of awf file header */
#define    C_TEMP_TBL_OFFSET    (C_HEADER_INFO_OFFSET+C_HEADER_INFO_SIZE)
/* temperature table offset from the beginning of the awf file */
#define    C_TEMP_TBL_SIZE        32    /* temperature table size */
 
#define    C_MODE_ADDR_TBL_OFFSET (C_TEMP_TBL_OFFSET+C_TEMP_TBL_SIZE)
/* mode address table offset */
#define    C_MODE_ADDR_TBL_SIZE     64
#define    C_INIT_MODE_ADDR_OFFSET            C_MODE_ADDR_TBL_OFFSET
#define    C_GC16_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+4)
#define    C_GC4_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+8)
#define    C_DU_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+12)
#define    C_A2_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+16)
#define    C_GC16_LOCAL_MODE_ADDR_OFFSET    (C_MODE_ADDR_TBL_OFFSET+20)
#define    C_GC4_LOCAL_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+24)
#define    C_A2_IN_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+28)
#define    C_A2_OUT_MODE_ADDR_OFFSET        (C_MODE_ADDR_TBL_OFFSET+32)
 
#define    C_INIT_MODE_OFFSET    (C_MODE_ADDR_TBL_OFFSET+C_MODE_ADDR_TBL_SIZE)
 
#define    C_REAL_TEMP_AREA_NUM     15          /* max temperature range number */
#define    WF_MAX_COL        256        /* GC16, 16*16 = 256 */
 
 
typedef struct {
   u8 load_flag;        /* when awf has been loaded, init_flag = 1 */
   char *p_wf_vaddr;    /* virtual address of waveform file */
   u32 p_wf_paddr;        /* phy address of waveform file */
   EINK_PANEL_TYPE eink_panel_type;/*eink type, example PVI, OED and etc*/
   u8 wf_temp_area_tbl[C_TEMP_TBL_SIZE];    /* temperature table */
 
   /*phy address*/
   u32 p_init_wf;        /* init mode address (include temperature table address) */
   u32 p_gc16_wf;        /* gc16 mode address (include temperature table address) */
   u32 p_gc4_wf;        /* gc4 mode address (include temperature table address) */
   u32 p_du_wf;        /* du mode address (include temperature table address) */
   u32 p_A2_wf;        /* A2 mode address (include temperature table address) */
   u32 p_gc16_local_wf;    /* gc16 local mode address (include temperature table address) */
   u32 p_gc4_local_wf;    /* gc4 local mode address (include temperature table address) */
   u32 p_A2_in_wf;        /* A2 in mode address (include temperature table address) */
   u32 p_A2_out_wf;    /* A2 out mode address (include temperature table address) */
} AWF_WAVEFILE;
 
static AWF_WAVEFILE g_waveform_file;
 
/*
*Description    : get temperature range index from temperature table,
*          if temperature match TBL[id] <= temperature < TBL[id + 1]
*          condition, then temp range index = id
*Input        : temperature -- temperature of eink panel
*Output        : None
*Return        : 0 & positive -- temperature range index, negative -- fail
*/
static __s32 get_temp_range_index(int temperature)
{
   __s32 index = -EINVAL;
 
   for (index = 0; index < C_TEMP_TBL_SIZE; index++) {
       if ((g_waveform_file.wf_temp_area_tbl[index] == 0) &&
                               (index > 0))
           break;
 
       if (temperature < g_waveform_file.wf_temp_area_tbl[index])
           break;
 
   }
 
   if (index > 0)
       index -= 1;
 
   return index;
}
 
/*
Description    : get mode address according to flush mode
Input        : mode -- flush mode
Output        : None
Return        : NULL -- get mode address fail,
others -- get mode address successfully
*/
static u8 *get_mode_virt_address(enum eink_update_mode mode)
{
   u8 *p_wf_file = NULL;
   u32 offset = 0;
 
   switch (mode) {
   case EINK_INIT_MODE:
   {
       offset = (u32)g_waveform_file.p_init_wf -
                       g_waveform_file.p_wf_paddr;
       p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset;
       break;
   }
 
   case EINK_DU_MODE:
   case EINK_DU_RECT_MODE:
   {
       offset = (u32)g_waveform_file.p_du_wf -
                       g_waveform_file.p_wf_paddr;
       p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset;
       break;
   }
 
   case EINK_GC16_MODE:
   case EINK_GC16_RECT_MODE:
   {
       offset = (u32)g_waveform_file.p_gc16_wf -
           g_waveform_file.p_wf_paddr;
       p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset;
       break;
   }
 
   case EINK_A2_MODE:
   case EINK_A2_RECT_MODE:
   {
       offset = (u32)g_waveform_file.p_A2_wf -
           g_waveform_file.p_wf_paddr;
       p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset;
       break;
   }
 
   case EINK_GC16_LOCAL_MODE:
   case EINK_GC16_LOCAL_RECT_MODE:
   {
       offset = (u32)g_waveform_file.p_gc16_local_wf -
           g_waveform_file.p_wf_paddr;
       p_wf_file = (u8 *)g_waveform_file.p_wf_vaddr + offset;
       break;
   }
 
   default:
   {
       WF_WRN("unkown mode(0x%x)\n", mode);
       p_wf_file = NULL;
       break;
   }
   }
 
   return p_wf_file;
}
 
/*
*Description    : get mode address according to flush mode
*Input        : mode -- flush mode
*Output        : None
*Return        : NULL -- get mode address fail,
*              others -- get mode address successfully
*/
static u32 get_mode_phy_address(enum eink_update_mode mode)
{
   u32 phy_addr = 0;
 
   switch (mode) {
   case EINK_INIT_MODE:
   {
       phy_addr = g_waveform_file.p_init_wf;
       break;
   }
 
   case EINK_DU_MODE:
   case EINK_DU_RECT_MODE:
   {
       phy_addr = g_waveform_file.p_du_wf;
       break;
   }
 
   case EINK_GC16_MODE:
   case EINK_GC16_RECT_MODE:
   {
       phy_addr = g_waveform_file.p_gc16_wf;
       break;
   }
 
   case EINK_A2_MODE:
   case EINK_A2_RECT_MODE:
   {
       phy_addr = g_waveform_file.p_A2_wf;
       break;
   }
 
   case EINK_GC16_LOCAL_MODE:
   case EINK_GC16_LOCAL_RECT_MODE:
   {
       phy_addr = g_waveform_file.p_gc16_local_wf;
       break;
   }
 
   default:
   {
       WF_WRN("unkown mode(0x%x)\n", mode);
       phy_addr = 0;
       break;
   }
   }
 
   return phy_addr;
}
 
 
/*
Description    : load waveform file to memory, and save each flush mode
         of address. This function should be called before waveform
         data has been used.
Input        : path -- path of waveform file
Output        : None
Return        : 0 -- load waveform ok, others -- fail
*/
extern void *disp_malloc(u32 num_bytes, void *phy_addr);
 
__s32 init_waveform(const char *path)
{
   struct file *fp = NULL;
   __s32 file_len = 0;
   __s32 read_len = 0;
   mm_segment_t fs;
   loff_t pos;
   __u32 wf_buffer_len = 0;           /* the len of waveform file */
   u32 *pAddr = NULL;
   __s32 ret = -EINVAL;
 
   if (path == NULL) {
       WF_ERR("path is null\n");
       return -EINVAL;
   }
 
   WF_DBG("starting to load awf waveform file(%s)\n", path);
   fp = filp_open(path, O_RDONLY, 0);
   if (IS_ERR(fp))    {
       WF_ERR("fail to open waveform file(%s)", path);
       return -EBADF;
   }
 
   memset(&g_waveform_file, 0, sizeof(g_waveform_file));
   fs = get_fs();
   set_fs(KERNEL_DS);
   pos = 0;
   file_len = fp->f_path.dentry->d_inode->i_size;
   wf_buffer_len = file_len+1023;
 
   g_waveform_file.p_wf_vaddr = (char *)disp_malloc(wf_buffer_len,
                   (u32 *)(&g_waveform_file.p_wf_paddr));
   /* waveform vaddr */
   if (g_waveform_file.p_wf_vaddr == NULL) {
       WF_ERR("fail to alloc memory for waveform file\n");
       ret = -ENOMEM;
       goto error;
   }
 
   read_len = vfs_read(fp, (char *)g_waveform_file.p_wf_vaddr, file_len, &pos);
   if (read_len != file_len) {
       WF_ERR("miss data(read=%d byte, file=%d byte) when read wavefile\n",
                           read_len, file_len);
       ret = -EAGAIN;
       goto error;
   }
 
   g_waveform_file.eink_panel_type = *((u8 *)g_waveform_file.p_wf_vaddr);
   WF_DBG("eink type=0x%x\n", g_waveform_file.eink_panel_type);
 
   /* starting to load data */
   memcpy(g_waveform_file.wf_temp_area_tbl,
           (g_waveform_file.p_wf_vaddr+C_TEMP_TBL_OFFSET),
           C_TEMP_TBL_SIZE);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_INIT_MODE_ADDR_OFFSET);
   g_waveform_file.p_init_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC16_MODE_ADDR_OFFSET);
   g_waveform_file.p_gc16_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_GC4_MODE_ADDR_OFFSET);
   g_waveform_file.p_gc4_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_DU_MODE_ADDR_OFFSET);
   g_waveform_file.p_du_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_MODE_ADDR_OFFSET);
   g_waveform_file.p_A2_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr +
                   C_GC16_LOCAL_MODE_ADDR_OFFSET);
   g_waveform_file.p_gc16_local_wf =
               (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr +
                       C_GC4_LOCAL_MODE_ADDR_OFFSET);
   g_waveform_file.p_gc4_local_wf =
               (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_IN_MODE_ADDR_OFFSET);
   g_waveform_file.p_A2_in_wf = (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   pAddr = (u32 *)(g_waveform_file.p_wf_vaddr + C_A2_OUT_MODE_ADDR_OFFSET);
   g_waveform_file.p_A2_out_wf =
               (u32)(g_waveform_file.p_wf_paddr + *pAddr);
 
   if (fp) {
       filp_close(fp, NULL);
       set_fs(fs);
   }
 
   g_waveform_file.load_flag = 1;
 
   WF_DBG("load waveform file(%s) successfully\n", path);
   return 0;
 
error:
   if (g_waveform_file.p_wf_vaddr != NULL)   {
       vfree(g_waveform_file.p_wf_vaddr);
       g_waveform_file.p_wf_vaddr = NULL;
   }
 
   if (fp) {
       filp_close(fp, NULL);
       set_fs(fs);
   }
 
   return ret;
}
 
/*
Description: get eink panel type, for example, 3 stands for ED060XC3(4-bit),
                          4 stands for ED060XD4(5-bit)
Input: None
Output: type -- save the type of eink panel
Return: 0 -- get eink panel type successfully, others -- fail
*/
int get_eink_panel_type(EINK_PANEL_TYPE *type)
{
   if (g_waveform_file.load_flag != 1) {
       WF_ERR("waveform hasn't init yet, pls init first\n");
       return -EAGAIN;
   }
 
   if (type == NULL) {
       WF_ERR("input param is null\n");
       return -EINVAL;
   }
 
   *type = g_waveform_file.eink_panel_type;
   return 0;
}
 
/*
Description: get bit number of eink panel, 4-bit stands for 16 grayscale,
                      5-bit stands for 32 grayscale
Input: None
Output: bit_num -- save the bit number of eink panel
Return: 0 -- get eink panel type successfully, others -- fail
*/
int get_eink_panel_bit_num(enum  eink_bit_num *bit_num)
{
   if (g_waveform_file.load_flag != 1) {
       WF_ERR("waveform hasn't init yet, pls init first\n");
       return -EAGAIN;
   }
 
   if (bit_num == NULL) {
       WF_ERR("input param is null\n");
       return -EINVAL;
   }
 
   if (g_waveform_file.eink_panel_type == ED060XD4)
       *bit_num = EINK_BIT_5;
   else
       *bit_num = EINK_BIT_4;
 
   return 0;
}
 
 
/*
Description: get waveform data address according to mode and temperature
Input: None
Output: type -- save the type of eink panel
Return: 0 -- get eink panel type successfully, others -- fail
*/
int get_waveform_data(enum eink_update_mode mode, u32 temp,
               u32 *total_frames, u32 *wf_buf)
{
   u32 p_mode_phy_addr = 0;
   u8 *p_mode_virt_addr = NULL;
   u32 mode_temp_offset = 0;
   u8 *p_mode_temp_addr = NULL;
   /* u16 *p_pointer = NULL; */
   u32 temp_range_id = 0;
 
   if (g_waveform_file.load_flag != 1) {
       WF_ERR("waveform hasn't init yet, pls init first\n");
       return -EAGAIN;
   }
 
   if ((total_frames == NULL) || (wf_buf == NULL)) {
       WF_ERR("input param is null\n");
       return -EINVAL;
   }
 
   p_mode_virt_addr = get_mode_virt_address(mode);
   if (p_mode_virt_addr == NULL) {
       WF_ERR("get mode virturl address fail, mode=0x%x\n", mode);
       return -EINVAL;
   }
 
   p_mode_phy_addr = get_mode_phy_address(mode);
   if (p_mode_phy_addr == 0) {
       WF_ERR("get mode phy address fail, mode=0x%x\n", mode);
       return -EINVAL;
   }
 
   temp_range_id = get_temp_range_index(temp);
   if (temp_range_id < 0) {
       WF_ERR("get temp range index fail, temp=0x%x\n", temp);
       return -EINVAL;
   }
 
   mode_temp_offset =  *((u32 *)p_mode_virt_addr + temp_range_id);
   p_mode_temp_addr = (u8 *)p_mode_virt_addr + mode_temp_offset;
 
   *total_frames = *((u16 *)p_mode_temp_addr);
   mode_temp_offset = mode_temp_offset + 4;
   /* skip total frame(2 Byte) and dividor(2 Byte) */
 
   *wf_buf = p_mode_phy_addr + mode_temp_offset;
 
   return 0;
}
 
 
/*
Description    : free memory that used by waveform file, after that, waveform
         data cannot be used until init_waveform function has been
         called. This function should be called when unload module.
Input        : None
Output        : None
Return        : None
*/
void free_waveform(void)
{
   if (g_waveform_file.p_wf_vaddr != NULL)   {
       vfree(g_waveform_file.p_wf_vaddr);
       g_waveform_file.p_wf_vaddr = NULL;
   }
 
   g_waveform_file.load_flag = 0;
   return;
}