brintos

brintos / linux-shallow public Read only

0
0
Text · 22.6 KiB · 825728c Raw
841 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2019 NXP.4 *5 * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com>6 */7 8#include <linux/device.h>9#include <linux/slab.h>10 11#include "dcss-dev.h"12 13#define DCSS_SCALER_CTRL			0x0014#define   SCALER_EN				BIT(0)15#define   REPEAT_EN				BIT(4)16#define   SCALE2MEM_EN				BIT(8)17#define   MEM2OFIFO_EN				BIT(12)18#define DCSS_SCALER_OFIFO_CTRL			0x0419#define   OFIFO_LOW_THRES_POS			020#define   OFIFO_LOW_THRES_MASK			GENMASK(9, 0)21#define   OFIFO_HIGH_THRES_POS			1622#define   OFIFO_HIGH_THRES_MASK			GENMASK(25, 16)23#define   UNDERRUN_DETECT_CLR			BIT(26)24#define   LOW_THRES_DETECT_CLR			BIT(27)25#define   HIGH_THRES_DETECT_CLR			BIT(28)26#define   UNDERRUN_DETECT_EN			BIT(29)27#define   LOW_THRES_DETECT_EN			BIT(30)28#define   HIGH_THRES_DETECT_EN			BIT(31)29#define DCSS_SCALER_SDATA_CTRL			0x0830#define   YUV_EN				BIT(0)31#define   RTRAM_8LINES				BIT(1)32#define   Y_UV_BYTE_SWAP			BIT(4)33#define   A2R10G10B10_FORMAT_POS		834#define   A2R10G10B10_FORMAT_MASK		GENMASK(11, 8)35#define DCSS_SCALER_BIT_DEPTH			0x0C36#define   LUM_BIT_DEPTH_POS			037#define   LUM_BIT_DEPTH_MASK			GENMASK(1, 0)38#define   CHR_BIT_DEPTH_POS			439#define   CHR_BIT_DEPTH_MASK			GENMASK(5, 4)40#define DCSS_SCALER_SRC_FORMAT			0x1041#define DCSS_SCALER_DST_FORMAT			0x1442#define   FORMAT_MASK				GENMASK(1, 0)43#define DCSS_SCALER_SRC_LUM_RES			0x1844#define DCSS_SCALER_SRC_CHR_RES			0x1C45#define DCSS_SCALER_DST_LUM_RES			0x2046#define DCSS_SCALER_DST_CHR_RES			0x2447#define   WIDTH_POS				048#define   WIDTH_MASK				GENMASK(11, 0)49#define   HEIGHT_POS				1650#define   HEIGHT_MASK				GENMASK(27, 16)51#define DCSS_SCALER_V_LUM_START			0x4852#define   V_START_MASK				GENMASK(15, 0)53#define DCSS_SCALER_V_LUM_INC			0x4C54#define   V_INC_MASK				GENMASK(15, 0)55#define DCSS_SCALER_H_LUM_START			0x5056#define   H_START_MASK				GENMASK(18, 0)57#define DCSS_SCALER_H_LUM_INC			0x5458#define   H_INC_MASK				GENMASK(15, 0)59#define DCSS_SCALER_V_CHR_START			0x5860#define DCSS_SCALER_V_CHR_INC			0x5C61#define DCSS_SCALER_H_CHR_START			0x6062#define DCSS_SCALER_H_CHR_INC			0x6463#define DCSS_SCALER_COEF_VLUM			0x8064#define DCSS_SCALER_COEF_HLUM			0x14065#define DCSS_SCALER_COEF_VCHR			0x20066#define DCSS_SCALER_COEF_HCHR			0x30067 68struct dcss_scaler_ch {69	void __iomem *base_reg;70	u32 base_ofs;71	struct dcss_scaler *scl;72 73	u32 sdata_ctrl;74	u32 scaler_ctrl;75 76	bool scaler_ctrl_chgd;77 78	u32 c_vstart;79	u32 c_hstart;80 81	bool use_nn_interpolation;82};83 84struct dcss_scaler {85	struct device *dev;86 87	struct dcss_ctxld *ctxld;88	u32 ctx_id;89 90	struct dcss_scaler_ch ch[3];91};92 93/* scaler coefficients generator */94#define PSC_FRAC_BITS 3095#define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS)96#define PSC_BITS_FOR_PHASE 497#define PSC_NUM_PHASES 1698#define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1)99#define PSC_NUM_TAPS 7100#define PSC_NUM_TAPS_RGBA 5101#define PSC_COEFF_PRECISION 10102#define PSC_PHASE_FRACTION_BITS 13103#define PSC_PHASE_MASK (PSC_NUM_PHASES - 1)104#define PSC_Q_FRACTION 19105#define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1))106 107/**108 * mult_q() - Performs fixed-point multiplication.109 * @A: multiplier110 * @B: multiplicand111 */112static int mult_q(int A, int B)113{114	int result;115	s64 temp;116 117	temp = (int64_t)A * (int64_t)B;118	temp += PSC_Q_ROUND_OFFSET;119	result = (int)(temp >> PSC_Q_FRACTION);120	return result;121}122 123/**124 * div_q() - Performs fixed-point division.125 * @A: dividend126 * @B: divisor127 */128static int div_q(int A, int B)129{130	int result;131	s64 temp;132 133	temp = (int64_t)A << PSC_Q_FRACTION;134	if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0))135		temp += B / 2;136	else137		temp -= B / 2;138 139	result = (int)(temp / B);140	return result;141}142 143/**144 * exp_approx_q() - Compute approximation to exp(x) function using Taylor145 *		    series.146 * @x: fixed-point argument of exp function147 */148static int exp_approx_q(int x)149{150	int sum = 1 << PSC_Q_FRACTION;151	int term = 1 << PSC_Q_FRACTION;152 153	term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION));154	sum += term;155	term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION));156	sum += term;157	term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION));158	sum += term;159	term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION));160	sum += term;161 162	return sum;163}164 165/**166 * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter.167 * @fc_q: fixed-point cutoff frequency normalized to range [0, 1]168 * @use_5_taps: indicates whether to use 5 taps or 7 taps169 * @coef: output filter coefficients170 */171static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,172					bool phase0_identity,173					int coef[][PSC_NUM_TAPS])174{175	int sigma_q, g0_q, g1_q, g2_q;176	int tap_cnt1, tap_cnt2, tap_idx, phase_cnt;177	int mid;178	int phase;179	int i;180	int taps;181 182	if (use_5_taps)183		for (phase = 0; phase < PSC_STORED_PHASES; phase++) {184			coef[phase][0] = 0;185			coef[phase][PSC_NUM_TAPS - 1] = 0;186		}187 188	/* seed coefficient scanner */189	taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS;190	mid = (PSC_NUM_PHASES * taps) / 2 - 1;191	phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2;192	tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;193	tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;194 195	/* seed gaussian filter generator */196	sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q);197	g0_q = 1 << PSC_Q_FRACTION;198	g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET,199				  mult_q(sigma_q, sigma_q)));200	g2_q = mult_q(g1_q, g1_q);201	coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q;202 203	for (i = 0; i < mid; i++) {204		phase_cnt++;205		tap_cnt1--;206		tap_cnt2++;207 208		g0_q = mult_q(g0_q, g1_q);209		g1_q = mult_q(g1_q, g2_q);210 211		if ((phase_cnt & PSC_PHASE_MASK) <= 8) {212			tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE;213			coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q;214		}215		if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) {216			tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE;217			coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q;218		}219	}220 221	phase_cnt++;222	tap_cnt1--;223	coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0;224 225	/* override phase 0 with identity filter if specified */226	if (phase0_identity)227		for (i = 0; i < PSC_NUM_TAPS; i++)228			coef[0][i] = i == (PSC_NUM_TAPS >> 1) ?229						(1 << PSC_COEFF_PRECISION) : 0;230 231	/* normalize coef */232	for (phase = 0; phase < PSC_STORED_PHASES; phase++) {233		int sum = 0;234		s64 ll_temp;235 236		for (i = 0; i < PSC_NUM_TAPS; i++)237			sum += coef[phase][i];238		for (i = 0; i < PSC_NUM_TAPS; i++) {239			ll_temp = coef[phase][i];240			ll_temp <<= PSC_COEFF_PRECISION;241			ll_temp += sum >> 1;242			ll_temp /= sum;243			coef[phase][i] = (int)ll_temp;244		}245	}246}247 248static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps,249						int coef[][PSC_NUM_TAPS])250{251	int i, j;252 253	for (i = 0; i < PSC_STORED_PHASES; i++)254		for (j = 0; j < PSC_NUM_TAPS; j++)255			coef[i][j] = j == PSC_NUM_TAPS >> 1 ?256						(1 << PSC_COEFF_PRECISION) : 0;257}258 259/**260 * dcss_scaler_filter_design() - Compute filter coefficients using261 *				 Gaussian filter.262 * @src_length: length of input263 * @dst_length: length of output264 * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps265 * @coef: output coefficients266 */267static void dcss_scaler_filter_design(int src_length, int dst_length,268				      bool use_5_taps, bool phase0_identity,269				      int coef[][PSC_NUM_TAPS],270				      bool nn_interpolation)271{272	int fc_q;273 274	/* compute cutoff frequency */275	if (dst_length >= src_length)276		fc_q = div_q(1, PSC_NUM_PHASES);277	else278		fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES);279 280	if (nn_interpolation)281		dcss_scaler_nearest_neighbor_filter(use_5_taps, coef);282	else283		/* compute gaussian filter coefficients */284		dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef);285}286 287static void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs)288{289	struct dcss_scaler *scl = ch->scl;290 291	dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs);292}293 294static int dcss_scaler_ch_init_all(struct dcss_scaler *scl,295				   unsigned long scaler_base)296{297	struct dcss_scaler_ch *ch;298	int i;299 300	for (i = 0; i < 3; i++) {301		ch = &scl->ch[i];302 303		ch->base_ofs = scaler_base + i * 0x400;304 305		ch->base_reg = devm_ioremap(scl->dev, ch->base_ofs, SZ_4K);306		if (!ch->base_reg) {307			dev_err(scl->dev, "scaler: unable to remap ch base\n");308			return -ENOMEM;309		}310 311		ch->scl = scl;312	}313 314	return 0;315}316 317int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base)318{319	struct dcss_scaler *scaler;320 321	scaler = devm_kzalloc(dcss->dev, sizeof(*scaler), GFP_KERNEL);322	if (!scaler)323		return -ENOMEM;324 325	dcss->scaler = scaler;326	scaler->dev = dcss->dev;327	scaler->ctxld = dcss->ctxld;328	scaler->ctx_id = CTX_SB_HP;329 330	if (dcss_scaler_ch_init_all(scaler, scaler_base))331		return -ENOMEM;332 333	return 0;334}335 336void dcss_scaler_exit(struct dcss_scaler *scl)337{338	int ch_no;339 340	for (ch_no = 0; ch_no < 3; ch_no++) {341		struct dcss_scaler_ch *ch = &scl->ch[ch_no];342 343		dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL);344	}345}346 347void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en)348{349	struct dcss_scaler_ch *ch = &scl->ch[ch_num];350	u32 scaler_ctrl;351 352	scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0;353 354	if (en)355		dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL);356 357	if (ch->scaler_ctrl != scaler_ctrl)358		ch->scaler_ctrl_chgd = true;359 360	ch->scaler_ctrl = scaler_ctrl;361}362 363static void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en)364{365	ch->sdata_ctrl &= ~YUV_EN;366	ch->sdata_ctrl |= en ? YUV_EN : 0;367}368 369static void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en)370{371	ch->sdata_ctrl &= ~RTRAM_8LINES;372	ch->sdata_ctrl |= en ? RTRAM_8LINES : 0;373}374 375static void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth)376{377	u32 val;378 379	val = depth == 30 ? 2 : 0;380 381	dcss_scaler_write(ch,382			  ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) |383			  ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK),384			  DCSS_SCALER_BIT_DEPTH);385}386 387enum buffer_format {388	BUF_FMT_YUV420,389	BUF_FMT_YUV422,390	BUF_FMT_ARGB8888_YUV444,391};392 393enum chroma_location {394	PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0,395	PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1,396	PSC_LOC_HORZ_0_VERT_0 = 2,397	PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3,398	PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4,399	PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5400};401 402static void dcss_scaler_format_set(struct dcss_scaler_ch *ch,403				   enum buffer_format src_fmt,404				   enum buffer_format dst_fmt)405{406	dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT);407	dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT);408}409 410static void dcss_scaler_res_set(struct dcss_scaler_ch *ch,411				int src_xres, int src_yres,412				int dst_xres, int dst_yres,413				u32 pix_format, enum buffer_format dst_format)414{415	u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres;416	u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres;417	bool src_is_444 = true;418 419	lsrc_xres = src_xres;420	csrc_xres = src_xres;421	lsrc_yres = src_yres;422	csrc_yres = src_yres;423	ldst_xres = dst_xres;424	cdst_xres = dst_xres;425	ldst_yres = dst_yres;426	cdst_yres = dst_yres;427 428	if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY ||429	    pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) {430		csrc_xres >>= 1;431		src_is_444 = false;432	} else if (pix_format == DRM_FORMAT_NV12 ||433		   pix_format == DRM_FORMAT_NV21) {434		csrc_xres >>= 1;435		csrc_yres >>= 1;436		src_is_444 = false;437	}438 439	if (dst_format == BUF_FMT_YUV422)440		cdst_xres >>= 1;441 442	/* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */443	if (src_is_444 && dst_format == BUF_FMT_YUV422) {444		lsrc_yres--;445		csrc_yres--;446	}447 448	dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |449			       (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),450			  DCSS_SCALER_SRC_LUM_RES);451	dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |452			       (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),453			  DCSS_SCALER_SRC_CHR_RES);454	dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |455			       (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK),456			  DCSS_SCALER_DST_LUM_RES);457	dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |458			       (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK),459			  DCSS_SCALER_DST_CHR_RES);460}461 462#define downscale_fp(factor, fp_pos)		((factor) << (fp_pos))463#define upscale_fp(factor, fp_pos)		((1 << (fp_pos)) / (factor))464 465struct dcss_scaler_factors {466	int downscale;467	int upscale;468};469 470static const struct dcss_scaler_factors dcss_scaler_factors[] = {471	{3, 8}, {5, 8}, {5, 8},472};473 474static void dcss_scaler_fractions_set(struct dcss_scaler_ch *ch,475				      int src_xres, int src_yres,476				      int dst_xres, int dst_yres,477				      u32 src_format, u32 dst_format,478				      enum chroma_location src_chroma_loc)479{480	int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres;481	u32 l_vinc, l_hinc, c_vinc, c_hinc;482	u32 c_vstart, c_hstart;483 484	src_c_xres = src_xres;485	src_c_yres = src_yres;486	dst_c_xres = dst_xres;487	dst_c_yres = dst_yres;488 489	c_vstart = 0;490	c_hstart = 0;491 492	/* adjustments for source chroma location */493	if (src_format == BUF_FMT_YUV420) {494		/* vertical input chroma position adjustment */495		switch (src_chroma_loc) {496		case PSC_LOC_HORZ_0_VERT_1_OVER_4:497		case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:498			/*499			 * move chroma up to first luma line500			 * (1/4 chroma input line spacing)501			 */502			c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));503			break;504		case PSC_LOC_HORZ_0_VERT_1_OVER_2:505		case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:506			/*507			 * move chroma up to first luma line508			 * (1/2 chroma input line spacing)509			 */510			c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1));511			break;512		default:513			break;514		}515		/* horizontal input chroma position adjustment */516		switch (src_chroma_loc) {517		case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:518		case PSC_LOC_HORZ_1_OVER_4_VERT_0:519		case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:520			/* move chroma left 1/4 chroma input sample spacing */521			c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));522			break;523		default:524			break;525		}526	}527 528	/* adjustments to chroma resolution */529	if (src_format == BUF_FMT_YUV420) {530		src_c_xres >>= 1;531		src_c_yres >>= 1;532	} else if (src_format == BUF_FMT_YUV422) {533		src_c_xres >>= 1;534	}535 536	if (dst_format == BUF_FMT_YUV422)537		dst_c_xres >>= 1;538 539	l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres;540	c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres;541	l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres;542	c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres;543 544	/* save chroma start phase */545	ch->c_vstart = c_vstart;546	ch->c_hstart = c_hstart;547 548	dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START);549	dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC);550 551	dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START);552	dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC);553 554	dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START);555	dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC);556 557	dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START);558	dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC);559}560 561int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num,562				   int *min, int *max)563{564	*min = upscale_fp(dcss_scaler_factors[ch_num].upscale, 16);565	*max = downscale_fp(dcss_scaler_factors[ch_num].downscale, 16);566 567	return 0;568}569 570static void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch,571					   int base_addr,572					   int coef[][PSC_NUM_TAPS])573{574	int i, phase;575 576	for (i = 0; i < PSC_STORED_PHASES; i++) {577		dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 |578				       (coef[i][2] & 0xfff) << 4  |579				       (coef[i][3] & 0xf00) >> 8),580				  base_addr + i * sizeof(u32));581		dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 |582				       (coef[i][4] & 0xfff) << 8  |583				       (coef[i][5] & 0xff0) >> 4),584				  base_addr + 0x40 + i * sizeof(u32));585		dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24),586				  base_addr + 0x80 + i * sizeof(u32));587	}588 589	/* reverse both phase and tap orderings */590	for (phase = (PSC_NUM_PHASES >> 1) - 1;591			i < PSC_NUM_PHASES; i++, phase--) {592		dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 |593				       (coef[phase][4] & 0xfff) << 4  |594				       (coef[phase][3] & 0xf00) >> 8),595				  base_addr + i * sizeof(u32));596		dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 |597				       (coef[phase][2] & 0xfff) << 8  |598				       (coef[phase][1] & 0xff0) >> 4),599				  base_addr + 0x40 + i * sizeof(u32));600		dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24),601				  base_addr + 0x80 + i * sizeof(u32));602	}603}604 605static void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch,606					   int base_addr,607					   int coef[][PSC_NUM_TAPS])608{609	int i, phase;610 611	for (i = 0; i < PSC_STORED_PHASES; i++) {612		dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 |613				       (coef[i][1] & 0xfff) << 4  |614				       (coef[i][2] & 0xf00) >> 8),615				  base_addr + i * sizeof(u32));616		dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 |617				       (coef[i][3] & 0xfff) << 8  |618				       (coef[i][4] & 0xff0) >> 4),619				  base_addr + 0x40 + i * sizeof(u32));620		dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 |621				       (coef[i][5] & 0xfff) << 12 |622				       (coef[i][6] & 0xfff)),623				  base_addr + 0x80 + i * sizeof(u32));624	}625 626	/* reverse both phase and tap orderings */627	for (phase = (PSC_NUM_PHASES >> 1) - 1;628			i < PSC_NUM_PHASES; i++, phase--) {629		dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 |630				       (coef[phase][5] & 0xfff) << 4  |631				       (coef[phase][4] & 0xf00) >> 8),632				  base_addr + i * sizeof(u32));633		dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 |634				       (coef[phase][3] & 0xfff) << 8  |635				       (coef[phase][2] & 0xff0) >> 4),636				  base_addr + 0x40 + i * sizeof(u32));637		dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 |638				       (coef[phase][1] & 0xfff) << 12 |639				       (coef[phase][0] & 0xfff)),640				  base_addr + 0x80 + i * sizeof(u32));641	}642}643 644static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch,645				     enum buffer_format src_format,646				     enum buffer_format dst_format,647				     bool use_5_taps,648				     int src_xres, int src_yres, int dst_xres,649				     int dst_yres)650{651	int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];652	bool program_5_taps = use_5_taps ||653			      (dst_format == BUF_FMT_YUV422 &&654			       src_format == BUF_FMT_ARGB8888_YUV444);655 656	/* horizontal luma */657	dcss_scaler_filter_design(src_xres, dst_xres, false,658				  src_xres == dst_xres, coef,659				  ch->use_nn_interpolation);660	dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);661 662	/* vertical luma */663	dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,664				  src_yres == dst_yres, coef,665				  ch->use_nn_interpolation);666 667	if (program_5_taps)668		dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);669	else670		dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);671 672	/* adjust chroma resolution */673	if (src_format != BUF_FMT_ARGB8888_YUV444)674		src_xres >>= 1;675	if (src_format == BUF_FMT_YUV420)676		src_yres >>= 1;677	if (dst_format != BUF_FMT_ARGB8888_YUV444)678		dst_xres >>= 1;679	if (dst_format == BUF_FMT_YUV420) /* should not happen */680		dst_yres >>= 1;681 682	/* horizontal chroma */683	dcss_scaler_filter_design(src_xres, dst_xres, false,684				  (src_xres == dst_xres) && (ch->c_hstart == 0),685				  coef, ch->use_nn_interpolation);686 687	dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef);688 689	/* vertical chroma */690	dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,691				  (src_yres == dst_yres) && (ch->c_vstart == 0),692				  coef, ch->use_nn_interpolation);693	if (program_5_taps)694		dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);695	else696		dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);697}698 699static void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch,700				     int src_xres, int src_yres, int dst_xres,701				     int dst_yres)702{703	int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];704 705	/* horizontal RGB */706	dcss_scaler_filter_design(src_xres, dst_xres, false,707				  src_xres == dst_xres, coef,708				  ch->use_nn_interpolation);709	dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);710 711	/* vertical RGB */712	dcss_scaler_filter_design(src_yres, dst_yres, false,713				  src_yres == dst_yres, coef,714				  ch->use_nn_interpolation);715	dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);716}717 718static void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch,719					const struct drm_format_info *format)720{721	u32 a2r10g10b10_format;722 723	if (format->is_yuv)724		return;725 726	ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK;727 728	if (format->depth != 30)729		return;730 731	switch (format->format) {732	case DRM_FORMAT_ARGB2101010:733	case DRM_FORMAT_XRGB2101010:734		a2r10g10b10_format = 0;735		break;736 737	case DRM_FORMAT_ABGR2101010:738	case DRM_FORMAT_XBGR2101010:739		a2r10g10b10_format = 5;740		break;741 742	case DRM_FORMAT_RGBA1010102:743	case DRM_FORMAT_RGBX1010102:744		a2r10g10b10_format = 6;745		break;746 747	case DRM_FORMAT_BGRA1010102:748	case DRM_FORMAT_BGRX1010102:749		a2r10g10b10_format = 11;750		break;751 752	default:753		a2r10g10b10_format = 0;754		break;755	}756 757	ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS;758}759 760void dcss_scaler_set_filter(struct dcss_scaler *scl, int ch_num,761			    enum drm_scaling_filter scaling_filter)762{763	struct dcss_scaler_ch *ch = &scl->ch[ch_num];764 765	ch->use_nn_interpolation = scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR;766}767 768void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,769		       const struct drm_format_info *format,770		       int src_xres, int src_yres, int dst_xres, int dst_yres,771		       u32 vrefresh_hz)772{773	struct dcss_scaler_ch *ch = &scl->ch[ch_num];774	unsigned int pixel_depth = 0;775	bool rtr_8line_en = false;776	bool use_5_taps = false;777	enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444;778	enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444;779	u32 pix_format = format->format;780 781	if (format->is_yuv) {782		dcss_scaler_yuv_enable(ch, true);783 784		if (pix_format == DRM_FORMAT_NV12 ||785		    pix_format == DRM_FORMAT_NV21) {786			rtr_8line_en = true;787			src_format = BUF_FMT_YUV420;788		} else if (pix_format == DRM_FORMAT_UYVY ||789			   pix_format == DRM_FORMAT_VYUY ||790			   pix_format == DRM_FORMAT_YUYV ||791			   pix_format == DRM_FORMAT_YVYU) {792			src_format = BUF_FMT_YUV422;793		}794 795		use_5_taps = !rtr_8line_en;796	} else {797		dcss_scaler_yuv_enable(ch, false);798 799		pixel_depth = format->depth;800	}801 802	dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres,803				  dst_yres, src_format, dst_format,804				  PSC_LOC_HORZ_0_VERT_1_OVER_4);805 806	if (format->is_yuv)807		dcss_scaler_yuv_coef_set(ch, src_format, dst_format,808					 use_5_taps, src_xres, src_yres,809					 dst_xres, dst_yres);810	else811		dcss_scaler_rgb_coef_set(ch, src_xres, src_yres,812					 dst_xres, dst_yres);813 814	dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en);815	dcss_scaler_bit_depth_set(ch, pixel_depth);816	dcss_scaler_set_rgb10_order(ch, format);817	dcss_scaler_format_set(ch, src_format, dst_format);818	dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres,819			    pix_format, dst_format);820}821 822/* This function will be called from interrupt context. */823void dcss_scaler_write_sclctrl(struct dcss_scaler *scl)824{825	int chnum;826 827	dcss_ctxld_assert_locked(scl->ctxld);828 829	for (chnum = 0; chnum < 3; chnum++) {830		struct dcss_scaler_ch *ch = &scl->ch[chnum];831 832		if (ch->scaler_ctrl_chgd) {833			dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id,834						 ch->scaler_ctrl,835						 ch->base_ofs +836						 DCSS_SCALER_CTRL);837			ch->scaler_ctrl_chgd = false;838		}839	}840}841