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