/* * Copyright (c) 2016 Samsung Electronics Co., Ltd. * http://www.samsung.com * * BTS file for Samsung EXYNOS DPU driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include "decon.h" #include "dpp.h" #include #include #if defined(CONFIG_CAL_IF) #include #endif #if defined(CONFIG_SOC_EXYNOS9610) #include #endif #define DISP_FACTOR 100UL #define LCD_REFRESH_RATE 63UL #define MULTI_FACTOR (1UL << 10) u64 dpu_bts_calc_aclk_disp(struct decon_device *decon, struct decon_win_config *config, u64 resol_clock) { u64 s_ratio_h, s_ratio_v; u64 aclk_disp; u64 ppc; struct decon_frame *src = &config->src; struct decon_frame *dst = &config->dst; s_ratio_h = (src->w <= dst->w) ? MULTI_FACTOR : MULTI_FACTOR * (u64)src->w / (u64)dst->w; s_ratio_v = (src->h <= dst->h) ? MULTI_FACTOR : MULTI_FACTOR * (u64)src->h / (u64)dst->h; /* case for using dsc encoder 1ea at decon0 or decon1 */ if ((decon->id != 2) && (decon->lcd_info->dsc_cnt == 1)) ppc = ((decon->bts.ppc / 2UL) >= 1UL) ? (decon->bts.ppc / 2UL) : 1UL; else ppc = decon->bts.ppc; aclk_disp = resol_clock * s_ratio_h * s_ratio_v * DISP_FACTOR / 100UL / ppc * (MULTI_FACTOR * (u64)dst->w / (u64)decon->lcd_info->xres) / (MULTI_FACTOR * MULTI_FACTOR * MULTI_FACTOR); if (aclk_disp < (resol_clock / ppc)) aclk_disp = resol_clock / ppc; return aclk_disp; } static void dpu_bts_sum_all_decon_bw(struct decon_device *decon, u32 ch_bw[]) { int i, j; if (decon->id < 0 || decon->id >= decon->dt.decon_cnt) { decon_warn("[%s] undefined decon id(%d)!\n", __func__, decon->id); return; } for (i = 0; i < BTS_DPU_MAX; ++i) decon->bts.ch_bw[decon->id][i] = ch_bw[i]; for (i = 0; i < decon->dt.decon_cnt; ++i) { if (decon->id == i) continue; for (j = 0; j < BTS_DPU_MAX; ++j) ch_bw[j] += decon->bts.ch_bw[i][j]; } } /* bus utilization 75% */ #define BUS_UTIL 75 static void dpu_bts_find_max_disp_freq(struct decon_device *decon, struct decon_reg_data *regs) { int i, j, idx; u32 disp_ch_bw[BTS_DPU_MAX]; u32 max_disp_ch_bw; u32 disp_op_freq = 0, freq = 0; u64 resol_clock; u64 op_fps = LCD_REFRESH_RATE; struct decon_win_config *config = regs->dpp_config; memset(disp_ch_bw, 0, sizeof(disp_ch_bw)); for (i = 0; i < BTS_DPP_MAX; ++i) for (j = 0; j < BTS_DPU_MAX; ++j) if (decon->bts.bw[i].ch_num == j) disp_ch_bw[j] += decon->bts.bw[i].val; /* must be considered other decon's bw */ dpu_bts_sum_all_decon_bw(decon, disp_ch_bw); for (i = 0; i < BTS_DPU_MAX; ++i) if (disp_ch_bw[i]) DPU_DEBUG_BTS("\tCH%d = %d\n", i, disp_ch_bw[i]); max_disp_ch_bw = disp_ch_bw[0]; for (i = 1; i < BTS_DPU_MAX; ++i) if (max_disp_ch_bw < disp_ch_bw[i]) max_disp_ch_bw = disp_ch_bw[i]; decon->bts.peak = max_disp_ch_bw; decon->bts.max_disp_freq = max_disp_ch_bw * 100 / (16 * BUS_UTIL) + 1; if (decon->dt.out_type == DECON_OUT_DP) op_fps = decon->lcd_info->fps; /* 1.1: 10% margin, 1000: for KHZ, 1: for raising to a unit */ resol_clock = decon->lcd_info->xres * decon->lcd_info->yres * op_fps * 11 / 10 / 1000 + 1; decon->bts.resol_clk = resol_clock; DPU_DEBUG_BTS("\tDECON%d : resol clock = %d Khz\n", decon->id, decon->bts.resol_clk); for (i = 0; i < decon->dt.max_win; ++i) { idx = config[i].idma_type; if ((config[i].state != DECON_WIN_STATE_BUFFER) && (config[i].state != DECON_WIN_STATE_COLOR)) continue; freq = dpu_bts_calc_aclk_disp(decon, &config[i], resol_clock); if (disp_op_freq < freq) disp_op_freq = freq; } DPU_DEBUG_BTS("\tDISP bus freq(%d), operating freq(%d)\n", decon->bts.max_disp_freq, disp_op_freq); if (decon->bts.max_disp_freq < disp_op_freq) decon->bts.max_disp_freq = disp_op_freq; DPU_DEBUG_BTS("\tMAX DISP CH FREQ = %d\n", decon->bts.max_disp_freq); } static void dpu_bts_share_bw_info(int id) { int i, j; struct decon_device *decon[3]; int decon_cnt; decon_cnt = get_decon_drvdata(0)->dt.decon_cnt; for (i = 0; i < MAX_DECON_CNT; i++) decon[i] = NULL; for (i = 0; i < decon_cnt; i++) decon[i] = get_decon_drvdata(i); for (i = 0; i < decon_cnt; ++i) { if (id == i || decon[i] == NULL) continue; for (j = 0; j < BTS_DPU_MAX; ++j) decon[i]->bts.ch_bw[id][j] = decon[id]->bts.ch_bw[id][j]; } } void dpu_bts_calc_bw(struct decon_device *decon, struct decon_reg_data *regs) { struct decon_win_config *config = regs->dpp_config; struct bts_decon_info bts_info; enum dpp_rotate rot; int idx, i; if (!decon->bts.enabled) return; DPU_DEBUG_BTS("\n"); DPU_DEBUG_BTS("%s + : DECON%d\n", __func__, decon->id); memset(&bts_info, 0, sizeof(struct bts_decon_info)); for (i = 0; i < decon->dt.max_win; ++i) { if (config[i].state == DECON_WIN_STATE_BUFFER) { idx = config[i].idma_type; bts_info.dpp[idx].used = true; } else { continue; } bts_info.dpp[idx].bpp = dpu_get_bpp(config[i].format); bts_info.dpp[idx].src_w = config[i].src.w; bts_info.dpp[idx].src_h = config[i].src.h; bts_info.dpp[idx].dst.x1 = config[i].dst.x; bts_info.dpp[idx].dst.x2 = config[i].dst.x + config[i].dst.w; bts_info.dpp[idx].dst.y1 = config[i].dst.y; bts_info.dpp[idx].dst.y2 = config[i].dst.y + config[i].dst.h; rot = config[i].dpp_parm.rot; bts_info.dpp[idx].rotation = (rot > DPP_ROT_180) ? true : false; DPU_DEBUG_BTS("\tDPP%d : bpp(%d) src w(%d) h(%d) rot(%d)\n", idx, bts_info.dpp[idx].bpp, bts_info.dpp[idx].src_w, bts_info.dpp[idx].src_h, bts_info.dpp[idx].rotation); DPU_DEBUG_BTS("\t\t\t\tdst x(%d) right(%d) y(%d) bottom(%d)\n", bts_info.dpp[idx].dst.x1, bts_info.dpp[idx].dst.x2, bts_info.dpp[idx].dst.y1, bts_info.dpp[idx].dst.y2); } bts_info.vclk = decon->bts.resol_clk; bts_info.lcd_w = decon->lcd_info->xres; bts_info.lcd_h = decon->lcd_info->yres; decon->bts.total_bw = bts_calc_bw(decon->bts.type, &bts_info); memcpy(&decon->bts.bts_info, &bts_info, sizeof(struct bts_decon_info)); for (i = 0; i < BTS_DPP_MAX; ++i) { decon->bts.bw[i].val = bts_info.dpp[i].bw; if (decon->bts.bw[i].val) DPU_DEBUG_BTS("\tDPP%d bandwidth = %d\n", i, decon->bts.bw[i].val); } DPU_DEBUG_BTS("\tDECON%d total bandwidth = %d\n", decon->id, decon->bts.total_bw); dpu_bts_find_max_disp_freq(decon, regs); /* update bw for other decons */ dpu_bts_share_bw_info(decon->id); DPU_DEBUG_BTS("%s -\n", __func__); } void dpu_bts_update_bw(struct decon_device *decon, struct decon_reg_data *regs, u32 is_after) { struct bts_bw bw = { 0, }; #if defined(CONFIG_EXYNOS_DISPLAYPORT) struct displayport_device *displayport = get_displayport_drvdata(); videoformat cur = displayport->cur_video; __u64 pixelclock = supported_videos[cur].dv_timings.bt.pixelclock; #endif DPU_DEBUG_BTS("%s +\n", __func__); if (!decon->bts.enabled) return; /* update peak & read bandwidth per DPU port */ bw.peak = decon->bts.peak; bw.read = decon->bts.total_bw; DPU_DEBUG_BTS("\tpeak = %d, read = %d\n", bw.peak, bw.read); if (bw.read == 0) bw.peak = 0; if (is_after) { /* after DECON h/w configuration */ if (decon->bts.total_bw <= decon->bts.prev_total_bw) bts_update_bw(decon->bts.type, bw); #if defined(CONFIG_EXYNOS_DISPLAYPORT) if ((displayport->state == DISPLAYPORT_STATE_ON) && (pixelclock >= 533000000)) /* 4K DP case */ return; #endif if (decon->bts.max_disp_freq <= decon->bts.prev_max_disp_freq) pm_qos_update_request(&decon->bts.disp_qos, decon->bts.max_disp_freq); decon->bts.prev_total_bw = decon->bts.total_bw; decon->bts.prev_max_disp_freq = decon->bts.max_disp_freq; } else { if (decon->bts.total_bw > decon->bts.prev_total_bw) bts_update_bw(decon->bts.type, bw); #if defined(CONFIG_EXYNOS_DISPLAYPORT) if ((displayport->state == DISPLAYPORT_STATE_ON) && (pixelclock >= 533000000)) /* 4K DP case */ return; #endif if (decon->bts.max_disp_freq > decon->bts.prev_max_disp_freq) pm_qos_update_request(&decon->bts.disp_qos, decon->bts.max_disp_freq); } DPU_DEBUG_BTS("%s -\n", __func__); } void dpu_bts_acquire_bw(struct decon_device *decon) { #if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) struct displayport_device *displayport = get_displayport_drvdata(); videoformat cur = displayport->cur_video; __u64 pixelclock = supported_videos[cur].dv_timings.bt.pixelclock; #endif struct decon_win_config config; u64 resol_clock; u32 aclk_freq = 0; DPU_DEBUG_BTS("%s +\n", __func__); if (!decon->bts.enabled) return; if (decon->dt.out_type == DECON_OUT_DSI) { memset(&config, 0, sizeof(struct decon_win_config)); config.src.w = config.dst.w = decon->lcd_info->xres; config.src.h = config.dst.h = decon->lcd_info->yres; resol_clock = decon->lcd_info->xres * decon->lcd_info->yres * LCD_REFRESH_RATE * 11 / 10 / 1000 + 1; aclk_freq = dpu_bts_calc_aclk_disp(decon, &config, resol_clock); DPU_DEBUG_BTS("Initial calculated disp freq(%lu)\n", aclk_freq); /* * If current disp freq is higher than calculated freq, * it must not be set. if not, underrun can occur. */ if (cal_dfs_get_rate(ACPM_DVFS_DISP) < aclk_freq) pm_qos_update_request(&decon->bts.disp_qos, aclk_freq); DPU_DEBUG_BTS("Get initial disp freq(%lu)\n", cal_dfs_get_rate(ACPM_DVFS_DISP)); return; } #if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) if (decon->dt.out_type != DECON_OUT_DP) return; if (pixelclock >= 533000000) { if (pm_qos_request_active(&decon->bts.mif_qos)) pm_qos_update_request(&decon->bts.mif_qos, 1794 * 1000); else DPU_ERR_BTS("%s mif qos setting error\n", __func__); if (pm_qos_request_active(&decon->bts.int_qos)) pm_qos_update_request(&decon->bts.int_qos, 534 * 1000); else DPU_ERR_BTS("%s int qos setting error\n", __func__); if (pm_qos_request_active(&decon->bts.disp_qos)) pm_qos_update_request(&decon->bts.disp_qos, 400 * 1000); else DPU_ERR_BTS("%s int qos setting error\n", __func__); if (!decon->bts.scen_updated) { decon->bts.scen_updated = 1; bts_update_scen(BS_DP_DEFAULT, 1); } } else if (pixelclock > 148500000) { /* pixelclock < 533000000 ? */ if (pm_qos_request_active(&decon->bts.mif_qos)) pm_qos_update_request(&decon->bts.mif_qos, 1352 * 1000); else DPU_ERR_BTS("%s mif qos setting error\n", __func__); } /* pixelclock <= 148500000 ? */ DPU_DEBUG_BTS("%s: decon%d, pixelclock(%u)\n", __func__, decon->id, pixelclock); #endif } void dpu_bts_release_bw(struct decon_device *decon) { struct bts_bw bw = { 0, }; DPU_DEBUG_BTS("%s +\n", __func__); if (!decon->bts.enabled) return; if (decon->dt.out_type == DECON_OUT_DSI) { bts_update_bw(decon->bts.type, bw); decon->bts.prev_total_bw = 0; pm_qos_update_request(&decon->bts.disp_qos, 0); decon->bts.prev_max_disp_freq = 0; } else if (decon->dt.out_type == DECON_OUT_DP) { #if defined(CONFIG_DECON_BTS_LEGACY) && defined(CONFIG_EXYNOS_DISPLAYPORT) if (pm_qos_request_active(&decon->bts.mif_qos)) pm_qos_update_request(&decon->bts.mif_qos, 0); else DPU_ERR_BTS("%s mif qos setting error\n", __func__); if (pm_qos_request_active(&decon->bts.int_qos)) pm_qos_update_request(&decon->bts.int_qos, 0); else DPU_ERR_BTS("%s int qos setting error\n", __func__); if (pm_qos_request_active(&decon->bts.disp_qos)) pm_qos_update_request(&decon->bts.disp_qos, 0); else DPU_ERR_BTS("%s int qos setting error\n", __func__); if (decon->bts.scen_updated) { decon->bts.scen_updated = 0; bts_update_scen(BS_DP_DEFAULT, 0); } #endif } DPU_DEBUG_BTS("%s -\n", __func__); } void dpu_bts_init(struct decon_device *decon) { int comp_ratio; int i; struct v4l2_subdev *sd = NULL; DPU_DEBUG_BTS("%s +\n", __func__); decon->bts.enabled = false; if (!IS_ENABLED(CONFIG_EXYNOS_BTS)) { DPU_ERR_BTS("decon%d bts feature is disabled\n", decon->id); return; } if (decon->id == 1) decon->bts.type = BTS_BW_DECON1; else if (decon->id == 2) decon->bts.type = BTS_BW_DECON2; else decon->bts.type = BTS_BW_DECON0; for (i = 0; i < BTS_DPU_MAX; i++) decon->bts.ch_bw[decon->id][i] = 0; DPU_DEBUG_BTS("BTS_BW_TYPE(%d) -\n", decon->bts.type); if (decon->lcd_info->dsc_enabled) comp_ratio = 3; else comp_ratio = 1; if (decon->dt.out_type == DECON_OUT_DP) { /* * Decon2-DP : various resolutions are available * therefore, set max resolution clock at init phase to avoid underrun */ decon->bts.resol_clk = (u32)((u64)4096 * 2160 * 60 * 11 / 10 / 1000 + 1); } else { /* * Resol clock(KHZ) = lcd width x lcd height x 63(refresh rate) x * 1.1(10% margin) x comp_ratio(1/3 DSC) / 2(2PPC) / * 1000(for KHZ) + 1(for raising to a unit) */ decon->bts.resol_clk = (u32)((u64)decon->lcd_info->xres * (u64)decon->lcd_info->yres * LCD_REFRESH_RATE * 11 / 10 / 1000 + 1); } DPU_DEBUG_BTS("[Init: D%d] resol clock = %d Khz\n", decon->id, decon->bts.resol_clk); pm_qos_add_request(&decon->bts.mif_qos, PM_QOS_BUS_THROUGHPUT, 0); pm_qos_add_request(&decon->bts.int_qos, PM_QOS_DEVICE_THROUGHPUT, 0); pm_qos_add_request(&decon->bts.disp_qos, PM_QOS_DISPLAY_THROUGHPUT, 0); decon->bts.scen_updated = 0; for (i = 0; i < BTS_DPP_MAX; ++i) { sd = decon->dpp_sd[DPU_DMA2CH(i)]; v4l2_subdev_call(sd, core, ioctl, DPP_GET_PORT_NUM, &decon->bts.bw[i].ch_num); DPU_INFO_BTS("IDMA_TYPE(%d) CH(%d) Port(%d)\n", i, DPU_DMA2CH(i), decon->bts.bw[i].ch_num); } decon->bts.enabled = true; DPU_INFO_BTS("decon%d bts feature is enabled\n", decon->id); } void dpu_bts_deinit(struct decon_device *decon) { if (!decon->bts.enabled) return; DPU_DEBUG_BTS("%s +\n", __func__); pm_qos_remove_request(&decon->bts.disp_qos); pm_qos_remove_request(&decon->bts.int_qos); pm_qos_remove_request(&decon->bts.mif_qos); DPU_DEBUG_BTS("%s -\n", __func__); } struct decon_bts_ops decon_bts_control = { .bts_init = dpu_bts_init, .bts_calc_bw = dpu_bts_calc_bw, .bts_update_bw = dpu_bts_update_bw, .bts_acquire_bw = dpu_bts_acquire_bw, .bts_release_bw = dpu_bts_release_bw, .bts_deinit = dpu_bts_deinit, };