/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2019 MediaTek Inc. */ #define LOG_TAG "RSZ" #include "ddp_info.h" #include "ddp_reg.h" #include "ddp_reg_rsz.h" #include "ddp_log.h" #include "disp_helper.h" #include "ddp_clkmgr.h" #include "ddp_hal.h" #include #include "ddp_rsz.h" #include "primary_display.h" #include "disp_rect.h" #define UNIT 32768 #define TILE_LOSS 4 #define TILE_LOSS_LEFT 4 #define TILE_LOSS_RIGHT 4 int rsz_calc_tile_params(u32 frm_in_len, u32 frm_out_len, bool tile_mode, struct rsz_tile_params *t) { u32 tile_loss = 0; u32 step = 0; u32 init_phase = 0; u32 offset[2] = { 0 }; u32 int_offset[2] = { 0 }; u32 sub_offset[2] = { 0 }; u32 tile_in_len[2] = { 0 }; u32 tile_out_len = 0; if (tile_mode) tile_loss = TILE_LOSS; step = (UNIT * (frm_in_len - 1) + (frm_out_len - 2)) / (frm_out_len - 1); /* left half */ offset[0] = (step * (frm_out_len - 1) - UNIT * (frm_in_len - 1)) / 2; init_phase = UNIT - offset[0]; int_offset[0] = init_phase / UNIT; sub_offset[0] = init_phase - UNIT * int_offset[0]; if (tile_mode) { tile_in_len[0] = frm_in_len / 2 + tile_loss; tile_out_len = frm_out_len / 2; } else { tile_in_len[0] = frm_in_len; tile_out_len = frm_out_len; } t[0].step = step; t[0].int_offset = int_offset[0]; t[0].sub_offset = sub_offset[0]; t[0].in_len = tile_in_len[0]; t[0].out_len = tile_out_len; DDPDBG("%s:%s:step:%u,offset:%u.%u,len:%u->%u\n", __func__, tile_mode ? "dual" : "single", t[0].step, t[0].int_offset, t[0].sub_offset, t[0].in_len, t[0].out_len); if (!tile_mode) return 0; /* right half */ offset[1] = (init_phase + frm_out_len / 2 * step) - (frm_in_len / 2 - tile_loss - (offset[0] ? 1 : 0) + 1) * UNIT + UNIT; int_offset[1] = offset[1] / UNIT; sub_offset[1] = offset[1] - UNIT * int_offset[1]; tile_in_len[1] = frm_in_len / 2 + tile_loss + (offset[0] ? 1 : 0); if (int_offset[1] & 0x1) { int_offset[1]++; tile_in_len[1]++; DDPMSG("right tile int_offset: make odd to even\n"); } t[1].step = step; t[1].int_offset = int_offset[1]; t[1].sub_offset = sub_offset[1]; t[1].in_len = tile_in_len[1]; t[1].out_len = tile_out_len; DDPDBG("%s:%s:step:%u,offset:%u.%u,len:%u->%u\n", __func__, tile_mode ? "dual" : "single", t[1].step, t[1].int_offset, t[1].sub_offset, t[1].in_len, t[1].out_len); return 0; } static unsigned long rsz_base_addr(enum DISP_MODULE_ENUM module) { switch (module) { case DISP_MODULE_RSZ0: return DISPSYS_RSZ0_BASE; default: DDPERR("invalid rsz module:%s\n", ddp_get_module_name(module)); break; } return 0; } static int rsz_clock_on(enum DISP_MODULE_ENUM module, void *qhandle) { ddp_clk_prepare_enable(ddp_get_module_clk_id(module)); return 0; } static int rsz_clock_off(enum DISP_MODULE_ENUM module, void *qhandle) { ddp_clk_disable_unprepare(ddp_get_module_clk_id(module)); return 0; } static int rsz_init(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_on(module, qhandle); return 0; } static int rsz_deinit(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_off(module, qhandle); return 0; } static int rsz_power_on(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_on(module, qhandle); return 0; } static int rsz_power_off(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_off(module, qhandle); return 0; } static int rsz_get_in_out_roi(struct disp_ddp_path_config *pconfig, u32 *in_w, u32 *in_h, u32 *out_w, u32 *out_h) { struct disp_rect rsz_src_roi = pconfig->rsz_src_roi; struct disp_rect rsz_dst_roi = pconfig->rsz_dst_roi; if (pconfig->rsz_enable) { *in_w = rsz_src_roi.width; *in_h = rsz_src_roi.height; *out_w = rsz_dst_roi.width; *out_h = rsz_dst_roi.height; } DDPDBG("[RPO] module=%s,in(w,h)=(%d,%d),out(w,h)=(%d,%d)\n", ddp_get_module_name(DISP_MODULE_RSZ0), *in_w, *in_h, *out_w, *out_h); return 0; } static int rsz_check_params(struct RSZ_CONFIG_STRUCT *rsz_config) { if ((rsz_config->frm_in_w != rsz_config->frm_out_w || rsz_config->frm_in_h != rsz_config->frm_out_h) && rsz_config->frm_in_w > RSZ_TILE_LENGTH) { DISPERR("%s:need rsz but input width(%u) > limit(%u)\n", __func__, rsz_config->frm_in_w, RSZ_TILE_LENGTH); return -EINVAL; } return 0; } static int disp_rsz_set_color_format(enum RSZ_COLOR_FORMAT fmt) { u32 reg_val = 0; switch (fmt) { case ARGB8101010: reg_val = REG_FLD_VAL(FLD_RSZ_POWER_SAVING, 0x0); reg_val |= REG_FLD_VAL(FLD_RSZ_RGB_BIT_MODE, 0x0); break; case RGB999: reg_val = REG_FLD_VAL(FLD_RSZ_POWER_SAVING, 0x1); reg_val |= REG_FLD_VAL(FLD_RSZ_RGB_BIT_MODE, 0x0); break; case RGB888: reg_val = REG_FLD_VAL(FLD_RSZ_POWER_SAVING, 0x1); reg_val |= REG_FLD_VAL(FLD_RSZ_RGB_BIT_MODE, 0x1); break; default: DDPMSG("unknown resize color format\n"); break; } return reg_val; } static int rsz_config(enum DISP_MODULE_ENUM module, struct disp_ddp_path_config *pconfig, void *qhandle) { unsigned long rsz_base = rsz_base_addr(module); struct RSZ_CONFIG_STRUCT *rsz_config = &pconfig->rsz_config; u32 in_w = 0, in_h = 0, out_w = 0, out_h = 0; u32 reg_val = 0; u32 tile_idx = 0; u32 frm_in_w = pconfig->dst_w, frm_in_h = pconfig->dst_h; u32 frm_out_w = pconfig->dst_w, frm_out_h = pconfig->dst_h; /* platform dependent: color_fmt, tile_mode */ enum RSZ_COLOR_FORMAT fmt = RGB888; bool tile_mode = false; if (!pconfig->ovl_dirty && !pconfig->dst_dirty) return 0; if (pconfig->dst_dirty) { rsz_config->frm_out_w = pconfig->dst_w; rsz_config->frm_out_h = pconfig->dst_h; } rsz_get_in_out_roi(pconfig, &frm_in_w, &frm_in_h, &frm_out_w, &frm_out_h); rsz_config->frm_in_w = frm_in_w; rsz_config->frm_in_h = frm_in_h; rsz_config->frm_out_w = frm_out_w; rsz_config->frm_out_h = frm_out_h; if (rsz_check_params(rsz_config)) { static bool dump; struct OVL_CONFIG_STRUCT *c = &pconfig->ovl_config[0]; DISPERR("%s:L%d:en%d:(%u,%u,%ux%u)->(%u,%u,%ux%u)\n", __func__, c->layer, c->layer_en, c->src_x, c->src_y, c->src_w, c->src_h, c->dst_x, c->dst_y, c->dst_w, c->dst_h); if (!dump) { dump = true; primary_display_diagnose(__func__, __LINE__); disp_aee_print("need rsz but input w(%u) > (%u)\n", rsz_config->frm_in_w, RSZ_TILE_LENGTH); } return -EINVAL; } rsz_calc_tile_params(rsz_config->frm_in_w, rsz_config->frm_out_w, tile_mode, rsz_config->tw); rsz_calc_tile_params(rsz_config->frm_in_h, rsz_config->frm_out_h, tile_mode, &rsz_config->th); in_w = rsz_config->tw[tile_idx].in_len; in_h = rsz_config->th.in_len; out_w = rsz_config->tw[tile_idx].out_len; out_h = rsz_config->th.out_len; if (in_w > out_w || in_h > out_h) { DDPERR("DISP_RSZ only supports scaling-up,(%dx%d)->(%dx%d)\n", in_w, in_h, out_w, out_h); return -EINVAL; } reg_val = 0; reg_val |= REG_FLD_VAL(FLD_RSZ_HORIZONTAL_EN, (in_w != out_w)); reg_val |= REG_FLD_VAL(FLD_RSZ_VERTICAL_EN, (in_h != out_h)); reg_val |= REG_FLD_VAL(FLD_RSZ_HORIZONTAL_TABLE_SELECT, (fmt == ARGB8101010 ? 0x1 : 0x0)); reg_val |= REG_FLD_VAL(FLD_RSZ_VERTICAL_TABLE_SELECT, (fmt == ARGB8101010 ? 0x1 : 0x0)); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_CONTROL_1, reg_val); DDPDBG("%s:CONTROL_1:0x%x\n", __func__, reg_val); reg_val = disp_rsz_set_color_format(fmt); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_CONTROL_2, reg_val); DDPDBG("%s:CONTROL_2:0x%x\n", __func__, reg_val); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_INPUT_IMAGE, in_h << 16 | in_w); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_OUTPUT_IMAGE, out_h << 16 | out_w); DDPDBG("%s:%s:(%ux%u)->(%ux%u)\n", __func__, ddp_get_module_name(module), in_w, in_h, out_w, out_h); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_HORIZONTAL_COEFF_STEP, rsz_config->tw[tile_idx].step); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_VERTICAL_COEFF_STEP, rsz_config->th.step); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_LUMA_HORIZONTAL_INTEGER_OFFSET, rsz_config->tw[tile_idx].int_offset); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET, rsz_config->tw[tile_idx].sub_offset); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_LUMA_VERTICAL_INTEGER_OFFSET, rsz_config->th.int_offset); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_LUMA_VERTICAL_SUBPIXEL_OFFSET, rsz_config->th.sub_offset); return 0; } static int rsz_start(enum DISP_MODULE_ENUM module, void *qhandle) { unsigned long rsz_base = rsz_base_addr(module); u32 reg_val = 0; if (disp_helper_get_option(DISP_OPT_SHADOW_REGISTER)) { reg_val = 0; if (disp_helper_get_option(DISP_OPT_SHADOW_MODE) == 0) { /* full shadow mode, read shadow reg */ } else if (disp_helper_get_option(DISP_OPT_SHADOW_MODE) == 1) { /* force commit, read shadow reg */ reg_val |= REG_FLD_VAL(FLD_RSZ_FORCE_COMMIT, 0x1); } else if (disp_helper_get_option(DISP_OPT_SHADOW_MODE) == 2) { /* bypass shadow, read shadow reg */ reg_val |= REG_FLD_VAL(FLD_RSZ_BYPASS_SHADOW, 0x1); } reg_val |= REG_FLD_VAL(FLD_RSZ_READ_WRK_REG, 0x0); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_SHADOW_CTRL, reg_val); } DISP_REG_SET_FIELD(qhandle, FLD_RSZ_EN, rsz_base + DISP_REG_RSZ_ENABLE, 0x1); DISP_REG_SET(qhandle, rsz_base + DISP_REG_RSZ_DEBUG_SEL, 0x3); return 0; } static int rsz_stop(enum DISP_MODULE_ENUM module, void *qhandle) { unsigned long rsz_base = rsz_base_addr(module); DISP_REG_SET_FIELD(qhandle, FLD_RSZ_EN, rsz_base + DISP_REG_RSZ_ENABLE, 0x0); return 0; } static int rsz_reset(enum DISP_MODULE_ENUM module, void *qhandle) { unsigned long rsz_base = rsz_base_addr(module); /* reset=1 -> wait 0.1 msec -> reset=0 */ if (qhandle == NULL) { DISP_REG_SET_FIELD(NULL, FLD_RSZ_RST, rsz_base + DISP_REG_RSZ_ENABLE, 0x1); usleep_range(100, 200); DISP_REG_SET_FIELD(NULL, FLD_RSZ_RST, rsz_base + DISP_REG_RSZ_ENABLE, 0x0); } else { DDPDBG("DISP_RSZ does not support cmdq reset\n"); } return 0; } static int rsz_suspend(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_off(module, qhandle); return 0; } static int rsz_resume(enum DISP_MODULE_ENUM module, void *qhandle) { rsz_clock_on(module, qhandle); return 0; } void rsz_dump_analysis(enum DISP_MODULE_ENUM module) { unsigned long rsz_base = rsz_base_addr(module); u32 enable = 0; u32 con1 = 0; u32 con2 = 0; u32 int_flag = 0; u32 in_size = 0; u32 out_size = 0; u32 in_pos = 0; u32 shadow = 0; enable = DISP_REG_GET(rsz_base + DISP_REG_RSZ_ENABLE); con1 = DISP_REG_GET(rsz_base + DISP_REG_RSZ_CONTROL_1); con2 = DISP_REG_GET(rsz_base + DISP_REG_RSZ_CONTROL_2); int_flag = DISP_REG_GET(rsz_base + DISP_REG_RSZ_INT_FLAG); in_size = DISP_REG_GET(rsz_base + DISP_REG_RSZ_INPUT_IMAGE); out_size = DISP_REG_GET(rsz_base + DISP_REG_RSZ_OUTPUT_IMAGE); in_pos = DISP_REG_GET(rsz_base + DISP_REG_RSZ_DEBUG); shadow = DISP_REG_GET(rsz_base + DISP_REG_RSZ_SHADOW_CTRL); DDPDUMP("== DISP %s ANALYSIS ==\n", ddp_get_module_name(module)); DISP_REG_SET(NULL, rsz_base + DISP_REG_RSZ_DEBUG_SEL, 0x3); DDPDUMP( "en:%d,rst:%d,h_en:%d,v_en:%d,h_table:%d,v_table:%d,dcm_dis:%d,int_en:%d,wclr_en:%d\n", REG_FLD_VAL_GET(FLD_RSZ_EN, enable), REG_FLD_VAL_GET(FLD_RSZ_RST, enable), REG_FLD_VAL_GET(FLD_RSZ_HORIZONTAL_EN, con1), REG_FLD_VAL_GET(FLD_RSZ_VERTICAL_EN, con1), REG_FLD_VAL_GET(FLD_RSZ_HORIZONTAL_TABLE_SELECT, con1), REG_FLD_VAL_GET(FLD_RSZ_VERTICAL_TABLE_SELECT, con1), REG_FLD_VAL_GET(FLD_RSZ_DCM_DIS, con1), REG_FLD_VAL_GET(FLD_RSZ_INTEN, con1), REG_FLD_VAL_GET(FLD_RSZ_INT_WCLR_EN, con1)); DDPDUMP( "power_saving:%d,rgb_bit_mode:%d,frm_start:%d,frm_end:%d,size_err:%d,sof_rst:%d\n", REG_FLD_VAL_GET(FLD_RSZ_POWER_SAVING, con2), REG_FLD_VAL_GET(FLD_RSZ_RGB_BIT_MODE, con2), REG_FLD_VAL_GET(FLD_RSZ_FRAME_START, int_flag), REG_FLD_VAL_GET(FLD_RSZ_FRAME_END, int_flag), REG_FLD_VAL_GET(FLD_RSZ_SIZE_ERR, int_flag), REG_FLD_VAL_GET(FLD_RSZ_SOF_RESET, int_flag)); DDPDUMP("in(%ux%u),out(%ux%u),h_step:%d,v_step:%d\n", REG_FLD_VAL_GET(FLD_RSZ_INPUT_IMAGE_W, in_size), REG_FLD_VAL_GET(FLD_RSZ_INPUT_IMAGE_H, in_size), REG_FLD_VAL_GET(FLD_RSZ_OUTPUT_IMAGE_W, out_size), REG_FLD_VAL_GET(FLD_RSZ_OUTPUT_IMAGE_H, out_size), DISP_REG_GET(rsz_base + DISP_REG_RSZ_HORIZONTAL_COEFF_STEP), DISP_REG_GET(rsz_base + DISP_REG_RSZ_VERTICAL_COEFF_STEP)); DDPDUMP("luma_h:%d.%d,luma_v:%d.%d\n", DISP_REG_GET(rsz_base + DISP_REG_RSZ_LUMA_HORIZONTAL_INTEGER_OFFSET), DISP_REG_GET(rsz_base + DISP_REG_RSZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET), DISP_REG_GET(rsz_base + DISP_REG_RSZ_LUMA_VERTICAL_INTEGER_OFFSET), DISP_REG_GET(rsz_base + DISP_REG_RSZ_LUMA_VERTICAL_SUBPIXEL_OFFSET)); DDPDUMP( "dbg_sel:%d, in(%u,%u);shadow_ctrl:bypass:%d,force:%d,read_working:%d\n", DISP_REG_GET(rsz_base + DISP_REG_RSZ_DEBUG_SEL), in_pos & 0xFFFF, (in_pos >> 16) & 0xFFFF, REG_FLD_VAL_GET(FLD_RSZ_BYPASS_SHADOW, shadow), REG_FLD_VAL_GET(FLD_RSZ_FORCE_COMMIT, shadow), REG_FLD_VAL_GET(FLD_RSZ_READ_WRK_REG, shadow)); } void rsz_dump_reg(enum DISP_MODULE_ENUM module) { unsigned long rsz_base = rsz_base_addr(module); int i = 0; DDPDUMP("== DISP %s REGS ==\n", ddp_get_module_name(module)); for (i = 0; i < 3; i++) { DDPDUMP("0x%03X: 0x%08x 0x%08x 0x%08x 0x%08x\n", i * 0x10, DISP_REG_GET(rsz_base + i * 0x10), DISP_REG_GET(rsz_base + i * 0x10 + 0x4), DISP_REG_GET(rsz_base + i * 0x10 + 0x8), DISP_REG_GET(rsz_base + i * 0x10 + 0xC)); } DDPDUMP("0x044: 0x%08x 0x%08x; 0x0F0: 0x%08x\n", DISP_REG_GET(rsz_base + 0x44), DISP_REG_GET(rsz_base + 0x48), DISP_REG_GET(rsz_base + 0xF0)); } static int rsz_dump_info(enum DISP_MODULE_ENUM module, int level) { rsz_dump_analysis(module); rsz_dump_reg(module); return 0; } static int rsz_bypass(enum DISP_MODULE_ENUM module, int bypass) { unsigned long rsz_base = rsz_base_addr(module); if (bypass) { /* use relay mode */ DISP_REG_SET_FIELD(NULL, FLD_RSZ_EN, rsz_base + DISP_REG_RSZ_ENABLE, 0x1); DISP_REG_SET_FIELD(NULL, FLD_RSZ_HORIZONTAL_EN, rsz_base + DISP_REG_RSZ_CONTROL_1, 0x0); DISP_REG_SET_FIELD(NULL, FLD_RSZ_VERTICAL_EN, rsz_base + DISP_REG_RSZ_CONTROL_1, 0x0); } return 0; } struct DDP_MODULE_DRIVER ddp_driver_rsz = { .init = rsz_init, .deinit = rsz_deinit, .config = rsz_config, .start = rsz_start, .stop = rsz_stop, .reset = rsz_reset, .power_on = rsz_power_on, .power_off = rsz_power_off, .suspend = rsz_suspend, .resume = rsz_resume, .dump_info = rsz_dump_info, .bypass = rsz_bypass, };