18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2019 NXP. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "dcss-dev.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define DCSS_SCALER_CTRL 0x00 148c2ecf20Sopenharmony_ci#define SCALER_EN BIT(0) 158c2ecf20Sopenharmony_ci#define REPEAT_EN BIT(4) 168c2ecf20Sopenharmony_ci#define SCALE2MEM_EN BIT(8) 178c2ecf20Sopenharmony_ci#define MEM2OFIFO_EN BIT(12) 188c2ecf20Sopenharmony_ci#define DCSS_SCALER_OFIFO_CTRL 0x04 198c2ecf20Sopenharmony_ci#define OFIFO_LOW_THRES_POS 0 208c2ecf20Sopenharmony_ci#define OFIFO_LOW_THRES_MASK GENMASK(9, 0) 218c2ecf20Sopenharmony_ci#define OFIFO_HIGH_THRES_POS 16 228c2ecf20Sopenharmony_ci#define OFIFO_HIGH_THRES_MASK GENMASK(25, 16) 238c2ecf20Sopenharmony_ci#define UNDERRUN_DETECT_CLR BIT(26) 248c2ecf20Sopenharmony_ci#define LOW_THRES_DETECT_CLR BIT(27) 258c2ecf20Sopenharmony_ci#define HIGH_THRES_DETECT_CLR BIT(28) 268c2ecf20Sopenharmony_ci#define UNDERRUN_DETECT_EN BIT(29) 278c2ecf20Sopenharmony_ci#define LOW_THRES_DETECT_EN BIT(30) 288c2ecf20Sopenharmony_ci#define HIGH_THRES_DETECT_EN BIT(31) 298c2ecf20Sopenharmony_ci#define DCSS_SCALER_SDATA_CTRL 0x08 308c2ecf20Sopenharmony_ci#define YUV_EN BIT(0) 318c2ecf20Sopenharmony_ci#define RTRAM_8LINES BIT(1) 328c2ecf20Sopenharmony_ci#define Y_UV_BYTE_SWAP BIT(4) 338c2ecf20Sopenharmony_ci#define A2R10G10B10_FORMAT_POS 8 348c2ecf20Sopenharmony_ci#define A2R10G10B10_FORMAT_MASK GENMASK(11, 8) 358c2ecf20Sopenharmony_ci#define DCSS_SCALER_BIT_DEPTH 0x0C 368c2ecf20Sopenharmony_ci#define LUM_BIT_DEPTH_POS 0 378c2ecf20Sopenharmony_ci#define LUM_BIT_DEPTH_MASK GENMASK(1, 0) 388c2ecf20Sopenharmony_ci#define CHR_BIT_DEPTH_POS 4 398c2ecf20Sopenharmony_ci#define CHR_BIT_DEPTH_MASK GENMASK(5, 4) 408c2ecf20Sopenharmony_ci#define DCSS_SCALER_SRC_FORMAT 0x10 418c2ecf20Sopenharmony_ci#define DCSS_SCALER_DST_FORMAT 0x14 428c2ecf20Sopenharmony_ci#define FORMAT_MASK GENMASK(1, 0) 438c2ecf20Sopenharmony_ci#define DCSS_SCALER_SRC_LUM_RES 0x18 448c2ecf20Sopenharmony_ci#define DCSS_SCALER_SRC_CHR_RES 0x1C 458c2ecf20Sopenharmony_ci#define DCSS_SCALER_DST_LUM_RES 0x20 468c2ecf20Sopenharmony_ci#define DCSS_SCALER_DST_CHR_RES 0x24 478c2ecf20Sopenharmony_ci#define WIDTH_POS 0 488c2ecf20Sopenharmony_ci#define WIDTH_MASK GENMASK(11, 0) 498c2ecf20Sopenharmony_ci#define HEIGHT_POS 16 508c2ecf20Sopenharmony_ci#define HEIGHT_MASK GENMASK(27, 16) 518c2ecf20Sopenharmony_ci#define DCSS_SCALER_V_LUM_START 0x48 528c2ecf20Sopenharmony_ci#define V_START_MASK GENMASK(15, 0) 538c2ecf20Sopenharmony_ci#define DCSS_SCALER_V_LUM_INC 0x4C 548c2ecf20Sopenharmony_ci#define V_INC_MASK GENMASK(15, 0) 558c2ecf20Sopenharmony_ci#define DCSS_SCALER_H_LUM_START 0x50 568c2ecf20Sopenharmony_ci#define H_START_MASK GENMASK(18, 0) 578c2ecf20Sopenharmony_ci#define DCSS_SCALER_H_LUM_INC 0x54 588c2ecf20Sopenharmony_ci#define H_INC_MASK GENMASK(15, 0) 598c2ecf20Sopenharmony_ci#define DCSS_SCALER_V_CHR_START 0x58 608c2ecf20Sopenharmony_ci#define DCSS_SCALER_V_CHR_INC 0x5C 618c2ecf20Sopenharmony_ci#define DCSS_SCALER_H_CHR_START 0x60 628c2ecf20Sopenharmony_ci#define DCSS_SCALER_H_CHR_INC 0x64 638c2ecf20Sopenharmony_ci#define DCSS_SCALER_COEF_VLUM 0x80 648c2ecf20Sopenharmony_ci#define DCSS_SCALER_COEF_HLUM 0x140 658c2ecf20Sopenharmony_ci#define DCSS_SCALER_COEF_VCHR 0x200 668c2ecf20Sopenharmony_ci#define DCSS_SCALER_COEF_HCHR 0x300 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct dcss_scaler_ch { 698c2ecf20Sopenharmony_ci void __iomem *base_reg; 708c2ecf20Sopenharmony_ci u32 base_ofs; 718c2ecf20Sopenharmony_ci struct dcss_scaler *scl; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci u32 sdata_ctrl; 748c2ecf20Sopenharmony_ci u32 scaler_ctrl; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci bool scaler_ctrl_chgd; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci u32 c_vstart; 798c2ecf20Sopenharmony_ci u32 c_hstart; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistruct dcss_scaler { 838c2ecf20Sopenharmony_ci struct device *dev; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci struct dcss_ctxld *ctxld; 868c2ecf20Sopenharmony_ci u32 ctx_id; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci struct dcss_scaler_ch ch[3]; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* scaler coefficients generator */ 928c2ecf20Sopenharmony_ci#define PSC_FRAC_BITS 30 938c2ecf20Sopenharmony_ci#define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS) 948c2ecf20Sopenharmony_ci#define PSC_BITS_FOR_PHASE 4 958c2ecf20Sopenharmony_ci#define PSC_NUM_PHASES 16 968c2ecf20Sopenharmony_ci#define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1) 978c2ecf20Sopenharmony_ci#define PSC_NUM_TAPS 7 988c2ecf20Sopenharmony_ci#define PSC_NUM_TAPS_RGBA 5 998c2ecf20Sopenharmony_ci#define PSC_COEFF_PRECISION 10 1008c2ecf20Sopenharmony_ci#define PSC_PHASE_FRACTION_BITS 13 1018c2ecf20Sopenharmony_ci#define PSC_PHASE_MASK (PSC_NUM_PHASES - 1) 1028c2ecf20Sopenharmony_ci#define PSC_Q_FRACTION 19 1038c2ecf20Sopenharmony_ci#define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1)) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * mult_q() - Performs fixed-point multiplication. 1078c2ecf20Sopenharmony_ci * @A: multiplier 1088c2ecf20Sopenharmony_ci * @B: multiplicand 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic int mult_q(int A, int B) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci int result; 1138c2ecf20Sopenharmony_ci s64 temp; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci temp = (int64_t)A * (int64_t)B; 1168c2ecf20Sopenharmony_ci temp += PSC_Q_ROUND_OFFSET; 1178c2ecf20Sopenharmony_ci result = (int)(temp >> PSC_Q_FRACTION); 1188c2ecf20Sopenharmony_ci return result; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * div_q() - Performs fixed-point division. 1238c2ecf20Sopenharmony_ci * @A: dividend 1248c2ecf20Sopenharmony_ci * @B: divisor 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistatic int div_q(int A, int B) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int result; 1298c2ecf20Sopenharmony_ci s64 temp; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci temp = (int64_t)A << PSC_Q_FRACTION; 1328c2ecf20Sopenharmony_ci if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0)) 1338c2ecf20Sopenharmony_ci temp += B / 2; 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci temp -= B / 2; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci result = (int)(temp / B); 1388c2ecf20Sopenharmony_ci return result; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/** 1428c2ecf20Sopenharmony_ci * exp_approx_q() - Compute approximation to exp(x) function using Taylor 1438c2ecf20Sopenharmony_ci * series. 1448c2ecf20Sopenharmony_ci * @x: fixed-point argument of exp function 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic int exp_approx_q(int x) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int sum = 1 << PSC_Q_FRACTION; 1498c2ecf20Sopenharmony_ci int term = 1 << PSC_Q_FRACTION; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION)); 1528c2ecf20Sopenharmony_ci sum += term; 1538c2ecf20Sopenharmony_ci term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION)); 1548c2ecf20Sopenharmony_ci sum += term; 1558c2ecf20Sopenharmony_ci term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION)); 1568c2ecf20Sopenharmony_ci sum += term; 1578c2ecf20Sopenharmony_ci term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION)); 1588c2ecf20Sopenharmony_ci sum += term; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return sum; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/** 1648c2ecf20Sopenharmony_ci * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. 1658c2ecf20Sopenharmony_ci * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] 1668c2ecf20Sopenharmony_ci * @use_5_taps: indicates whether to use 5 taps or 7 taps 1678c2ecf20Sopenharmony_ci * @coef: output filter coefficients 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistatic void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, 1708c2ecf20Sopenharmony_ci bool phase0_identity, 1718c2ecf20Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci int sigma_q, g0_q, g1_q, g2_q; 1748c2ecf20Sopenharmony_ci int tap_cnt1, tap_cnt2, tap_idx, phase_cnt; 1758c2ecf20Sopenharmony_ci int mid; 1768c2ecf20Sopenharmony_ci int phase; 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci int taps; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (use_5_taps) 1818c2ecf20Sopenharmony_ci for (phase = 0; phase < PSC_STORED_PHASES; phase++) { 1828c2ecf20Sopenharmony_ci coef[phase][0] = 0; 1838c2ecf20Sopenharmony_ci coef[phase][PSC_NUM_TAPS - 1] = 0; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* seed coefficient scanner */ 1878c2ecf20Sopenharmony_ci taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS; 1888c2ecf20Sopenharmony_ci mid = (PSC_NUM_PHASES * taps) / 2 - 1; 1898c2ecf20Sopenharmony_ci phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2; 1908c2ecf20Sopenharmony_ci tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; 1918c2ecf20Sopenharmony_ci tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* seed gaussian filter generator */ 1948c2ecf20Sopenharmony_ci sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q); 1958c2ecf20Sopenharmony_ci g0_q = 1 << PSC_Q_FRACTION; 1968c2ecf20Sopenharmony_ci g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET, 1978c2ecf20Sopenharmony_ci mult_q(sigma_q, sigma_q))); 1988c2ecf20Sopenharmony_ci g2_q = mult_q(g1_q, g1_q); 1998c2ecf20Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (i = 0; i < mid; i++) { 2028c2ecf20Sopenharmony_ci phase_cnt++; 2038c2ecf20Sopenharmony_ci tap_cnt1--; 2048c2ecf20Sopenharmony_ci tap_cnt2++; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci g0_q = mult_q(g0_q, g1_q); 2078c2ecf20Sopenharmony_ci g1_q = mult_q(g1_q, g2_q); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if ((phase_cnt & PSC_PHASE_MASK) <= 8) { 2108c2ecf20Sopenharmony_ci tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE; 2118c2ecf20Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) { 2148c2ecf20Sopenharmony_ci tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE; 2158c2ecf20Sopenharmony_ci coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci phase_cnt++; 2208c2ecf20Sopenharmony_ci tap_cnt1--; 2218c2ecf20Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* override phase 0 with identity filter if specified */ 2248c2ecf20Sopenharmony_ci if (phase0_identity) 2258c2ecf20Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) 2268c2ecf20Sopenharmony_ci coef[0][i] = i == (PSC_NUM_TAPS >> 1) ? 2278c2ecf20Sopenharmony_ci (1 << PSC_COEFF_PRECISION) : 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* normalize coef */ 2308c2ecf20Sopenharmony_ci for (phase = 0; phase < PSC_STORED_PHASES; phase++) { 2318c2ecf20Sopenharmony_ci int sum = 0; 2328c2ecf20Sopenharmony_ci s64 ll_temp; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) 2358c2ecf20Sopenharmony_ci sum += coef[phase][i]; 2368c2ecf20Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) { 2378c2ecf20Sopenharmony_ci ll_temp = coef[phase][i]; 2388c2ecf20Sopenharmony_ci ll_temp <<= PSC_COEFF_PRECISION; 2398c2ecf20Sopenharmony_ci ll_temp += sum >> 1; 2408c2ecf20Sopenharmony_ci ll_temp /= sum; 2418c2ecf20Sopenharmony_ci coef[phase][i] = (int)ll_temp; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/** 2478c2ecf20Sopenharmony_ci * dcss_scaler_filter_design() - Compute filter coefficients using 2488c2ecf20Sopenharmony_ci * Gaussian filter. 2498c2ecf20Sopenharmony_ci * @src_length: length of input 2508c2ecf20Sopenharmony_ci * @dst_length: length of output 2518c2ecf20Sopenharmony_ci * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps 2528c2ecf20Sopenharmony_ci * @coef: output coefficients 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic void dcss_scaler_filter_design(int src_length, int dst_length, 2558c2ecf20Sopenharmony_ci bool use_5_taps, bool phase0_identity, 2568c2ecf20Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int fc_q; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* compute cutoff frequency */ 2618c2ecf20Sopenharmony_ci if (dst_length >= src_length) 2628c2ecf20Sopenharmony_ci fc_q = div_q(1, PSC_NUM_PHASES); 2638c2ecf20Sopenharmony_ci else 2648c2ecf20Sopenharmony_ci fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* compute gaussian filter coefficients */ 2678c2ecf20Sopenharmony_ci dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct dcss_scaler *scl = ch->scl; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int dcss_scaler_ch_init_all(struct dcss_scaler *scl, 2788c2ecf20Sopenharmony_ci unsigned long scaler_base) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct dcss_scaler_ch *ch; 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 2848c2ecf20Sopenharmony_ci ch = &scl->ch[i]; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci ch->base_ofs = scaler_base + i * 0x400; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ch->base_reg = ioremap(ch->base_ofs, SZ_4K); 2898c2ecf20Sopenharmony_ci if (!ch->base_reg) { 2908c2ecf20Sopenharmony_ci dev_err(scl->dev, "scaler: unable to remap ch base\n"); 2918c2ecf20Sopenharmony_ci return -ENOMEM; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ch->scl = scl; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciint dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct dcss_scaler *scaler; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci scaler = kzalloc(sizeof(*scaler), GFP_KERNEL); 3058c2ecf20Sopenharmony_ci if (!scaler) 3068c2ecf20Sopenharmony_ci return -ENOMEM; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dcss->scaler = scaler; 3098c2ecf20Sopenharmony_ci scaler->dev = dcss->dev; 3108c2ecf20Sopenharmony_ci scaler->ctxld = dcss->ctxld; 3118c2ecf20Sopenharmony_ci scaler->ctx_id = CTX_SB_HP; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (dcss_scaler_ch_init_all(scaler, scaler_base)) { 3148c2ecf20Sopenharmony_ci int i; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 3178c2ecf20Sopenharmony_ci if (scaler->ch[i].base_reg) 3188c2ecf20Sopenharmony_ci iounmap(scaler->ch[i].base_reg); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci kfree(scaler); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return -ENOMEM; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_civoid dcss_scaler_exit(struct dcss_scaler *scl) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int ch_no; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (ch_no = 0; ch_no < 3; ch_no++) { 3348c2ecf20Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_no]; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (ch->base_reg) 3398c2ecf20Sopenharmony_ci iounmap(ch->base_reg); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci kfree(scl); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_civoid dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_num]; 3488c2ecf20Sopenharmony_ci u32 scaler_ctrl; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (en) 3538c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ch->scaler_ctrl != scaler_ctrl) 3568c2ecf20Sopenharmony_ci ch->scaler_ctrl_chgd = true; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ch->scaler_ctrl = scaler_ctrl; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci ch->sdata_ctrl &= ~YUV_EN; 3648c2ecf20Sopenharmony_ci ch->sdata_ctrl |= en ? YUV_EN : 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci ch->sdata_ctrl &= ~RTRAM_8LINES; 3708c2ecf20Sopenharmony_ci ch->sdata_ctrl |= en ? RTRAM_8LINES : 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci u32 val; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci val = depth == 30 ? 2 : 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dcss_scaler_write(ch, 3808c2ecf20Sopenharmony_ci ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) | 3818c2ecf20Sopenharmony_ci ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK), 3828c2ecf20Sopenharmony_ci DCSS_SCALER_BIT_DEPTH); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cienum buffer_format { 3868c2ecf20Sopenharmony_ci BUF_FMT_YUV420, 3878c2ecf20Sopenharmony_ci BUF_FMT_YUV422, 3888c2ecf20Sopenharmony_ci BUF_FMT_ARGB8888_YUV444, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cienum chroma_location { 3928c2ecf20Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0, 3938c2ecf20Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1, 3948c2ecf20Sopenharmony_ci PSC_LOC_HORZ_0_VERT_0 = 2, 3958c2ecf20Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3, 3968c2ecf20Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4, 3978c2ecf20Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void dcss_scaler_format_set(struct dcss_scaler_ch *ch, 4018c2ecf20Sopenharmony_ci enum buffer_format src_fmt, 4028c2ecf20Sopenharmony_ci enum buffer_format dst_fmt) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT); 4058c2ecf20Sopenharmony_ci dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void dcss_scaler_res_set(struct dcss_scaler_ch *ch, 4098c2ecf20Sopenharmony_ci int src_xres, int src_yres, 4108c2ecf20Sopenharmony_ci int dst_xres, int dst_yres, 4118c2ecf20Sopenharmony_ci u32 pix_format, enum buffer_format dst_format) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres; 4148c2ecf20Sopenharmony_ci u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres; 4158c2ecf20Sopenharmony_ci bool src_is_444 = true; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci lsrc_xres = src_xres; 4188c2ecf20Sopenharmony_ci csrc_xres = src_xres; 4198c2ecf20Sopenharmony_ci lsrc_yres = src_yres; 4208c2ecf20Sopenharmony_ci csrc_yres = src_yres; 4218c2ecf20Sopenharmony_ci ldst_xres = dst_xres; 4228c2ecf20Sopenharmony_ci cdst_xres = dst_xres; 4238c2ecf20Sopenharmony_ci ldst_yres = dst_yres; 4248c2ecf20Sopenharmony_ci cdst_yres = dst_yres; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY || 4278c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) { 4288c2ecf20Sopenharmony_ci csrc_xres >>= 1; 4298c2ecf20Sopenharmony_ci src_is_444 = false; 4308c2ecf20Sopenharmony_ci } else if (pix_format == DRM_FORMAT_NV12 || 4318c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_NV21) { 4328c2ecf20Sopenharmony_ci csrc_xres >>= 1; 4338c2ecf20Sopenharmony_ci csrc_yres >>= 1; 4348c2ecf20Sopenharmony_ci src_is_444 = false; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (dst_format == BUF_FMT_YUV422) 4388c2ecf20Sopenharmony_ci cdst_xres >>= 1; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */ 4418c2ecf20Sopenharmony_ci if (src_is_444 && dst_format == BUF_FMT_YUV422) { 4428c2ecf20Sopenharmony_ci lsrc_yres--; 4438c2ecf20Sopenharmony_ci csrc_yres--; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 4478c2ecf20Sopenharmony_ci (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), 4488c2ecf20Sopenharmony_ci DCSS_SCALER_SRC_LUM_RES); 4498c2ecf20Sopenharmony_ci dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 4508c2ecf20Sopenharmony_ci (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), 4518c2ecf20Sopenharmony_ci DCSS_SCALER_SRC_CHR_RES); 4528c2ecf20Sopenharmony_ci dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 4538c2ecf20Sopenharmony_ci (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK), 4548c2ecf20Sopenharmony_ci DCSS_SCALER_DST_LUM_RES); 4558c2ecf20Sopenharmony_ci dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 4568c2ecf20Sopenharmony_ci (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK), 4578c2ecf20Sopenharmony_ci DCSS_SCALER_DST_CHR_RES); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci#define downscale_fp(factor, fp_pos) ((factor) << (fp_pos)) 4618c2ecf20Sopenharmony_ci#define upscale_fp(factor, fp_pos) ((1 << (fp_pos)) / (factor)) 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistruct dcss_scaler_factors { 4648c2ecf20Sopenharmony_ci int downscale; 4658c2ecf20Sopenharmony_ci int upscale; 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct dcss_scaler_factors dcss_scaler_factors[] = { 4698c2ecf20Sopenharmony_ci {3, 8}, {5, 8}, {5, 8}, 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void dcss_scaler_fractions_set(struct dcss_scaler_ch *ch, 4738c2ecf20Sopenharmony_ci int src_xres, int src_yres, 4748c2ecf20Sopenharmony_ci int dst_xres, int dst_yres, 4758c2ecf20Sopenharmony_ci u32 src_format, u32 dst_format, 4768c2ecf20Sopenharmony_ci enum chroma_location src_chroma_loc) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres; 4798c2ecf20Sopenharmony_ci u32 l_vinc, l_hinc, c_vinc, c_hinc; 4808c2ecf20Sopenharmony_ci u32 c_vstart, c_hstart; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci src_c_xres = src_xres; 4838c2ecf20Sopenharmony_ci src_c_yres = src_yres; 4848c2ecf20Sopenharmony_ci dst_c_xres = dst_xres; 4858c2ecf20Sopenharmony_ci dst_c_yres = dst_yres; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci c_vstart = 0; 4888c2ecf20Sopenharmony_ci c_hstart = 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* adjustments for source chroma location */ 4918c2ecf20Sopenharmony_ci if (src_format == BUF_FMT_YUV420) { 4928c2ecf20Sopenharmony_ci /* vertical input chroma position adjustment */ 4938c2ecf20Sopenharmony_ci switch (src_chroma_loc) { 4948c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_0_VERT_1_OVER_4: 4958c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * move chroma up to first luma line 4988c2ecf20Sopenharmony_ci * (1/4 chroma input line spacing) 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_0_VERT_1_OVER_2: 5038c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * move chroma up to first luma line 5068c2ecf20Sopenharmony_ci * (1/2 chroma input line spacing) 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1)); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci default: 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci /* horizontal input chroma position adjustment */ 5148c2ecf20Sopenharmony_ci switch (src_chroma_loc) { 5158c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: 5168c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_0: 5178c2ecf20Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: 5188c2ecf20Sopenharmony_ci /* move chroma left 1/4 chroma input sample spacing */ 5198c2ecf20Sopenharmony_ci c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci default: 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* adjustments to chroma resolution */ 5278c2ecf20Sopenharmony_ci if (src_format == BUF_FMT_YUV420) { 5288c2ecf20Sopenharmony_ci src_c_xres >>= 1; 5298c2ecf20Sopenharmony_ci src_c_yres >>= 1; 5308c2ecf20Sopenharmony_ci } else if (src_format == BUF_FMT_YUV422) { 5318c2ecf20Sopenharmony_ci src_c_xres >>= 1; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (dst_format == BUF_FMT_YUV422) 5358c2ecf20Sopenharmony_ci dst_c_xres >>= 1; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; 5388c2ecf20Sopenharmony_ci c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres; 5398c2ecf20Sopenharmony_ci l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; 5408c2ecf20Sopenharmony_ci c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* save chroma start phase */ 5438c2ecf20Sopenharmony_ci ch->c_vstart = c_vstart; 5448c2ecf20Sopenharmony_ci ch->c_hstart = c_hstart; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START); 5478c2ecf20Sopenharmony_ci dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START); 5508c2ecf20Sopenharmony_ci dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START); 5538c2ecf20Sopenharmony_ci dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START); 5568c2ecf20Sopenharmony_ci dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ciint dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num, 5608c2ecf20Sopenharmony_ci int *min, int *max) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci *min = upscale_fp(dcss_scaler_factors[ch_num].upscale, 16); 5638c2ecf20Sopenharmony_ci *max = downscale_fp(dcss_scaler_factors[ch_num].downscale, 16); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch, 5698c2ecf20Sopenharmony_ci int base_addr, 5708c2ecf20Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci int i, phase; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci for (i = 0; i < PSC_STORED_PHASES; i++) { 5758c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 | 5768c2ecf20Sopenharmony_ci (coef[i][2] & 0xfff) << 4 | 5778c2ecf20Sopenharmony_ci (coef[i][3] & 0xf00) >> 8), 5788c2ecf20Sopenharmony_ci base_addr + i * sizeof(u32)); 5798c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 | 5808c2ecf20Sopenharmony_ci (coef[i][4] & 0xfff) << 8 | 5818c2ecf20Sopenharmony_ci (coef[i][5] & 0xff0) >> 4), 5828c2ecf20Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 5838c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24), 5848c2ecf20Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* reverse both phase and tap orderings */ 5888c2ecf20Sopenharmony_ci for (phase = (PSC_NUM_PHASES >> 1) - 1; 5898c2ecf20Sopenharmony_ci i < PSC_NUM_PHASES; i++, phase--) { 5908c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 | 5918c2ecf20Sopenharmony_ci (coef[phase][4] & 0xfff) << 4 | 5928c2ecf20Sopenharmony_ci (coef[phase][3] & 0xf00) >> 8), 5938c2ecf20Sopenharmony_ci base_addr + i * sizeof(u32)); 5948c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 | 5958c2ecf20Sopenharmony_ci (coef[phase][2] & 0xfff) << 8 | 5968c2ecf20Sopenharmony_ci (coef[phase][1] & 0xff0) >> 4), 5978c2ecf20Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 5988c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24), 5998c2ecf20Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch, 6048c2ecf20Sopenharmony_ci int base_addr, 6058c2ecf20Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci int i, phase; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci for (i = 0; i < PSC_STORED_PHASES; i++) { 6108c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 | 6118c2ecf20Sopenharmony_ci (coef[i][1] & 0xfff) << 4 | 6128c2ecf20Sopenharmony_ci (coef[i][2] & 0xf00) >> 8), 6138c2ecf20Sopenharmony_ci base_addr + i * sizeof(u32)); 6148c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 | 6158c2ecf20Sopenharmony_ci (coef[i][3] & 0xfff) << 8 | 6168c2ecf20Sopenharmony_ci (coef[i][4] & 0xff0) >> 4), 6178c2ecf20Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 6188c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 | 6198c2ecf20Sopenharmony_ci (coef[i][5] & 0xfff) << 12 | 6208c2ecf20Sopenharmony_ci (coef[i][6] & 0xfff)), 6218c2ecf20Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* reverse both phase and tap orderings */ 6258c2ecf20Sopenharmony_ci for (phase = (PSC_NUM_PHASES >> 1) - 1; 6268c2ecf20Sopenharmony_ci i < PSC_NUM_PHASES; i++, phase--) { 6278c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 | 6288c2ecf20Sopenharmony_ci (coef[phase][5] & 0xfff) << 4 | 6298c2ecf20Sopenharmony_ci (coef[phase][4] & 0xf00) >> 8), 6308c2ecf20Sopenharmony_ci base_addr + i * sizeof(u32)); 6318c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 | 6328c2ecf20Sopenharmony_ci (coef[phase][3] & 0xfff) << 8 | 6338c2ecf20Sopenharmony_ci (coef[phase][2] & 0xff0) >> 4), 6348c2ecf20Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 6358c2ecf20Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 | 6368c2ecf20Sopenharmony_ci (coef[phase][1] & 0xfff) << 12 | 6378c2ecf20Sopenharmony_ci (coef[phase][0] & 0xfff)), 6388c2ecf20Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch, 6438c2ecf20Sopenharmony_ci enum buffer_format src_format, 6448c2ecf20Sopenharmony_ci enum buffer_format dst_format, 6458c2ecf20Sopenharmony_ci bool use_5_taps, 6468c2ecf20Sopenharmony_ci int src_xres, int src_yres, int dst_xres, 6478c2ecf20Sopenharmony_ci int dst_yres) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; 6508c2ecf20Sopenharmony_ci bool program_5_taps = use_5_taps || 6518c2ecf20Sopenharmony_ci (dst_format == BUF_FMT_YUV422 && 6528c2ecf20Sopenharmony_ci src_format == BUF_FMT_ARGB8888_YUV444); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* horizontal luma */ 6558c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 6568c2ecf20Sopenharmony_ci src_xres == dst_xres, coef); 6578c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* vertical luma */ 6608c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, 6618c2ecf20Sopenharmony_ci src_yres == dst_yres, coef); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (program_5_taps) 6648c2ecf20Sopenharmony_ci dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 6658c2ecf20Sopenharmony_ci else 6668c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* adjust chroma resolution */ 6698c2ecf20Sopenharmony_ci if (src_format != BUF_FMT_ARGB8888_YUV444) 6708c2ecf20Sopenharmony_ci src_xres >>= 1; 6718c2ecf20Sopenharmony_ci if (src_format == BUF_FMT_YUV420) 6728c2ecf20Sopenharmony_ci src_yres >>= 1; 6738c2ecf20Sopenharmony_ci if (dst_format != BUF_FMT_ARGB8888_YUV444) 6748c2ecf20Sopenharmony_ci dst_xres >>= 1; 6758c2ecf20Sopenharmony_ci if (dst_format == BUF_FMT_YUV420) /* should not happen */ 6768c2ecf20Sopenharmony_ci dst_yres >>= 1; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* horizontal chroma */ 6798c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 6808c2ecf20Sopenharmony_ci (src_xres == dst_xres) && (ch->c_hstart == 0), 6818c2ecf20Sopenharmony_ci coef); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* vertical chroma */ 6868c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, 6878c2ecf20Sopenharmony_ci (src_yres == dst_yres) && (ch->c_vstart == 0), 6888c2ecf20Sopenharmony_ci coef); 6898c2ecf20Sopenharmony_ci if (program_5_taps) 6908c2ecf20Sopenharmony_ci dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); 6918c2ecf20Sopenharmony_ci else 6928c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch, 6968c2ecf20Sopenharmony_ci int src_xres, int src_yres, int dst_xres, 6978c2ecf20Sopenharmony_ci int dst_yres) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* horizontal RGB */ 7028c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 7038c2ecf20Sopenharmony_ci src_xres == dst_xres, coef); 7048c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* vertical RGB */ 7078c2ecf20Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, false, 7088c2ecf20Sopenharmony_ci src_yres == dst_yres, coef); 7098c2ecf20Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch, 7138c2ecf20Sopenharmony_ci const struct drm_format_info *format) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci u32 a2r10g10b10_format; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (format->is_yuv) 7188c2ecf20Sopenharmony_ci return; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (format->depth != 30) 7238c2ecf20Sopenharmony_ci return; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci switch (format->format) { 7268c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB2101010: 7278c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB2101010: 7288c2ecf20Sopenharmony_ci a2r10g10b10_format = 0; 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR2101010: 7328c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR2101010: 7338c2ecf20Sopenharmony_ci a2r10g10b10_format = 5; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBA1010102: 7378c2ecf20Sopenharmony_ci case DRM_FORMAT_RGBX1010102: 7388c2ecf20Sopenharmony_ci a2r10g10b10_format = 6; 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRA1010102: 7428c2ecf20Sopenharmony_ci case DRM_FORMAT_BGRX1010102: 7438c2ecf20Sopenharmony_ci a2r10g10b10_format = 11; 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci default: 7478c2ecf20Sopenharmony_ci a2r10g10b10_format = 0; 7488c2ecf20Sopenharmony_ci break; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_civoid dcss_scaler_setup(struct dcss_scaler *scl, int ch_num, 7558c2ecf20Sopenharmony_ci const struct drm_format_info *format, 7568c2ecf20Sopenharmony_ci int src_xres, int src_yres, int dst_xres, int dst_yres, 7578c2ecf20Sopenharmony_ci u32 vrefresh_hz) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_num]; 7608c2ecf20Sopenharmony_ci unsigned int pixel_depth = 0; 7618c2ecf20Sopenharmony_ci bool rtr_8line_en = false; 7628c2ecf20Sopenharmony_ci bool use_5_taps = false; 7638c2ecf20Sopenharmony_ci enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444; 7648c2ecf20Sopenharmony_ci enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444; 7658c2ecf20Sopenharmony_ci u32 pix_format = format->format; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (format->is_yuv) { 7688c2ecf20Sopenharmony_ci dcss_scaler_yuv_enable(ch, true); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (pix_format == DRM_FORMAT_NV12 || 7718c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_NV21) { 7728c2ecf20Sopenharmony_ci rtr_8line_en = true; 7738c2ecf20Sopenharmony_ci src_format = BUF_FMT_YUV420; 7748c2ecf20Sopenharmony_ci } else if (pix_format == DRM_FORMAT_UYVY || 7758c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_VYUY || 7768c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_YUYV || 7778c2ecf20Sopenharmony_ci pix_format == DRM_FORMAT_YVYU) { 7788c2ecf20Sopenharmony_ci src_format = BUF_FMT_YUV422; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci use_5_taps = !rtr_8line_en; 7828c2ecf20Sopenharmony_ci } else { 7838c2ecf20Sopenharmony_ci dcss_scaler_yuv_enable(ch, false); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci pixel_depth = format->depth; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres, 7898c2ecf20Sopenharmony_ci dst_yres, src_format, dst_format, 7908c2ecf20Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_4); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (format->is_yuv) 7938c2ecf20Sopenharmony_ci dcss_scaler_yuv_coef_set(ch, src_format, dst_format, 7948c2ecf20Sopenharmony_ci use_5_taps, src_xres, src_yres, 7958c2ecf20Sopenharmony_ci dst_xres, dst_yres); 7968c2ecf20Sopenharmony_ci else 7978c2ecf20Sopenharmony_ci dcss_scaler_rgb_coef_set(ch, src_xres, src_yres, 7988c2ecf20Sopenharmony_ci dst_xres, dst_yres); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en); 8018c2ecf20Sopenharmony_ci dcss_scaler_bit_depth_set(ch, pixel_depth); 8028c2ecf20Sopenharmony_ci dcss_scaler_set_rgb10_order(ch, format); 8038c2ecf20Sopenharmony_ci dcss_scaler_format_set(ch, src_format, dst_format); 8048c2ecf20Sopenharmony_ci dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres, 8058c2ecf20Sopenharmony_ci pix_format, dst_format); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* This function will be called from interrupt context. */ 8098c2ecf20Sopenharmony_civoid dcss_scaler_write_sclctrl(struct dcss_scaler *scl) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci int chnum; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci dcss_ctxld_assert_locked(scl->ctxld); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci for (chnum = 0; chnum < 3; chnum++) { 8168c2ecf20Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[chnum]; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (ch->scaler_ctrl_chgd) { 8198c2ecf20Sopenharmony_ci dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id, 8208c2ecf20Sopenharmony_ci ch->scaler_ctrl, 8218c2ecf20Sopenharmony_ci ch->base_ofs + 8228c2ecf20Sopenharmony_ci DCSS_SCALER_CTRL); 8238c2ecf20Sopenharmony_ci ch->scaler_ctrl_chgd = false; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 827