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