1 /*
2 * Copyright 2015 Rockchip Electronics Co. LTD
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define MODULE_TAG "vepu541_common"
18
19 #include <string.h>
20
21 #include "mpp_mem.h"
22 #include "mpp_debug.h"
23 #include "mpp_common.h"
24 #include "vepu5xx_common.h"
25 #include "vepu541_common.h"
26
vepu541_get_roi_buf_size(RK_S32 w,RK_S32 h)27 RK_S32 vepu541_get_roi_buf_size(RK_S32 w, RK_S32 h)
28 {
29 RK_S32 stride_h = MPP_ALIGN(w, 64) / 16;
30 RK_S32 stride_v = MPP_ALIGN(h, 64) / 16;
31 RK_S32 buf_size = stride_h * stride_v * sizeof(Vepu541RoiCfg);
32
33 /* extra 32 byte for hardware access padding */
34 return buf_size + 32;
35 }
36
vepu541_set_one_roi(void * buf,MppEncROIRegion * region,RK_S32 w,RK_S32 h)37 MPP_RET vepu541_set_one_roi(void *buf, MppEncROIRegion *region, RK_S32 w, RK_S32 h)
38 {
39 Vepu541RoiCfg *ptr = (Vepu541RoiCfg *)buf;
40 RK_S32 mb_w = MPP_ALIGN(w, 16) / 16;
41 RK_S32 mb_h = MPP_ALIGN(h, 16) / 16;
42 RK_S32 stride_h = MPP_ALIGN(mb_w, 4);
43 Vepu541RoiCfg cfg;
44 MPP_RET ret = MPP_NOK;
45
46 if (NULL == buf || NULL == region) {
47 mpp_err_f("invalid buf %p roi %p\n", buf, region);
48 goto DONE;
49 }
50
51 RK_S32 roi_width = (region->w + 15) / 16;
52 RK_S32 roi_height = (region->h + 15) / 16;
53 RK_S32 pos_x_init = region->x / 16;
54 RK_S32 pos_y_init = region->y / 16;
55 RK_S32 pos_x_end = pos_x_init + roi_width;
56 RK_S32 pos_y_end = pos_y_init + roi_height;
57 RK_S32 x, y;
58
59 pos_x_end = MPP_MIN(pos_x_end, mb_w);
60 pos_y_end = MPP_MIN(pos_y_end, mb_h);
61 pos_x_init = MPP_MAX(pos_x_init, 0);
62 pos_y_init = MPP_MAX(pos_y_init, 0);
63
64 mpp_assert(pos_x_end > pos_x_init);
65 mpp_assert(pos_y_end > pos_y_init);
66
67 cfg.force_intra = region->intra;
68 cfg.reserved = 0;
69 cfg.qp_area_idx = region->qp_area_idx;
70 // NOTE: When roi is enabled the qp_area_en should be one.
71 cfg.qp_area_en = 1; // region->area_map_en;
72 cfg.qp_adj = region->quality;
73 cfg.qp_adj_mode = region->abs_qp_en;
74
75 ptr += pos_y_init * stride_h + pos_x_init;
76 roi_width = pos_x_end - pos_x_init;
77 roi_height = pos_y_end - pos_y_init;
78
79 for (y = 0; y < roi_height; y++) {
80 Vepu541RoiCfg *dst = ptr;
81
82 for (x = 0; x < roi_width; x++, dst++)
83 memcpy(dst, &cfg, sizeof(cfg));
84
85 ptr += stride_h;
86 }
87 DONE:
88 return ret;
89 }
90
vepu541_set_roi(void * buf,MppEncROICfg * roi,RK_S32 w,RK_S32 h)91 MPP_RET vepu541_set_roi(void *buf, MppEncROICfg *roi, RK_S32 w, RK_S32 h)
92 {
93 MppEncROIRegion *region = roi->regions;
94 Vepu541RoiCfg *ptr = (Vepu541RoiCfg *)buf;
95 RK_S32 mb_w = MPP_ALIGN(w, 16) / 16;
96 RK_S32 mb_h = MPP_ALIGN(h, 16) / 16;
97 RK_S32 stride_h = MPP_ALIGN(mb_w, 4);
98 RK_S32 stride_v = MPP_ALIGN(mb_h, 4);
99 Vepu541RoiCfg cfg;
100 MPP_RET ret = MPP_NOK;
101 RK_S32 i;
102
103 if (NULL == buf || NULL == roi) {
104 mpp_err_f("invalid buf %p roi %p\n", buf, roi);
105 goto DONE;
106 }
107
108 cfg.force_intra = 0;
109 cfg.reserved = 0;
110 cfg.qp_area_idx = 0;
111 cfg.qp_area_en = 1;
112 cfg.qp_adj = 0;
113 cfg.qp_adj_mode = 0;
114
115 /* step 1. reset all the config */
116 for (i = 0; i < stride_h * stride_v; i++, ptr++)
117 memcpy(ptr, &cfg, sizeof(cfg));
118
119 if (w <= 0 || h <= 0) {
120 mpp_err_f("invalid size [%d:%d]\n", w, h);
121 goto DONE;
122 }
123
124 if (roi->number > VEPU541_MAX_ROI_NUM) {
125 mpp_err_f("invalid region number %d\n", roi->number);
126 goto DONE;
127 }
128
129 /* check region config */
130 ret = MPP_OK;
131 for (i = 0; i < (RK_S32)roi->number; i++, region++) {
132 if (region->x + region->w > w || region->y + region->h > h)
133 ret = MPP_NOK;
134
135 if (region->intra > 1 || region->qp_area_idx >= VEPU541_MAX_ROI_NUM ||
136 region->area_map_en > 1 || region->abs_qp_en > 1)
137 ret = MPP_NOK;
138
139 if ((region->abs_qp_en && region->quality > 51) ||
140 (!region->abs_qp_en && (region->quality > 51 || region->quality < -51)))
141 ret = MPP_NOK;
142
143 if (ret) {
144 mpp_err_f("region %d invalid param:\n", i);
145 mpp_err_f("position [%d:%d:%d:%d] vs [%d:%d]\n",
146 region->x, region->y, region->w, region->h, w, h);
147 mpp_err_f("force intra %d qp area index %d\n",
148 region->intra, region->qp_area_idx);
149 mpp_err_f("abs qp mode %d value %d\n",
150 region->abs_qp_en, region->quality);
151 goto DONE;
152 }
153 }
154
155 region = roi->regions;
156 /* step 2. setup region for top to bottom */
157 for (i = 0; i < (RK_S32)roi->number; i++, region++) {
158 vepu541_set_one_roi(buf, region, w, h);
159 }
160
161 DONE:
162 return ret;
163 }
164
165 /*
166 * Invert color threshold is for the absolute difference between background
167 * and foregroud color.
168 * If background color and foregroud color are close enough then trigger the
169 * invert color process.
170 */
171 #define ENC_DEFAULT_OSD_INV_THR 15
172
173 #define VEPU541_OSD_ADDR_IDX_BASE 124
174
175 #define VEPU541_OSD_CFG_OFFSET 0x01C0
176 #define VEPU541_OSD_PLT_OFFSET 0x0400
177
178 typedef struct Vepu541OsdReg_t {
179 /*
180 * OSD_CFG
181 * Address offset: 0x01C0 Access type: read and write
182 * OSD configuration
183 */
184 struct {
185 /* OSD region enable, each bit controls corresponding OSD region. */
186 RK_U32 osd_e : 8;
187 /* OSD inverse color enable, each bit controls corresponding region. */
188 RK_U32 osd_inv_e : 8;
189 /*
190 * OSD palette clock selection.
191 * 1'h0: Configure bus clock domain.
192 * 1'h1: Core clock domain.
193 */
194 RK_U32 osd_plt_cks : 1;
195 /*
196 * OSD palette type.
197 * 1'h1: Default type.
198 * 1'h0: User defined type.
199 */
200 RK_U32 osd_plt_typ : 1;
201 RK_U32 reserved : 14;
202 } reg112;
203
204 /*
205 * OSD_INV
206 * Address offset: 0x01C4 Access type: read and write
207 * OSD color inverse configuration
208 */
209 struct {
210 /* Color inverse theshold for OSD region0. */
211 RK_U32 osd_ithd_r0 : 4;
212 /* Color inverse theshold for OSD region1. */
213 RK_U32 osd_ithd_r1 : 4;
214 /* Color inverse theshold for OSD region2. */
215 RK_U32 osd_ithd_r2 : 4;
216 /* Color inverse theshold for OSD region3. */
217 RK_U32 osd_ithd_r3 : 4;
218 /* Color inverse theshold for OSD region4. */
219 RK_U32 osd_ithd_r4 : 4;
220 /* Color inverse theshold for OSD region5. */
221 RK_U32 osd_ithd_r5 : 4;
222 /* Color inverse theshold for OSD region6. */
223 RK_U32 osd_ithd_r6 : 4;
224 /* Color inverse theshold for OSD region7. */
225 RK_U32 osd_ithd_r7 : 4;
226 } reg113;
227
228 RK_U32 reg114;
229 RK_U32 reg115;
230
231 /*
232 * OSD_POS reg116_123
233 * Address offset: 0x01D0~0x01EC Access type: read and write
234 * OSD region position
235 */
236 Vepu541OsdPos osd_pos[8];
237
238 /*
239 * ADR_OSD reg124_131
240 * Address offset: 0x01F0~0x20C Access type: read and write
241 * Base address for OSD region, 16B aligned
242 */
243 RK_U32 osd_addr[8];
244 } Vepu541OsdReg;
245
vepu541_set_osd(Vepu5xxOsdCfg * cfg)246 MPP_RET vepu541_set_osd(Vepu5xxOsdCfg *cfg)
247 {
248 Vepu541OsdReg *regs = (Vepu541OsdReg *)(cfg->reg_base + (size_t)VEPU541_OSD_CFG_OFFSET);
249 MppDev dev = cfg->dev;
250 MppEncOSDPltCfg *plt_cfg = cfg->plt_cfg;
251 MppEncOSDData2 osd;
252
253 if (copy2osd2(&osd, cfg->osd_data, cfg->osd_data2))
254 return MPP_NOK;
255
256 if (osd.num_region == 0)
257 return MPP_OK;
258
259 if (osd.num_region > 8) {
260 mpp_err_f("do NOT support more than 8 regions invalid num %d\n",
261 osd.num_region);
262 mpp_assert(osd.num_region <= 8);
263 return MPP_NOK;
264 }
265
266 if (plt_cfg->type == MPP_ENC_OSD_PLT_TYPE_USERDEF) {
267 MppDevRegWrCfg wr_cfg;
268
269 wr_cfg.reg = plt_cfg->plt;
270 wr_cfg.size = sizeof(MppEncOSDPlt);
271 wr_cfg.offset = VEPU541_REG_BASE_OSD_PLT;
272
273 mpp_dev_ioctl(dev, MPP_DEV_REG_WR, &wr_cfg);
274
275 regs->reg112.osd_plt_cks = 1;
276 regs->reg112.osd_plt_typ = VEPU5xx_OSD_PLT_TYPE_USERDEF;
277 } else {
278 regs->reg112.osd_plt_cks = 0;
279 regs->reg112.osd_plt_typ = VEPU5xx_OSD_PLT_TYPE_DEFAULT;
280 }
281
282 regs->reg112.osd_e = 0;
283 regs->reg112.osd_inv_e = 0;
284
285 RK_U32 i = 0;
286 MppEncOSDRegion2 *region = osd.region;
287 MppEncOSDRegion2 *tmp = region;
288 RK_U32 num = osd.num_region;
289
290 for (i = 0; i < num; i++, tmp++) {
291 regs->reg112.osd_e |= tmp->enable << i;
292 regs->reg112.osd_inv_e |= tmp->inverse << i;
293
294 if (tmp->enable && tmp->num_mb_x && tmp->num_mb_y) {
295 Vepu541OsdPos *pos = ®s->osd_pos[i];
296 size_t blk_len = tmp->num_mb_x * tmp->num_mb_y * 256;
297 RK_S32 fd = 0;
298 RK_U32 buf_size = 0;
299
300 pos->osd_lt_x = tmp->start_mb_x;
301 pos->osd_lt_y = tmp->start_mb_y;
302 pos->osd_rb_x = tmp->start_mb_x + tmp->num_mb_x - 1;
303 pos->osd_rb_y = tmp->start_mb_y + tmp->num_mb_y - 1;
304
305 buf_size = mpp_buffer_get_size(tmp->buf);
306 fd = mpp_buffer_get_fd(tmp->buf);
307 if (fd < 0) {
308 mpp_err_f("invalid osd buffer fd %d\n", fd);
309 return MPP_NOK;
310 }
311 regs->osd_addr[i] = fd;
312
313 if (tmp->buf_offset)
314 mpp_dev_set_reg_offset(dev, VEPU541_OSD_ADDR_IDX_BASE + i, tmp->buf_offset);
315
316 /* There should be enough buffer and offset should be 16B aligned */
317 if (buf_size < tmp->buf_offset + blk_len ||
318 (tmp->buf_offset & 0xf)) {
319 mpp_err_f("invalid osd cfg: %d x:y:w:h:off %d:%d:%d:%d:%x\n",
320 i, tmp->start_mb_x, tmp->start_mb_y,
321 tmp->num_mb_x, tmp->num_mb_y, tmp->buf_offset);
322 }
323 }
324 }
325
326 SET_OSD_INV_THR(0, regs->reg113, region);
327 SET_OSD_INV_THR(1, regs->reg113, region);
328 SET_OSD_INV_THR(2, regs->reg113, region);
329 SET_OSD_INV_THR(3, regs->reg113, region);
330 SET_OSD_INV_THR(4, regs->reg113, region);
331 SET_OSD_INV_THR(5, regs->reg113, region);
332 SET_OSD_INV_THR(6, regs->reg113, region);
333 SET_OSD_INV_THR(7, regs->reg113, region);
334
335 return MPP_OK;
336 }
337
338 #define VEPU540_OSD_CFG_OFFSET 0x0178
339
340 typedef struct Vepu540OsdReg_t {
341 /*
342 * OSD_INV_CFG
343 * Address offset: 0x0178 Access type: read and write
344 * OSD color inverse configuration
345 */
346 struct {
347 /*
348 * OSD color inverse enable of chroma component,
349 * each bit controls corresponding region.
350 */
351 RK_U32 osd_ch_inv_en : 8;
352 /*
353 * OSD color inverse expression type
354 * each bit controls corresponding region.
355 * 1'h0: AND;
356 * 1'h1: OR
357 */
358 RK_U32 osd_itype : 8;
359 /*
360 * OSD color inverse expression switch for luma component
361 * each bit controls corresponding region.
362 * 1'h0: Expression need to determine the condition;
363 * 1'h1: Expression don't need to determine the condition;
364 */
365 RK_U32 osd_lu_inv_msk : 8;
366 /*
367 * OSD color inverse expression switch for chroma component
368 * each bit controls corresponding region.
369 * 1'h0: Expression need to determine the condition;
370 * 1'h1: Expression don't need to determine the condition;
371 */
372 RK_U32 osd_ch_inv_msk : 8;
373 } reg094;
374
375 /* reg gap 095~111 */
376 RK_U32 reg_095_111[17];
377
378 /*
379 * OSD_CFG
380 * Address offset: 0x01C0 Access type: read and write
381 * OSD configuration
382 */
383 struct {
384 /* OSD region enable, each bit controls corresponding OSD region. */
385 RK_U32 osd_e : 8;
386 /* OSD inverse color enable, each bit controls corresponding region. */
387 RK_U32 osd_lu_inv_en : 8;
388 /*
389 * OSD palette clock selection.
390 * 1'h0: Configure bus clock domain.
391 * 1'h1: Core clock domain.
392 */
393 RK_U32 osd_plt_cks : 1;
394 /*
395 * OSD palette type.
396 * 1'h1: Default type.
397 * 1'h0: User defined type.
398 */
399 RK_U32 osd_plt_typ : 1;
400 RK_U32 reserved : 14;
401 } reg112;
402
403 /*
404 * OSD_INV
405 * Address offset: 0x01C4 Access type: read and write
406 * OSD color inverse configuration
407 */
408 struct {
409 /* Color inverse theshold for OSD region0. */
410 RK_U32 osd_ithd_r0 : 4;
411 /* Color inverse theshold for OSD region1. */
412 RK_U32 osd_ithd_r1 : 4;
413 /* Color inverse theshold for OSD region2. */
414 RK_U32 osd_ithd_r2 : 4;
415 /* Color inverse theshold for OSD region3. */
416 RK_U32 osd_ithd_r3 : 4;
417 /* Color inverse theshold for OSD region4. */
418 RK_U32 osd_ithd_r4 : 4;
419 /* Color inverse theshold for OSD region5. */
420 RK_U32 osd_ithd_r5 : 4;
421 /* Color inverse theshold for OSD region6. */
422 RK_U32 osd_ithd_r6 : 4;
423 /* Color inverse theshold for OSD region7. */
424 RK_U32 osd_ithd_r7 : 4;
425 } reg113;
426
427 RK_U32 reg114;
428 RK_U32 reg115;
429
430 /*
431 * OSD_POS reg116_123
432 * Address offset: 0x01D0~0x01EC Access type: read and write
433 * OSD region position
434 */
435 Vepu541OsdPos osd_pos[8];
436
437 /*
438 * ADR_OSD reg124_131
439 * Address offset: 0x01F0~0x20C Access type: read and write
440 * Base address for OSD region, 16B aligned
441 */
442 RK_U32 osd_addr[8];
443 } Vepu540OsdReg;
444
vepu540_set_osd(Vepu5xxOsdCfg * cfg)445 MPP_RET vepu540_set_osd(Vepu5xxOsdCfg *cfg)
446 {
447 Vepu540OsdReg *regs = (Vepu540OsdReg *)(cfg->reg_base + (size_t)VEPU540_OSD_CFG_OFFSET);
448 MppDev dev = cfg->dev;
449 MppEncOSDPltCfg *plt_cfg = cfg->plt_cfg;
450 MppEncOSDData2 osd;
451
452 if (copy2osd2(&osd, cfg->osd_data, cfg->osd_data2))
453 return MPP_NOK;
454
455 if (osd.num_region == 0)
456 return MPP_OK;
457
458 if (osd.num_region > 8) {
459 mpp_err_f("do NOT support more than 8 regions invalid num %d\n",
460 osd.num_region);
461 mpp_assert(osd.num_region <= 8);
462 return MPP_NOK;
463 }
464
465 if (plt_cfg->type == MPP_ENC_OSD_PLT_TYPE_USERDEF) {
466 MppDevRegWrCfg wr_cfg;
467
468 wr_cfg.reg = plt_cfg->plt;
469 wr_cfg.size = sizeof(MppEncOSDPlt);
470 wr_cfg.offset = VEPU541_REG_BASE_OSD_PLT;
471 mpp_dev_ioctl(dev, MPP_DEV_REG_WR, &wr_cfg);
472
473 regs->reg112.osd_plt_cks = 1;
474 regs->reg112.osd_plt_typ = VEPU5xx_OSD_PLT_TYPE_USERDEF;
475 } else {
476 regs->reg112.osd_plt_cks = 0;
477 regs->reg112.osd_plt_typ = VEPU5xx_OSD_PLT_TYPE_DEFAULT;
478 }
479
480 regs->reg112.osd_e = 0;
481 regs->reg112.osd_lu_inv_en = 0;
482 regs->reg094.osd_ch_inv_en = 0;
483 regs->reg094.osd_lu_inv_msk = 0;
484
485 RK_U32 num = osd.num_region;
486 RK_U32 k = 0;
487 MppEncOSDRegion2 *region = osd.region;
488 MppEncOSDRegion2 *tmp = region;
489
490 for (k = 0; k < num; k++, tmp++) {
491 regs->reg112.osd_e |= tmp->enable << k;
492 regs->reg112.osd_lu_inv_en |= (tmp->inverse) ? (1 << k) : 0;
493 regs->reg094.osd_ch_inv_en |= (tmp->inverse) ? (1 << k) : 0;
494
495 if (tmp->enable && tmp->num_mb_x && tmp->num_mb_y) {
496 Vepu541OsdPos *pos = ®s->osd_pos[k];
497 size_t blk_len = tmp->num_mb_x * tmp->num_mb_y * 256;
498 RK_S32 fd = -1;
499 size_t buf_size = 0;
500
501 pos->osd_lt_x = tmp->start_mb_x;
502 pos->osd_lt_y = tmp->start_mb_y;
503 pos->osd_rb_x = tmp->start_mb_x + tmp->num_mb_x - 1;
504 pos->osd_rb_y = tmp->start_mb_y + tmp->num_mb_y - 1;
505
506 buf_size = mpp_buffer_get_size(tmp->buf);
507 fd = mpp_buffer_get_fd(tmp->buf);
508 if (fd < 0) {
509 mpp_err_f("invalid osd buffer fd %d\n", fd);
510 return MPP_NOK;
511 }
512 regs->osd_addr[k] = fd;
513
514 if (tmp->buf_offset)
515 mpp_dev_set_reg_offset(dev, VEPU541_OSD_ADDR_IDX_BASE + k, tmp->buf_offset);
516
517 /* There should be enough buffer and offset should be 16B aligned */
518 if (buf_size < tmp->buf_offset + blk_len ||
519 (tmp->buf_offset & 0xf)) {
520 mpp_err_f("invalid osd cfg: %d x:y:w:h:off %d:%d:%d:%d:%x size %x\n",
521 k, tmp->start_mb_x, tmp->start_mb_y,
522 tmp->num_mb_x, tmp->num_mb_y, tmp->buf_offset, buf_size);
523 }
524 }
525 }
526
527 SET_OSD_INV_THR(0, regs->reg113, region);
528 SET_OSD_INV_THR(1, regs->reg113, region);
529 SET_OSD_INV_THR(2, regs->reg113, region);
530 SET_OSD_INV_THR(3, regs->reg113, region);
531 SET_OSD_INV_THR(4, regs->reg113, region);
532 SET_OSD_INV_THR(5, regs->reg113, region);
533 SET_OSD_INV_THR(6, regs->reg113, region);
534 SET_OSD_INV_THR(7, regs->reg113, region);
535
536 return MPP_OK;
537 }
538