162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2019 NXP. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "dcss-dev.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define DCSS_SCALER_CTRL 0x00 1462306a36Sopenharmony_ci#define SCALER_EN BIT(0) 1562306a36Sopenharmony_ci#define REPEAT_EN BIT(4) 1662306a36Sopenharmony_ci#define SCALE2MEM_EN BIT(8) 1762306a36Sopenharmony_ci#define MEM2OFIFO_EN BIT(12) 1862306a36Sopenharmony_ci#define DCSS_SCALER_OFIFO_CTRL 0x04 1962306a36Sopenharmony_ci#define OFIFO_LOW_THRES_POS 0 2062306a36Sopenharmony_ci#define OFIFO_LOW_THRES_MASK GENMASK(9, 0) 2162306a36Sopenharmony_ci#define OFIFO_HIGH_THRES_POS 16 2262306a36Sopenharmony_ci#define OFIFO_HIGH_THRES_MASK GENMASK(25, 16) 2362306a36Sopenharmony_ci#define UNDERRUN_DETECT_CLR BIT(26) 2462306a36Sopenharmony_ci#define LOW_THRES_DETECT_CLR BIT(27) 2562306a36Sopenharmony_ci#define HIGH_THRES_DETECT_CLR BIT(28) 2662306a36Sopenharmony_ci#define UNDERRUN_DETECT_EN BIT(29) 2762306a36Sopenharmony_ci#define LOW_THRES_DETECT_EN BIT(30) 2862306a36Sopenharmony_ci#define HIGH_THRES_DETECT_EN BIT(31) 2962306a36Sopenharmony_ci#define DCSS_SCALER_SDATA_CTRL 0x08 3062306a36Sopenharmony_ci#define YUV_EN BIT(0) 3162306a36Sopenharmony_ci#define RTRAM_8LINES BIT(1) 3262306a36Sopenharmony_ci#define Y_UV_BYTE_SWAP BIT(4) 3362306a36Sopenharmony_ci#define A2R10G10B10_FORMAT_POS 8 3462306a36Sopenharmony_ci#define A2R10G10B10_FORMAT_MASK GENMASK(11, 8) 3562306a36Sopenharmony_ci#define DCSS_SCALER_BIT_DEPTH 0x0C 3662306a36Sopenharmony_ci#define LUM_BIT_DEPTH_POS 0 3762306a36Sopenharmony_ci#define LUM_BIT_DEPTH_MASK GENMASK(1, 0) 3862306a36Sopenharmony_ci#define CHR_BIT_DEPTH_POS 4 3962306a36Sopenharmony_ci#define CHR_BIT_DEPTH_MASK GENMASK(5, 4) 4062306a36Sopenharmony_ci#define DCSS_SCALER_SRC_FORMAT 0x10 4162306a36Sopenharmony_ci#define DCSS_SCALER_DST_FORMAT 0x14 4262306a36Sopenharmony_ci#define FORMAT_MASK GENMASK(1, 0) 4362306a36Sopenharmony_ci#define DCSS_SCALER_SRC_LUM_RES 0x18 4462306a36Sopenharmony_ci#define DCSS_SCALER_SRC_CHR_RES 0x1C 4562306a36Sopenharmony_ci#define DCSS_SCALER_DST_LUM_RES 0x20 4662306a36Sopenharmony_ci#define DCSS_SCALER_DST_CHR_RES 0x24 4762306a36Sopenharmony_ci#define WIDTH_POS 0 4862306a36Sopenharmony_ci#define WIDTH_MASK GENMASK(11, 0) 4962306a36Sopenharmony_ci#define HEIGHT_POS 16 5062306a36Sopenharmony_ci#define HEIGHT_MASK GENMASK(27, 16) 5162306a36Sopenharmony_ci#define DCSS_SCALER_V_LUM_START 0x48 5262306a36Sopenharmony_ci#define V_START_MASK GENMASK(15, 0) 5362306a36Sopenharmony_ci#define DCSS_SCALER_V_LUM_INC 0x4C 5462306a36Sopenharmony_ci#define V_INC_MASK GENMASK(15, 0) 5562306a36Sopenharmony_ci#define DCSS_SCALER_H_LUM_START 0x50 5662306a36Sopenharmony_ci#define H_START_MASK GENMASK(18, 0) 5762306a36Sopenharmony_ci#define DCSS_SCALER_H_LUM_INC 0x54 5862306a36Sopenharmony_ci#define H_INC_MASK GENMASK(15, 0) 5962306a36Sopenharmony_ci#define DCSS_SCALER_V_CHR_START 0x58 6062306a36Sopenharmony_ci#define DCSS_SCALER_V_CHR_INC 0x5C 6162306a36Sopenharmony_ci#define DCSS_SCALER_H_CHR_START 0x60 6262306a36Sopenharmony_ci#define DCSS_SCALER_H_CHR_INC 0x64 6362306a36Sopenharmony_ci#define DCSS_SCALER_COEF_VLUM 0x80 6462306a36Sopenharmony_ci#define DCSS_SCALER_COEF_HLUM 0x140 6562306a36Sopenharmony_ci#define DCSS_SCALER_COEF_VCHR 0x200 6662306a36Sopenharmony_ci#define DCSS_SCALER_COEF_HCHR 0x300 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct dcss_scaler_ch { 6962306a36Sopenharmony_ci void __iomem *base_reg; 7062306a36Sopenharmony_ci u32 base_ofs; 7162306a36Sopenharmony_ci struct dcss_scaler *scl; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci u32 sdata_ctrl; 7462306a36Sopenharmony_ci u32 scaler_ctrl; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci bool scaler_ctrl_chgd; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci u32 c_vstart; 7962306a36Sopenharmony_ci u32 c_hstart; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci bool use_nn_interpolation; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct dcss_scaler { 8562306a36Sopenharmony_ci struct device *dev; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci struct dcss_ctxld *ctxld; 8862306a36Sopenharmony_ci u32 ctx_id; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci struct dcss_scaler_ch ch[3]; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* scaler coefficients generator */ 9462306a36Sopenharmony_ci#define PSC_FRAC_BITS 30 9562306a36Sopenharmony_ci#define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS) 9662306a36Sopenharmony_ci#define PSC_BITS_FOR_PHASE 4 9762306a36Sopenharmony_ci#define PSC_NUM_PHASES 16 9862306a36Sopenharmony_ci#define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1) 9962306a36Sopenharmony_ci#define PSC_NUM_TAPS 7 10062306a36Sopenharmony_ci#define PSC_NUM_TAPS_RGBA 5 10162306a36Sopenharmony_ci#define PSC_COEFF_PRECISION 10 10262306a36Sopenharmony_ci#define PSC_PHASE_FRACTION_BITS 13 10362306a36Sopenharmony_ci#define PSC_PHASE_MASK (PSC_NUM_PHASES - 1) 10462306a36Sopenharmony_ci#define PSC_Q_FRACTION 19 10562306a36Sopenharmony_ci#define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1)) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/** 10862306a36Sopenharmony_ci * mult_q() - Performs fixed-point multiplication. 10962306a36Sopenharmony_ci * @A: multiplier 11062306a36Sopenharmony_ci * @B: multiplicand 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cistatic int mult_q(int A, int B) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int result; 11562306a36Sopenharmony_ci s64 temp; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci temp = (int64_t)A * (int64_t)B; 11862306a36Sopenharmony_ci temp += PSC_Q_ROUND_OFFSET; 11962306a36Sopenharmony_ci result = (int)(temp >> PSC_Q_FRACTION); 12062306a36Sopenharmony_ci return result; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * div_q() - Performs fixed-point division. 12562306a36Sopenharmony_ci * @A: dividend 12662306a36Sopenharmony_ci * @B: divisor 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic int div_q(int A, int B) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci int result; 13162306a36Sopenharmony_ci s64 temp; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci temp = (int64_t)A << PSC_Q_FRACTION; 13462306a36Sopenharmony_ci if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0)) 13562306a36Sopenharmony_ci temp += B / 2; 13662306a36Sopenharmony_ci else 13762306a36Sopenharmony_ci temp -= B / 2; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci result = (int)(temp / B); 14062306a36Sopenharmony_ci return result; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * exp_approx_q() - Compute approximation to exp(x) function using Taylor 14562306a36Sopenharmony_ci * series. 14662306a36Sopenharmony_ci * @x: fixed-point argument of exp function 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic int exp_approx_q(int x) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int sum = 1 << PSC_Q_FRACTION; 15162306a36Sopenharmony_ci int term = 1 << PSC_Q_FRACTION; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION)); 15462306a36Sopenharmony_ci sum += term; 15562306a36Sopenharmony_ci term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION)); 15662306a36Sopenharmony_ci sum += term; 15762306a36Sopenharmony_ci term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION)); 15862306a36Sopenharmony_ci sum += term; 15962306a36Sopenharmony_ci term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION)); 16062306a36Sopenharmony_ci sum += term; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return sum; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/** 16662306a36Sopenharmony_ci * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter. 16762306a36Sopenharmony_ci * @fc_q: fixed-point cutoff frequency normalized to range [0, 1] 16862306a36Sopenharmony_ci * @use_5_taps: indicates whether to use 5 taps or 7 taps 16962306a36Sopenharmony_ci * @coef: output filter coefficients 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, 17262306a36Sopenharmony_ci bool phase0_identity, 17362306a36Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int sigma_q, g0_q, g1_q, g2_q; 17662306a36Sopenharmony_ci int tap_cnt1, tap_cnt2, tap_idx, phase_cnt; 17762306a36Sopenharmony_ci int mid; 17862306a36Sopenharmony_ci int phase; 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci int taps; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (use_5_taps) 18362306a36Sopenharmony_ci for (phase = 0; phase < PSC_STORED_PHASES; phase++) { 18462306a36Sopenharmony_ci coef[phase][0] = 0; 18562306a36Sopenharmony_ci coef[phase][PSC_NUM_TAPS - 1] = 0; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* seed coefficient scanner */ 18962306a36Sopenharmony_ci taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS; 19062306a36Sopenharmony_ci mid = (PSC_NUM_PHASES * taps) / 2 - 1; 19162306a36Sopenharmony_ci phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2; 19262306a36Sopenharmony_ci tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; 19362306a36Sopenharmony_ci tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* seed gaussian filter generator */ 19662306a36Sopenharmony_ci sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q); 19762306a36Sopenharmony_ci g0_q = 1 << PSC_Q_FRACTION; 19862306a36Sopenharmony_ci g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET, 19962306a36Sopenharmony_ci mult_q(sigma_q, sigma_q))); 20062306a36Sopenharmony_ci g2_q = mult_q(g1_q, g1_q); 20162306a36Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (i = 0; i < mid; i++) { 20462306a36Sopenharmony_ci phase_cnt++; 20562306a36Sopenharmony_ci tap_cnt1--; 20662306a36Sopenharmony_ci tap_cnt2++; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci g0_q = mult_q(g0_q, g1_q); 20962306a36Sopenharmony_ci g1_q = mult_q(g1_q, g2_q); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if ((phase_cnt & PSC_PHASE_MASK) <= 8) { 21262306a36Sopenharmony_ci tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE; 21362306a36Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) { 21662306a36Sopenharmony_ci tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE; 21762306a36Sopenharmony_ci coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci phase_cnt++; 22262306a36Sopenharmony_ci tap_cnt1--; 22362306a36Sopenharmony_ci coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* override phase 0 with identity filter if specified */ 22662306a36Sopenharmony_ci if (phase0_identity) 22762306a36Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) 22862306a36Sopenharmony_ci coef[0][i] = i == (PSC_NUM_TAPS >> 1) ? 22962306a36Sopenharmony_ci (1 << PSC_COEFF_PRECISION) : 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* normalize coef */ 23262306a36Sopenharmony_ci for (phase = 0; phase < PSC_STORED_PHASES; phase++) { 23362306a36Sopenharmony_ci int sum = 0; 23462306a36Sopenharmony_ci s64 ll_temp; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) 23762306a36Sopenharmony_ci sum += coef[phase][i]; 23862306a36Sopenharmony_ci for (i = 0; i < PSC_NUM_TAPS; i++) { 23962306a36Sopenharmony_ci ll_temp = coef[phase][i]; 24062306a36Sopenharmony_ci ll_temp <<= PSC_COEFF_PRECISION; 24162306a36Sopenharmony_ci ll_temp += sum >> 1; 24262306a36Sopenharmony_ci ll_temp /= sum; 24362306a36Sopenharmony_ci coef[phase][i] = (int)ll_temp; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void dcss_scaler_nearest_neighbor_filter(bool use_5_taps, 24962306a36Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int i, j; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci for (i = 0; i < PSC_STORED_PHASES; i++) 25462306a36Sopenharmony_ci for (j = 0; j < PSC_NUM_TAPS; j++) 25562306a36Sopenharmony_ci coef[i][j] = j == PSC_NUM_TAPS >> 1 ? 25662306a36Sopenharmony_ci (1 << PSC_COEFF_PRECISION) : 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/** 26062306a36Sopenharmony_ci * dcss_scaler_filter_design() - Compute filter coefficients using 26162306a36Sopenharmony_ci * Gaussian filter. 26262306a36Sopenharmony_ci * @src_length: length of input 26362306a36Sopenharmony_ci * @dst_length: length of output 26462306a36Sopenharmony_ci * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps 26562306a36Sopenharmony_ci * @coef: output coefficients 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_cistatic void dcss_scaler_filter_design(int src_length, int dst_length, 26862306a36Sopenharmony_ci bool use_5_taps, bool phase0_identity, 26962306a36Sopenharmony_ci int coef[][PSC_NUM_TAPS], 27062306a36Sopenharmony_ci bool nn_interpolation) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int fc_q; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* compute cutoff frequency */ 27562306a36Sopenharmony_ci if (dst_length >= src_length) 27662306a36Sopenharmony_ci fc_q = div_q(1, PSC_NUM_PHASES); 27762306a36Sopenharmony_ci else 27862306a36Sopenharmony_ci fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (nn_interpolation) 28162306a36Sopenharmony_ci dcss_scaler_nearest_neighbor_filter(use_5_taps, coef); 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci /* compute gaussian filter coefficients */ 28462306a36Sopenharmony_ci dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct dcss_scaler *scl = ch->scl; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int dcss_scaler_ch_init_all(struct dcss_scaler *scl, 29562306a36Sopenharmony_ci unsigned long scaler_base) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct dcss_scaler_ch *ch; 29862306a36Sopenharmony_ci int i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 30162306a36Sopenharmony_ci ch = &scl->ch[i]; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ch->base_ofs = scaler_base + i * 0x400; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ch->base_reg = ioremap(ch->base_ofs, SZ_4K); 30662306a36Sopenharmony_ci if (!ch->base_reg) { 30762306a36Sopenharmony_ci dev_err(scl->dev, "scaler: unable to remap ch base\n"); 30862306a36Sopenharmony_ci return -ENOMEM; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ch->scl = scl; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciint dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct dcss_scaler *scaler; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci scaler = kzalloc(sizeof(*scaler), GFP_KERNEL); 32262306a36Sopenharmony_ci if (!scaler) 32362306a36Sopenharmony_ci return -ENOMEM; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dcss->scaler = scaler; 32662306a36Sopenharmony_ci scaler->dev = dcss->dev; 32762306a36Sopenharmony_ci scaler->ctxld = dcss->ctxld; 32862306a36Sopenharmony_ci scaler->ctx_id = CTX_SB_HP; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (dcss_scaler_ch_init_all(scaler, scaler_base)) { 33162306a36Sopenharmony_ci int i; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 33462306a36Sopenharmony_ci if (scaler->ch[i].base_reg) 33562306a36Sopenharmony_ci iounmap(scaler->ch[i].base_reg); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci kfree(scaler); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return -ENOMEM; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_civoid dcss_scaler_exit(struct dcss_scaler *scl) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci int ch_no; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci for (ch_no = 0; ch_no < 3; ch_no++) { 35162306a36Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_no]; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (ch->base_reg) 35662306a36Sopenharmony_ci iounmap(ch->base_reg); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci kfree(scl); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_num]; 36562306a36Sopenharmony_ci u32 scaler_ctrl; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (en) 37062306a36Sopenharmony_ci dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (ch->scaler_ctrl != scaler_ctrl) 37362306a36Sopenharmony_ci ch->scaler_ctrl_chgd = true; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ch->scaler_ctrl = scaler_ctrl; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci ch->sdata_ctrl &= ~YUV_EN; 38162306a36Sopenharmony_ci ch->sdata_ctrl |= en ? YUV_EN : 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci ch->sdata_ctrl &= ~RTRAM_8LINES; 38762306a36Sopenharmony_ci ch->sdata_ctrl |= en ? RTRAM_8LINES : 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci u32 val; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci val = depth == 30 ? 2 : 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dcss_scaler_write(ch, 39762306a36Sopenharmony_ci ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) | 39862306a36Sopenharmony_ci ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK), 39962306a36Sopenharmony_ci DCSS_SCALER_BIT_DEPTH); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cienum buffer_format { 40362306a36Sopenharmony_ci BUF_FMT_YUV420, 40462306a36Sopenharmony_ci BUF_FMT_YUV422, 40562306a36Sopenharmony_ci BUF_FMT_ARGB8888_YUV444, 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cienum chroma_location { 40962306a36Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0, 41062306a36Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1, 41162306a36Sopenharmony_ci PSC_LOC_HORZ_0_VERT_0 = 2, 41262306a36Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3, 41362306a36Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4, 41462306a36Sopenharmony_ci PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void dcss_scaler_format_set(struct dcss_scaler_ch *ch, 41862306a36Sopenharmony_ci enum buffer_format src_fmt, 41962306a36Sopenharmony_ci enum buffer_format dst_fmt) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT); 42262306a36Sopenharmony_ci dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void dcss_scaler_res_set(struct dcss_scaler_ch *ch, 42662306a36Sopenharmony_ci int src_xres, int src_yres, 42762306a36Sopenharmony_ci int dst_xres, int dst_yres, 42862306a36Sopenharmony_ci u32 pix_format, enum buffer_format dst_format) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres; 43162306a36Sopenharmony_ci u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres; 43262306a36Sopenharmony_ci bool src_is_444 = true; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci lsrc_xres = src_xres; 43562306a36Sopenharmony_ci csrc_xres = src_xres; 43662306a36Sopenharmony_ci lsrc_yres = src_yres; 43762306a36Sopenharmony_ci csrc_yres = src_yres; 43862306a36Sopenharmony_ci ldst_xres = dst_xres; 43962306a36Sopenharmony_ci cdst_xres = dst_xres; 44062306a36Sopenharmony_ci ldst_yres = dst_yres; 44162306a36Sopenharmony_ci cdst_yres = dst_yres; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY || 44462306a36Sopenharmony_ci pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) { 44562306a36Sopenharmony_ci csrc_xres >>= 1; 44662306a36Sopenharmony_ci src_is_444 = false; 44762306a36Sopenharmony_ci } else if (pix_format == DRM_FORMAT_NV12 || 44862306a36Sopenharmony_ci pix_format == DRM_FORMAT_NV21) { 44962306a36Sopenharmony_ci csrc_xres >>= 1; 45062306a36Sopenharmony_ci csrc_yres >>= 1; 45162306a36Sopenharmony_ci src_is_444 = false; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (dst_format == BUF_FMT_YUV422) 45562306a36Sopenharmony_ci cdst_xres >>= 1; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */ 45862306a36Sopenharmony_ci if (src_is_444 && dst_format == BUF_FMT_YUV422) { 45962306a36Sopenharmony_ci lsrc_yres--; 46062306a36Sopenharmony_ci csrc_yres--; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 46462306a36Sopenharmony_ci (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), 46562306a36Sopenharmony_ci DCSS_SCALER_SRC_LUM_RES); 46662306a36Sopenharmony_ci dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 46762306a36Sopenharmony_ci (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), 46862306a36Sopenharmony_ci DCSS_SCALER_SRC_CHR_RES); 46962306a36Sopenharmony_ci dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 47062306a36Sopenharmony_ci (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK), 47162306a36Sopenharmony_ci DCSS_SCALER_DST_LUM_RES); 47262306a36Sopenharmony_ci dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | 47362306a36Sopenharmony_ci (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK), 47462306a36Sopenharmony_ci DCSS_SCALER_DST_CHR_RES); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci#define downscale_fp(factor, fp_pos) ((factor) << (fp_pos)) 47862306a36Sopenharmony_ci#define upscale_fp(factor, fp_pos) ((1 << (fp_pos)) / (factor)) 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistruct dcss_scaler_factors { 48162306a36Sopenharmony_ci int downscale; 48262306a36Sopenharmony_ci int upscale; 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic const struct dcss_scaler_factors dcss_scaler_factors[] = { 48662306a36Sopenharmony_ci {3, 8}, {5, 8}, {5, 8}, 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void dcss_scaler_fractions_set(struct dcss_scaler_ch *ch, 49062306a36Sopenharmony_ci int src_xres, int src_yres, 49162306a36Sopenharmony_ci int dst_xres, int dst_yres, 49262306a36Sopenharmony_ci u32 src_format, u32 dst_format, 49362306a36Sopenharmony_ci enum chroma_location src_chroma_loc) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres; 49662306a36Sopenharmony_ci u32 l_vinc, l_hinc, c_vinc, c_hinc; 49762306a36Sopenharmony_ci u32 c_vstart, c_hstart; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci src_c_xres = src_xres; 50062306a36Sopenharmony_ci src_c_yres = src_yres; 50162306a36Sopenharmony_ci dst_c_xres = dst_xres; 50262306a36Sopenharmony_ci dst_c_yres = dst_yres; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci c_vstart = 0; 50562306a36Sopenharmony_ci c_hstart = 0; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* adjustments for source chroma location */ 50862306a36Sopenharmony_ci if (src_format == BUF_FMT_YUV420) { 50962306a36Sopenharmony_ci /* vertical input chroma position adjustment */ 51062306a36Sopenharmony_ci switch (src_chroma_loc) { 51162306a36Sopenharmony_ci case PSC_LOC_HORZ_0_VERT_1_OVER_4: 51262306a36Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * move chroma up to first luma line 51562306a36Sopenharmony_ci * (1/4 chroma input line spacing) 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case PSC_LOC_HORZ_0_VERT_1_OVER_2: 52062306a36Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * move chroma up to first luma line 52362306a36Sopenharmony_ci * (1/2 chroma input line spacing) 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1)); 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci default: 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci /* horizontal input chroma position adjustment */ 53162306a36Sopenharmony_ci switch (src_chroma_loc) { 53262306a36Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4: 53362306a36Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_0: 53462306a36Sopenharmony_ci case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2: 53562306a36Sopenharmony_ci /* move chroma left 1/4 chroma input sample spacing */ 53662306a36Sopenharmony_ci c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2)); 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci default: 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* adjustments to chroma resolution */ 54462306a36Sopenharmony_ci if (src_format == BUF_FMT_YUV420) { 54562306a36Sopenharmony_ci src_c_xres >>= 1; 54662306a36Sopenharmony_ci src_c_yres >>= 1; 54762306a36Sopenharmony_ci } else if (src_format == BUF_FMT_YUV422) { 54862306a36Sopenharmony_ci src_c_xres >>= 1; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (dst_format == BUF_FMT_YUV422) 55262306a36Sopenharmony_ci dst_c_xres >>= 1; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; 55562306a36Sopenharmony_ci c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres; 55662306a36Sopenharmony_ci l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; 55762306a36Sopenharmony_ci c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* save chroma start phase */ 56062306a36Sopenharmony_ci ch->c_vstart = c_vstart; 56162306a36Sopenharmony_ci ch->c_hstart = c_hstart; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START); 56462306a36Sopenharmony_ci dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START); 56762306a36Sopenharmony_ci dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START); 57062306a36Sopenharmony_ci dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START); 57362306a36Sopenharmony_ci dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciint dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num, 57762306a36Sopenharmony_ci int *min, int *max) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci *min = upscale_fp(dcss_scaler_factors[ch_num].upscale, 16); 58062306a36Sopenharmony_ci *max = downscale_fp(dcss_scaler_factors[ch_num].downscale, 16); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch, 58662306a36Sopenharmony_ci int base_addr, 58762306a36Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci int i, phase; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for (i = 0; i < PSC_STORED_PHASES; i++) { 59262306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 | 59362306a36Sopenharmony_ci (coef[i][2] & 0xfff) << 4 | 59462306a36Sopenharmony_ci (coef[i][3] & 0xf00) >> 8), 59562306a36Sopenharmony_ci base_addr + i * sizeof(u32)); 59662306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 | 59762306a36Sopenharmony_ci (coef[i][4] & 0xfff) << 8 | 59862306a36Sopenharmony_ci (coef[i][5] & 0xff0) >> 4), 59962306a36Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 60062306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24), 60162306a36Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* reverse both phase and tap orderings */ 60562306a36Sopenharmony_ci for (phase = (PSC_NUM_PHASES >> 1) - 1; 60662306a36Sopenharmony_ci i < PSC_NUM_PHASES; i++, phase--) { 60762306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 | 60862306a36Sopenharmony_ci (coef[phase][4] & 0xfff) << 4 | 60962306a36Sopenharmony_ci (coef[phase][3] & 0xf00) >> 8), 61062306a36Sopenharmony_ci base_addr + i * sizeof(u32)); 61162306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 | 61262306a36Sopenharmony_ci (coef[phase][2] & 0xfff) << 8 | 61362306a36Sopenharmony_ci (coef[phase][1] & 0xff0) >> 4), 61462306a36Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 61562306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24), 61662306a36Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch, 62162306a36Sopenharmony_ci int base_addr, 62262306a36Sopenharmony_ci int coef[][PSC_NUM_TAPS]) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci int i, phase; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci for (i = 0; i < PSC_STORED_PHASES; i++) { 62762306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 | 62862306a36Sopenharmony_ci (coef[i][1] & 0xfff) << 4 | 62962306a36Sopenharmony_ci (coef[i][2] & 0xf00) >> 8), 63062306a36Sopenharmony_ci base_addr + i * sizeof(u32)); 63162306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 | 63262306a36Sopenharmony_ci (coef[i][3] & 0xfff) << 8 | 63362306a36Sopenharmony_ci (coef[i][4] & 0xff0) >> 4), 63462306a36Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 63562306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 | 63662306a36Sopenharmony_ci (coef[i][5] & 0xfff) << 12 | 63762306a36Sopenharmony_ci (coef[i][6] & 0xfff)), 63862306a36Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* reverse both phase and tap orderings */ 64262306a36Sopenharmony_ci for (phase = (PSC_NUM_PHASES >> 1) - 1; 64362306a36Sopenharmony_ci i < PSC_NUM_PHASES; i++, phase--) { 64462306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 | 64562306a36Sopenharmony_ci (coef[phase][5] & 0xfff) << 4 | 64662306a36Sopenharmony_ci (coef[phase][4] & 0xf00) >> 8), 64762306a36Sopenharmony_ci base_addr + i * sizeof(u32)); 64862306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 | 64962306a36Sopenharmony_ci (coef[phase][3] & 0xfff) << 8 | 65062306a36Sopenharmony_ci (coef[phase][2] & 0xff0) >> 4), 65162306a36Sopenharmony_ci base_addr + 0x40 + i * sizeof(u32)); 65262306a36Sopenharmony_ci dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 | 65362306a36Sopenharmony_ci (coef[phase][1] & 0xfff) << 12 | 65462306a36Sopenharmony_ci (coef[phase][0] & 0xfff)), 65562306a36Sopenharmony_ci base_addr + 0x80 + i * sizeof(u32)); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch, 66062306a36Sopenharmony_ci enum buffer_format src_format, 66162306a36Sopenharmony_ci enum buffer_format dst_format, 66262306a36Sopenharmony_ci bool use_5_taps, 66362306a36Sopenharmony_ci int src_xres, int src_yres, int dst_xres, 66462306a36Sopenharmony_ci int dst_yres) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; 66762306a36Sopenharmony_ci bool program_5_taps = use_5_taps || 66862306a36Sopenharmony_ci (dst_format == BUF_FMT_YUV422 && 66962306a36Sopenharmony_ci src_format == BUF_FMT_ARGB8888_YUV444); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* horizontal luma */ 67262306a36Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 67362306a36Sopenharmony_ci src_xres == dst_xres, coef, 67462306a36Sopenharmony_ci ch->use_nn_interpolation); 67562306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* vertical luma */ 67862306a36Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, 67962306a36Sopenharmony_ci src_yres == dst_yres, coef, 68062306a36Sopenharmony_ci ch->use_nn_interpolation); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (program_5_taps) 68362306a36Sopenharmony_ci dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 68462306a36Sopenharmony_ci else 68562306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* adjust chroma resolution */ 68862306a36Sopenharmony_ci if (src_format != BUF_FMT_ARGB8888_YUV444) 68962306a36Sopenharmony_ci src_xres >>= 1; 69062306a36Sopenharmony_ci if (src_format == BUF_FMT_YUV420) 69162306a36Sopenharmony_ci src_yres >>= 1; 69262306a36Sopenharmony_ci if (dst_format != BUF_FMT_ARGB8888_YUV444) 69362306a36Sopenharmony_ci dst_xres >>= 1; 69462306a36Sopenharmony_ci if (dst_format == BUF_FMT_YUV420) /* should not happen */ 69562306a36Sopenharmony_ci dst_yres >>= 1; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* horizontal chroma */ 69862306a36Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 69962306a36Sopenharmony_ci (src_xres == dst_xres) && (ch->c_hstart == 0), 70062306a36Sopenharmony_ci coef, ch->use_nn_interpolation); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* vertical chroma */ 70562306a36Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps, 70662306a36Sopenharmony_ci (src_yres == dst_yres) && (ch->c_vstart == 0), 70762306a36Sopenharmony_ci coef, ch->use_nn_interpolation); 70862306a36Sopenharmony_ci if (program_5_taps) 70962306a36Sopenharmony_ci dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch, 71562306a36Sopenharmony_ci int src_xres, int src_yres, int dst_xres, 71662306a36Sopenharmony_ci int dst_yres) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci int coef[PSC_STORED_PHASES][PSC_NUM_TAPS]; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* horizontal RGB */ 72162306a36Sopenharmony_ci dcss_scaler_filter_design(src_xres, dst_xres, false, 72262306a36Sopenharmony_ci src_xres == dst_xres, coef, 72362306a36Sopenharmony_ci ch->use_nn_interpolation); 72462306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* vertical RGB */ 72762306a36Sopenharmony_ci dcss_scaler_filter_design(src_yres, dst_yres, false, 72862306a36Sopenharmony_ci src_yres == dst_yres, coef, 72962306a36Sopenharmony_ci ch->use_nn_interpolation); 73062306a36Sopenharmony_ci dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch, 73462306a36Sopenharmony_ci const struct drm_format_info *format) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci u32 a2r10g10b10_format; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (format->is_yuv) 73962306a36Sopenharmony_ci return; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (format->depth != 30) 74462306a36Sopenharmony_ci return; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci switch (format->format) { 74762306a36Sopenharmony_ci case DRM_FORMAT_ARGB2101010: 74862306a36Sopenharmony_ci case DRM_FORMAT_XRGB2101010: 74962306a36Sopenharmony_ci a2r10g10b10_format = 0; 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci case DRM_FORMAT_ABGR2101010: 75362306a36Sopenharmony_ci case DRM_FORMAT_XBGR2101010: 75462306a36Sopenharmony_ci a2r10g10b10_format = 5; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci case DRM_FORMAT_RGBA1010102: 75862306a36Sopenharmony_ci case DRM_FORMAT_RGBX1010102: 75962306a36Sopenharmony_ci a2r10g10b10_format = 6; 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci case DRM_FORMAT_BGRA1010102: 76362306a36Sopenharmony_ci case DRM_FORMAT_BGRX1010102: 76462306a36Sopenharmony_ci a2r10g10b10_format = 11; 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci default: 76862306a36Sopenharmony_ci a2r10g10b10_format = 0; 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_civoid dcss_scaler_set_filter(struct dcss_scaler *scl, int ch_num, 77662306a36Sopenharmony_ci enum drm_scaling_filter scaling_filter) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_num]; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ch->use_nn_interpolation = scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_civoid dcss_scaler_setup(struct dcss_scaler *scl, int ch_num, 78462306a36Sopenharmony_ci const struct drm_format_info *format, 78562306a36Sopenharmony_ci int src_xres, int src_yres, int dst_xres, int dst_yres, 78662306a36Sopenharmony_ci u32 vrefresh_hz) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[ch_num]; 78962306a36Sopenharmony_ci unsigned int pixel_depth = 0; 79062306a36Sopenharmony_ci bool rtr_8line_en = false; 79162306a36Sopenharmony_ci bool use_5_taps = false; 79262306a36Sopenharmony_ci enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444; 79362306a36Sopenharmony_ci enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444; 79462306a36Sopenharmony_ci u32 pix_format = format->format; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (format->is_yuv) { 79762306a36Sopenharmony_ci dcss_scaler_yuv_enable(ch, true); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (pix_format == DRM_FORMAT_NV12 || 80062306a36Sopenharmony_ci pix_format == DRM_FORMAT_NV21) { 80162306a36Sopenharmony_ci rtr_8line_en = true; 80262306a36Sopenharmony_ci src_format = BUF_FMT_YUV420; 80362306a36Sopenharmony_ci } else if (pix_format == DRM_FORMAT_UYVY || 80462306a36Sopenharmony_ci pix_format == DRM_FORMAT_VYUY || 80562306a36Sopenharmony_ci pix_format == DRM_FORMAT_YUYV || 80662306a36Sopenharmony_ci pix_format == DRM_FORMAT_YVYU) { 80762306a36Sopenharmony_ci src_format = BUF_FMT_YUV422; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci use_5_taps = !rtr_8line_en; 81162306a36Sopenharmony_ci } else { 81262306a36Sopenharmony_ci dcss_scaler_yuv_enable(ch, false); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci pixel_depth = format->depth; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres, 81862306a36Sopenharmony_ci dst_yres, src_format, dst_format, 81962306a36Sopenharmony_ci PSC_LOC_HORZ_0_VERT_1_OVER_4); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (format->is_yuv) 82262306a36Sopenharmony_ci dcss_scaler_yuv_coef_set(ch, src_format, dst_format, 82362306a36Sopenharmony_ci use_5_taps, src_xres, src_yres, 82462306a36Sopenharmony_ci dst_xres, dst_yres); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci dcss_scaler_rgb_coef_set(ch, src_xres, src_yres, 82762306a36Sopenharmony_ci dst_xres, dst_yres); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en); 83062306a36Sopenharmony_ci dcss_scaler_bit_depth_set(ch, pixel_depth); 83162306a36Sopenharmony_ci dcss_scaler_set_rgb10_order(ch, format); 83262306a36Sopenharmony_ci dcss_scaler_format_set(ch, src_format, dst_format); 83362306a36Sopenharmony_ci dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres, 83462306a36Sopenharmony_ci pix_format, dst_format); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/* This function will be called from interrupt context. */ 83862306a36Sopenharmony_civoid dcss_scaler_write_sclctrl(struct dcss_scaler *scl) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci int chnum; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci dcss_ctxld_assert_locked(scl->ctxld); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci for (chnum = 0; chnum < 3; chnum++) { 84562306a36Sopenharmony_ci struct dcss_scaler_ch *ch = &scl->ch[chnum]; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (ch->scaler_ctrl_chgd) { 84862306a36Sopenharmony_ci dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id, 84962306a36Sopenharmony_ci ch->scaler_ctrl, 85062306a36Sopenharmony_ci ch->base_ofs + 85162306a36Sopenharmony_ci DCSS_SCALER_CTRL); 85262306a36Sopenharmony_ci ch->scaler_ctrl_chgd = false; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci} 856