Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright 2019 NXP.
0004  *
0005  * Scaling algorithms were contributed by Dzung Hoang <dzung.hoang@nxp.com>
0006  */
0007 
0008 #include <linux/device.h>
0009 #include <linux/slab.h>
0010 
0011 #include "dcss-dev.h"
0012 
0013 #define DCSS_SCALER_CTRL            0x00
0014 #define   SCALER_EN             BIT(0)
0015 #define   REPEAT_EN             BIT(4)
0016 #define   SCALE2MEM_EN              BIT(8)
0017 #define   MEM2OFIFO_EN              BIT(12)
0018 #define DCSS_SCALER_OFIFO_CTRL          0x04
0019 #define   OFIFO_LOW_THRES_POS           0
0020 #define   OFIFO_LOW_THRES_MASK          GENMASK(9, 0)
0021 #define   OFIFO_HIGH_THRES_POS          16
0022 #define   OFIFO_HIGH_THRES_MASK         GENMASK(25, 16)
0023 #define   UNDERRUN_DETECT_CLR           BIT(26)
0024 #define   LOW_THRES_DETECT_CLR          BIT(27)
0025 #define   HIGH_THRES_DETECT_CLR         BIT(28)
0026 #define   UNDERRUN_DETECT_EN            BIT(29)
0027 #define   LOW_THRES_DETECT_EN           BIT(30)
0028 #define   HIGH_THRES_DETECT_EN          BIT(31)
0029 #define DCSS_SCALER_SDATA_CTRL          0x08
0030 #define   YUV_EN                BIT(0)
0031 #define   RTRAM_8LINES              BIT(1)
0032 #define   Y_UV_BYTE_SWAP            BIT(4)
0033 #define   A2R10G10B10_FORMAT_POS        8
0034 #define   A2R10G10B10_FORMAT_MASK       GENMASK(11, 8)
0035 #define DCSS_SCALER_BIT_DEPTH           0x0C
0036 #define   LUM_BIT_DEPTH_POS         0
0037 #define   LUM_BIT_DEPTH_MASK            GENMASK(1, 0)
0038 #define   CHR_BIT_DEPTH_POS         4
0039 #define   CHR_BIT_DEPTH_MASK            GENMASK(5, 4)
0040 #define DCSS_SCALER_SRC_FORMAT          0x10
0041 #define DCSS_SCALER_DST_FORMAT          0x14
0042 #define   FORMAT_MASK               GENMASK(1, 0)
0043 #define DCSS_SCALER_SRC_LUM_RES         0x18
0044 #define DCSS_SCALER_SRC_CHR_RES         0x1C
0045 #define DCSS_SCALER_DST_LUM_RES         0x20
0046 #define DCSS_SCALER_DST_CHR_RES         0x24
0047 #define   WIDTH_POS             0
0048 #define   WIDTH_MASK                GENMASK(11, 0)
0049 #define   HEIGHT_POS                16
0050 #define   HEIGHT_MASK               GENMASK(27, 16)
0051 #define DCSS_SCALER_V_LUM_START         0x48
0052 #define   V_START_MASK              GENMASK(15, 0)
0053 #define DCSS_SCALER_V_LUM_INC           0x4C
0054 #define   V_INC_MASK                GENMASK(15, 0)
0055 #define DCSS_SCALER_H_LUM_START         0x50
0056 #define   H_START_MASK              GENMASK(18, 0)
0057 #define DCSS_SCALER_H_LUM_INC           0x54
0058 #define   H_INC_MASK                GENMASK(15, 0)
0059 #define DCSS_SCALER_V_CHR_START         0x58
0060 #define DCSS_SCALER_V_CHR_INC           0x5C
0061 #define DCSS_SCALER_H_CHR_START         0x60
0062 #define DCSS_SCALER_H_CHR_INC           0x64
0063 #define DCSS_SCALER_COEF_VLUM           0x80
0064 #define DCSS_SCALER_COEF_HLUM           0x140
0065 #define DCSS_SCALER_COEF_VCHR           0x200
0066 #define DCSS_SCALER_COEF_HCHR           0x300
0067 
0068 struct dcss_scaler_ch {
0069     void __iomem *base_reg;
0070     u32 base_ofs;
0071     struct dcss_scaler *scl;
0072 
0073     u32 sdata_ctrl;
0074     u32 scaler_ctrl;
0075 
0076     bool scaler_ctrl_chgd;
0077 
0078     u32 c_vstart;
0079     u32 c_hstart;
0080 
0081     bool use_nn_interpolation;
0082 };
0083 
0084 struct dcss_scaler {
0085     struct device *dev;
0086 
0087     struct dcss_ctxld *ctxld;
0088     u32 ctx_id;
0089 
0090     struct dcss_scaler_ch ch[3];
0091 };
0092 
0093 /* scaler coefficients generator */
0094 #define PSC_FRAC_BITS 30
0095 #define PSC_FRAC_SCALE BIT(PSC_FRAC_BITS)
0096 #define PSC_BITS_FOR_PHASE 4
0097 #define PSC_NUM_PHASES 16
0098 #define PSC_STORED_PHASES (PSC_NUM_PHASES / 2 + 1)
0099 #define PSC_NUM_TAPS 7
0100 #define PSC_NUM_TAPS_RGBA 5
0101 #define PSC_COEFF_PRECISION 10
0102 #define PSC_PHASE_FRACTION_BITS 13
0103 #define PSC_PHASE_MASK (PSC_NUM_PHASES - 1)
0104 #define PSC_Q_FRACTION 19
0105 #define PSC_Q_ROUND_OFFSET (1 << (PSC_Q_FRACTION - 1))
0106 
0107 /**
0108  * mult_q() - Performs fixed-point multiplication.
0109  * @A: multiplier
0110  * @B: multiplicand
0111  */
0112 static int mult_q(int A, int B)
0113 {
0114     int result;
0115     s64 temp;
0116 
0117     temp = (int64_t)A * (int64_t)B;
0118     temp += PSC_Q_ROUND_OFFSET;
0119     result = (int)(temp >> PSC_Q_FRACTION);
0120     return result;
0121 }
0122 
0123 /**
0124  * div_q() - Performs fixed-point division.
0125  * @A: dividend
0126  * @B: divisor
0127  */
0128 static int div_q(int A, int B)
0129 {
0130     int result;
0131     s64 temp;
0132 
0133     temp = (int64_t)A << PSC_Q_FRACTION;
0134     if ((temp >= 0 && B >= 0) || (temp < 0 && B < 0))
0135         temp += B / 2;
0136     else
0137         temp -= B / 2;
0138 
0139     result = (int)(temp / B);
0140     return result;
0141 }
0142 
0143 /**
0144  * exp_approx_q() - Compute approximation to exp(x) function using Taylor
0145  *          series.
0146  * @x: fixed-point argument of exp function
0147  */
0148 static int exp_approx_q(int x)
0149 {
0150     int sum = 1 << PSC_Q_FRACTION;
0151     int term = 1 << PSC_Q_FRACTION;
0152 
0153     term = mult_q(term, div_q(x, 1 << PSC_Q_FRACTION));
0154     sum += term;
0155     term = mult_q(term, div_q(x, 2 << PSC_Q_FRACTION));
0156     sum += term;
0157     term = mult_q(term, div_q(x, 3 << PSC_Q_FRACTION));
0158     sum += term;
0159     term = mult_q(term, div_q(x, 4 << PSC_Q_FRACTION));
0160     sum += term;
0161 
0162     return sum;
0163 }
0164 
0165 /**
0166  * dcss_scaler_gaussian_filter() - Generate gaussian prototype filter.
0167  * @fc_q: fixed-point cutoff frequency normalized to range [0, 1]
0168  * @use_5_taps: indicates whether to use 5 taps or 7 taps
0169  * @coef: output filter coefficients
0170  */
0171 static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
0172                     bool phase0_identity,
0173                     int coef[][PSC_NUM_TAPS])
0174 {
0175     int sigma_q, g0_q, g1_q, g2_q;
0176     int tap_cnt1, tap_cnt2, tap_idx, phase_cnt;
0177     int mid;
0178     int phase;
0179     int i;
0180     int taps;
0181 
0182     if (use_5_taps)
0183         for (phase = 0; phase < PSC_STORED_PHASES; phase++) {
0184             coef[phase][0] = 0;
0185             coef[phase][PSC_NUM_TAPS - 1] = 0;
0186         }
0187 
0188     /* seed coefficient scanner */
0189     taps = use_5_taps ? PSC_NUM_TAPS_RGBA : PSC_NUM_TAPS;
0190     mid = (PSC_NUM_PHASES * taps) / 2 - 1;
0191     phase_cnt = (PSC_NUM_PHASES * (PSC_NUM_TAPS + 1)) / 2;
0192     tap_cnt1 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;
0193     tap_cnt2 = (PSC_NUM_PHASES * PSC_NUM_TAPS) / 2;
0194 
0195     /* seed gaussian filter generator */
0196     sigma_q = div_q(PSC_Q_ROUND_OFFSET, fc_q);
0197     g0_q = 1 << PSC_Q_FRACTION;
0198     g1_q = exp_approx_q(div_q(-PSC_Q_ROUND_OFFSET,
0199                   mult_q(sigma_q, sigma_q)));
0200     g2_q = mult_q(g1_q, g1_q);
0201     coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = g0_q;
0202 
0203     for (i = 0; i < mid; i++) {
0204         phase_cnt++;
0205         tap_cnt1--;
0206         tap_cnt2++;
0207 
0208         g0_q = mult_q(g0_q, g1_q);
0209         g1_q = mult_q(g1_q, g2_q);
0210 
0211         if ((phase_cnt & PSC_PHASE_MASK) <= 8) {
0212             tap_idx = tap_cnt1 >> PSC_BITS_FOR_PHASE;
0213             coef[phase_cnt & PSC_PHASE_MASK][tap_idx] = g0_q;
0214         }
0215         if (((-phase_cnt) & PSC_PHASE_MASK) <= 8) {
0216             tap_idx = tap_cnt2 >> PSC_BITS_FOR_PHASE;
0217             coef[(-phase_cnt) & PSC_PHASE_MASK][tap_idx] = g0_q;
0218         }
0219     }
0220 
0221     phase_cnt++;
0222     tap_cnt1--;
0223     coef[phase_cnt & PSC_PHASE_MASK][tap_cnt1 >> PSC_BITS_FOR_PHASE] = 0;
0224 
0225     /* override phase 0 with identity filter if specified */
0226     if (phase0_identity)
0227         for (i = 0; i < PSC_NUM_TAPS; i++)
0228             coef[0][i] = i == (PSC_NUM_TAPS >> 1) ?
0229                         (1 << PSC_COEFF_PRECISION) : 0;
0230 
0231     /* normalize coef */
0232     for (phase = 0; phase < PSC_STORED_PHASES; phase++) {
0233         int sum = 0;
0234         s64 ll_temp;
0235 
0236         for (i = 0; i < PSC_NUM_TAPS; i++)
0237             sum += coef[phase][i];
0238         for (i = 0; i < PSC_NUM_TAPS; i++) {
0239             ll_temp = coef[phase][i];
0240             ll_temp <<= PSC_COEFF_PRECISION;
0241             ll_temp += sum >> 1;
0242             ll_temp /= sum;
0243             coef[phase][i] = (int)ll_temp;
0244         }
0245     }
0246 }
0247 
0248 static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps,
0249                         int coef[][PSC_NUM_TAPS])
0250 {
0251     int i, j;
0252 
0253     for (i = 0; i < PSC_STORED_PHASES; i++)
0254         for (j = 0; j < PSC_NUM_TAPS; j++)
0255             coef[i][j] = j == PSC_NUM_TAPS >> 1 ?
0256                         (1 << PSC_COEFF_PRECISION) : 0;
0257 }
0258 
0259 /**
0260  * dcss_scaler_filter_design() - Compute filter coefficients using
0261  *               Gaussian filter.
0262  * @src_length: length of input
0263  * @dst_length: length of output
0264  * @use_5_taps: 0 for 7 taps per phase, 1 for 5 taps
0265  * @coef: output coefficients
0266  */
0267 static void dcss_scaler_filter_design(int src_length, int dst_length,
0268                       bool use_5_taps, bool phase0_identity,
0269                       int coef[][PSC_NUM_TAPS],
0270                       bool nn_interpolation)
0271 {
0272     int fc_q;
0273 
0274     /* compute cutoff frequency */
0275     if (dst_length >= src_length)
0276         fc_q = div_q(1, PSC_NUM_PHASES);
0277     else
0278         fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES);
0279 
0280     if (nn_interpolation)
0281         dcss_scaler_nearest_neighbor_filter(use_5_taps, coef);
0282     else
0283         /* compute gaussian filter coefficients */
0284         dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef);
0285 }
0286 
0287 static void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs)
0288 {
0289     struct dcss_scaler *scl = ch->scl;
0290 
0291     dcss_ctxld_write(scl->ctxld, scl->ctx_id, val, ch->base_ofs + ofs);
0292 }
0293 
0294 static int dcss_scaler_ch_init_all(struct dcss_scaler *scl,
0295                    unsigned long scaler_base)
0296 {
0297     struct dcss_scaler_ch *ch;
0298     int i;
0299 
0300     for (i = 0; i < 3; i++) {
0301         ch = &scl->ch[i];
0302 
0303         ch->base_ofs = scaler_base + i * 0x400;
0304 
0305         ch->base_reg = ioremap(ch->base_ofs, SZ_4K);
0306         if (!ch->base_reg) {
0307             dev_err(scl->dev, "scaler: unable to remap ch base\n");
0308             return -ENOMEM;
0309         }
0310 
0311         ch->scl = scl;
0312     }
0313 
0314     return 0;
0315 }
0316 
0317 int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base)
0318 {
0319     struct dcss_scaler *scaler;
0320 
0321     scaler = kzalloc(sizeof(*scaler), GFP_KERNEL);
0322     if (!scaler)
0323         return -ENOMEM;
0324 
0325     dcss->scaler = scaler;
0326     scaler->dev = dcss->dev;
0327     scaler->ctxld = dcss->ctxld;
0328     scaler->ctx_id = CTX_SB_HP;
0329 
0330     if (dcss_scaler_ch_init_all(scaler, scaler_base)) {
0331         int i;
0332 
0333         for (i = 0; i < 3; i++) {
0334             if (scaler->ch[i].base_reg)
0335                 iounmap(scaler->ch[i].base_reg);
0336         }
0337 
0338         kfree(scaler);
0339 
0340         return -ENOMEM;
0341     }
0342 
0343     return 0;
0344 }
0345 
0346 void dcss_scaler_exit(struct dcss_scaler *scl)
0347 {
0348     int ch_no;
0349 
0350     for (ch_no = 0; ch_no < 3; ch_no++) {
0351         struct dcss_scaler_ch *ch = &scl->ch[ch_no];
0352 
0353         dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL);
0354 
0355         if (ch->base_reg)
0356             iounmap(ch->base_reg);
0357     }
0358 
0359     kfree(scl);
0360 }
0361 
0362 void dcss_scaler_ch_enable(struct dcss_scaler *scl, int ch_num, bool en)
0363 {
0364     struct dcss_scaler_ch *ch = &scl->ch[ch_num];
0365     u32 scaler_ctrl;
0366 
0367     scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0;
0368 
0369     if (en)
0370         dcss_scaler_write(ch, ch->sdata_ctrl, DCSS_SCALER_SDATA_CTRL);
0371 
0372     if (ch->scaler_ctrl != scaler_ctrl)
0373         ch->scaler_ctrl_chgd = true;
0374 
0375     ch->scaler_ctrl = scaler_ctrl;
0376 }
0377 
0378 static void dcss_scaler_yuv_enable(struct dcss_scaler_ch *ch, bool en)
0379 {
0380     ch->sdata_ctrl &= ~YUV_EN;
0381     ch->sdata_ctrl |= en ? YUV_EN : 0;
0382 }
0383 
0384 static void dcss_scaler_rtr_8lines_enable(struct dcss_scaler_ch *ch, bool en)
0385 {
0386     ch->sdata_ctrl &= ~RTRAM_8LINES;
0387     ch->sdata_ctrl |= en ? RTRAM_8LINES : 0;
0388 }
0389 
0390 static void dcss_scaler_bit_depth_set(struct dcss_scaler_ch *ch, int depth)
0391 {
0392     u32 val;
0393 
0394     val = depth == 30 ? 2 : 0;
0395 
0396     dcss_scaler_write(ch,
0397               ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) |
0398               ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK),
0399               DCSS_SCALER_BIT_DEPTH);
0400 }
0401 
0402 enum buffer_format {
0403     BUF_FMT_YUV420,
0404     BUF_FMT_YUV422,
0405     BUF_FMT_ARGB8888_YUV444,
0406 };
0407 
0408 enum chroma_location {
0409     PSC_LOC_HORZ_0_VERT_1_OVER_4 = 0,
0410     PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4 = 1,
0411     PSC_LOC_HORZ_0_VERT_0 = 2,
0412     PSC_LOC_HORZ_1_OVER_4_VERT_0 = 3,
0413     PSC_LOC_HORZ_0_VERT_1_OVER_2 = 4,
0414     PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2 = 5
0415 };
0416 
0417 static void dcss_scaler_format_set(struct dcss_scaler_ch *ch,
0418                    enum buffer_format src_fmt,
0419                    enum buffer_format dst_fmt)
0420 {
0421     dcss_scaler_write(ch, src_fmt, DCSS_SCALER_SRC_FORMAT);
0422     dcss_scaler_write(ch, dst_fmt, DCSS_SCALER_DST_FORMAT);
0423 }
0424 
0425 static void dcss_scaler_res_set(struct dcss_scaler_ch *ch,
0426                 int src_xres, int src_yres,
0427                 int dst_xres, int dst_yres,
0428                 u32 pix_format, enum buffer_format dst_format)
0429 {
0430     u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres;
0431     u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres;
0432     bool src_is_444 = true;
0433 
0434     lsrc_xres = src_xres;
0435     csrc_xres = src_xres;
0436     lsrc_yres = src_yres;
0437     csrc_yres = src_yres;
0438     ldst_xres = dst_xres;
0439     cdst_xres = dst_xres;
0440     ldst_yres = dst_yres;
0441     cdst_yres = dst_yres;
0442 
0443     if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY ||
0444         pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) {
0445         csrc_xres >>= 1;
0446         src_is_444 = false;
0447     } else if (pix_format == DRM_FORMAT_NV12 ||
0448            pix_format == DRM_FORMAT_NV21) {
0449         csrc_xres >>= 1;
0450         csrc_yres >>= 1;
0451         src_is_444 = false;
0452     }
0453 
0454     if (dst_format == BUF_FMT_YUV422)
0455         cdst_xres >>= 1;
0456 
0457     /* for 4:4:4 to 4:2:2 conversion, source height should be 1 less */
0458     if (src_is_444 && dst_format == BUF_FMT_YUV422) {
0459         lsrc_yres--;
0460         csrc_yres--;
0461     }
0462 
0463     dcss_scaler_write(ch, (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
0464                    (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),
0465               DCSS_SCALER_SRC_LUM_RES);
0466     dcss_scaler_write(ch, (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
0467                    (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK),
0468               DCSS_SCALER_SRC_CHR_RES);
0469     dcss_scaler_write(ch, (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
0470                    (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK),
0471               DCSS_SCALER_DST_LUM_RES);
0472     dcss_scaler_write(ch, (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) |
0473                    (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK),
0474               DCSS_SCALER_DST_CHR_RES);
0475 }
0476 
0477 #define downscale_fp(factor, fp_pos)        ((factor) << (fp_pos))
0478 #define upscale_fp(factor, fp_pos)      ((1 << (fp_pos)) / (factor))
0479 
0480 struct dcss_scaler_factors {
0481     int downscale;
0482     int upscale;
0483 };
0484 
0485 static const struct dcss_scaler_factors dcss_scaler_factors[] = {
0486     {3, 8}, {5, 8}, {5, 8},
0487 };
0488 
0489 static void dcss_scaler_fractions_set(struct dcss_scaler_ch *ch,
0490                       int src_xres, int src_yres,
0491                       int dst_xres, int dst_yres,
0492                       u32 src_format, u32 dst_format,
0493                       enum chroma_location src_chroma_loc)
0494 {
0495     int src_c_xres, src_c_yres, dst_c_xres, dst_c_yres;
0496     u32 l_vinc, l_hinc, c_vinc, c_hinc;
0497     u32 c_vstart, c_hstart;
0498 
0499     src_c_xres = src_xres;
0500     src_c_yres = src_yres;
0501     dst_c_xres = dst_xres;
0502     dst_c_yres = dst_yres;
0503 
0504     c_vstart = 0;
0505     c_hstart = 0;
0506 
0507     /* adjustments for source chroma location */
0508     if (src_format == BUF_FMT_YUV420) {
0509         /* vertical input chroma position adjustment */
0510         switch (src_chroma_loc) {
0511         case PSC_LOC_HORZ_0_VERT_1_OVER_4:
0512         case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:
0513             /*
0514              * move chroma up to first luma line
0515              * (1/4 chroma input line spacing)
0516              */
0517             c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));
0518             break;
0519         case PSC_LOC_HORZ_0_VERT_1_OVER_2:
0520         case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:
0521             /*
0522              * move chroma up to first luma line
0523              * (1/2 chroma input line spacing)
0524              */
0525             c_vstart -= (1 << (PSC_PHASE_FRACTION_BITS - 1));
0526             break;
0527         default:
0528             break;
0529         }
0530         /* horizontal input chroma position adjustment */
0531         switch (src_chroma_loc) {
0532         case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_4:
0533         case PSC_LOC_HORZ_1_OVER_4_VERT_0:
0534         case PSC_LOC_HORZ_1_OVER_4_VERT_1_OVER_2:
0535             /* move chroma left 1/4 chroma input sample spacing */
0536             c_hstart -= (1 << (PSC_PHASE_FRACTION_BITS - 2));
0537             break;
0538         default:
0539             break;
0540         }
0541     }
0542 
0543     /* adjustments to chroma resolution */
0544     if (src_format == BUF_FMT_YUV420) {
0545         src_c_xres >>= 1;
0546         src_c_yres >>= 1;
0547     } else if (src_format == BUF_FMT_YUV422) {
0548         src_c_xres >>= 1;
0549     }
0550 
0551     if (dst_format == BUF_FMT_YUV422)
0552         dst_c_xres >>= 1;
0553 
0554     l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres;
0555     c_vinc = ((src_c_yres << 13) + (dst_c_yres >> 1)) / dst_c_yres;
0556     l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres;
0557     c_hinc = ((src_c_xres << 13) + (dst_c_xres >> 1)) / dst_c_xres;
0558 
0559     /* save chroma start phase */
0560     ch->c_vstart = c_vstart;
0561     ch->c_hstart = c_hstart;
0562 
0563     dcss_scaler_write(ch, 0, DCSS_SCALER_V_LUM_START);
0564     dcss_scaler_write(ch, l_vinc, DCSS_SCALER_V_LUM_INC);
0565 
0566     dcss_scaler_write(ch, 0, DCSS_SCALER_H_LUM_START);
0567     dcss_scaler_write(ch, l_hinc, DCSS_SCALER_H_LUM_INC);
0568 
0569     dcss_scaler_write(ch, c_vstart, DCSS_SCALER_V_CHR_START);
0570     dcss_scaler_write(ch, c_vinc, DCSS_SCALER_V_CHR_INC);
0571 
0572     dcss_scaler_write(ch, c_hstart, DCSS_SCALER_H_CHR_START);
0573     dcss_scaler_write(ch, c_hinc, DCSS_SCALER_H_CHR_INC);
0574 }
0575 
0576 int dcss_scaler_get_min_max_ratios(struct dcss_scaler *scl, int ch_num,
0577                    int *min, int *max)
0578 {
0579     *min = upscale_fp(dcss_scaler_factors[ch_num].upscale, 16);
0580     *max = downscale_fp(dcss_scaler_factors[ch_num].downscale, 16);
0581 
0582     return 0;
0583 }
0584 
0585 static void dcss_scaler_program_5_coef_set(struct dcss_scaler_ch *ch,
0586                        int base_addr,
0587                        int coef[][PSC_NUM_TAPS])
0588 {
0589     int i, phase;
0590 
0591     for (i = 0; i < PSC_STORED_PHASES; i++) {
0592         dcss_scaler_write(ch, ((coef[i][1] & 0xfff) << 16 |
0593                        (coef[i][2] & 0xfff) << 4  |
0594                        (coef[i][3] & 0xf00) >> 8),
0595                   base_addr + i * sizeof(u32));
0596         dcss_scaler_write(ch, ((coef[i][3] & 0x0ff) << 20 |
0597                        (coef[i][4] & 0xfff) << 8  |
0598                        (coef[i][5] & 0xff0) >> 4),
0599                   base_addr + 0x40 + i * sizeof(u32));
0600         dcss_scaler_write(ch, ((coef[i][5] & 0x00f) << 24),
0601                   base_addr + 0x80 + i * sizeof(u32));
0602     }
0603 
0604     /* reverse both phase and tap orderings */
0605     for (phase = (PSC_NUM_PHASES >> 1) - 1;
0606             i < PSC_NUM_PHASES; i++, phase--) {
0607         dcss_scaler_write(ch, ((coef[phase][5] & 0xfff) << 16 |
0608                        (coef[phase][4] & 0xfff) << 4  |
0609                        (coef[phase][3] & 0xf00) >> 8),
0610                   base_addr + i * sizeof(u32));
0611         dcss_scaler_write(ch, ((coef[phase][3] & 0x0ff) << 20 |
0612                        (coef[phase][2] & 0xfff) << 8  |
0613                        (coef[phase][1] & 0xff0) >> 4),
0614                   base_addr + 0x40 + i * sizeof(u32));
0615         dcss_scaler_write(ch, ((coef[phase][1] & 0x00f) << 24),
0616                   base_addr + 0x80 + i * sizeof(u32));
0617     }
0618 }
0619 
0620 static void dcss_scaler_program_7_coef_set(struct dcss_scaler_ch *ch,
0621                        int base_addr,
0622                        int coef[][PSC_NUM_TAPS])
0623 {
0624     int i, phase;
0625 
0626     for (i = 0; i < PSC_STORED_PHASES; i++) {
0627         dcss_scaler_write(ch, ((coef[i][0] & 0xfff) << 16 |
0628                        (coef[i][1] & 0xfff) << 4  |
0629                        (coef[i][2] & 0xf00) >> 8),
0630                   base_addr + i * sizeof(u32));
0631         dcss_scaler_write(ch, ((coef[i][2] & 0x0ff) << 20 |
0632                        (coef[i][3] & 0xfff) << 8  |
0633                        (coef[i][4] & 0xff0) >> 4),
0634                   base_addr + 0x40 + i * sizeof(u32));
0635         dcss_scaler_write(ch, ((coef[i][4] & 0x00f) << 24 |
0636                        (coef[i][5] & 0xfff) << 12 |
0637                        (coef[i][6] & 0xfff)),
0638                   base_addr + 0x80 + i * sizeof(u32));
0639     }
0640 
0641     /* reverse both phase and tap orderings */
0642     for (phase = (PSC_NUM_PHASES >> 1) - 1;
0643             i < PSC_NUM_PHASES; i++, phase--) {
0644         dcss_scaler_write(ch, ((coef[phase][6] & 0xfff) << 16 |
0645                        (coef[phase][5] & 0xfff) << 4  |
0646                        (coef[phase][4] & 0xf00) >> 8),
0647                   base_addr + i * sizeof(u32));
0648         dcss_scaler_write(ch, ((coef[phase][4] & 0x0ff) << 20 |
0649                        (coef[phase][3] & 0xfff) << 8  |
0650                        (coef[phase][2] & 0xff0) >> 4),
0651                   base_addr + 0x40 + i * sizeof(u32));
0652         dcss_scaler_write(ch, ((coef[phase][2] & 0x00f) << 24 |
0653                        (coef[phase][1] & 0xfff) << 12 |
0654                        (coef[phase][0] & 0xfff)),
0655                   base_addr + 0x80 + i * sizeof(u32));
0656     }
0657 }
0658 
0659 static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch,
0660                      enum buffer_format src_format,
0661                      enum buffer_format dst_format,
0662                      bool use_5_taps,
0663                      int src_xres, int src_yres, int dst_xres,
0664                      int dst_yres)
0665 {
0666     int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];
0667     bool program_5_taps = use_5_taps ||
0668                   (dst_format == BUF_FMT_YUV422 &&
0669                    src_format == BUF_FMT_ARGB8888_YUV444);
0670 
0671     /* horizontal luma */
0672     dcss_scaler_filter_design(src_xres, dst_xres, false,
0673                   src_xres == dst_xres, coef,
0674                   ch->use_nn_interpolation);
0675     dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
0676 
0677     /* vertical luma */
0678     dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
0679                   src_yres == dst_yres, coef,
0680                   ch->use_nn_interpolation);
0681 
0682     if (program_5_taps)
0683         dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
0684     else
0685         dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
0686 
0687     /* adjust chroma resolution */
0688     if (src_format != BUF_FMT_ARGB8888_YUV444)
0689         src_xres >>= 1;
0690     if (src_format == BUF_FMT_YUV420)
0691         src_yres >>= 1;
0692     if (dst_format != BUF_FMT_ARGB8888_YUV444)
0693         dst_xres >>= 1;
0694     if (dst_format == BUF_FMT_YUV420) /* should not happen */
0695         dst_yres >>= 1;
0696 
0697     /* horizontal chroma */
0698     dcss_scaler_filter_design(src_xres, dst_xres, false,
0699                   (src_xres == dst_xres) && (ch->c_hstart == 0),
0700                   coef, ch->use_nn_interpolation);
0701 
0702     dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef);
0703 
0704     /* vertical chroma */
0705     dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
0706                   (src_yres == dst_yres) && (ch->c_vstart == 0),
0707                   coef, ch->use_nn_interpolation);
0708     if (program_5_taps)
0709         dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);
0710     else
0711         dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);
0712 }
0713 
0714 static void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch,
0715                      int src_xres, int src_yres, int dst_xres,
0716                      int dst_yres)
0717 {
0718     int coef[PSC_STORED_PHASES][PSC_NUM_TAPS];
0719 
0720     /* horizontal RGB */
0721     dcss_scaler_filter_design(src_xres, dst_xres, false,
0722                   src_xres == dst_xres, coef,
0723                   ch->use_nn_interpolation);
0724     dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
0725 
0726     /* vertical RGB */
0727     dcss_scaler_filter_design(src_yres, dst_yres, false,
0728                   src_yres == dst_yres, coef,
0729                   ch->use_nn_interpolation);
0730     dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
0731 }
0732 
0733 static void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch,
0734                     const struct drm_format_info *format)
0735 {
0736     u32 a2r10g10b10_format;
0737 
0738     if (format->is_yuv)
0739         return;
0740 
0741     ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK;
0742 
0743     if (format->depth != 30)
0744         return;
0745 
0746     switch (format->format) {
0747     case DRM_FORMAT_ARGB2101010:
0748     case DRM_FORMAT_XRGB2101010:
0749         a2r10g10b10_format = 0;
0750         break;
0751 
0752     case DRM_FORMAT_ABGR2101010:
0753     case DRM_FORMAT_XBGR2101010:
0754         a2r10g10b10_format = 5;
0755         break;
0756 
0757     case DRM_FORMAT_RGBA1010102:
0758     case DRM_FORMAT_RGBX1010102:
0759         a2r10g10b10_format = 6;
0760         break;
0761 
0762     case DRM_FORMAT_BGRA1010102:
0763     case DRM_FORMAT_BGRX1010102:
0764         a2r10g10b10_format = 11;
0765         break;
0766 
0767     default:
0768         a2r10g10b10_format = 0;
0769         break;
0770     }
0771 
0772     ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS;
0773 }
0774 
0775 void dcss_scaler_set_filter(struct dcss_scaler *scl, int ch_num,
0776                 enum drm_scaling_filter scaling_filter)
0777 {
0778     struct dcss_scaler_ch *ch = &scl->ch[ch_num];
0779 
0780     ch->use_nn_interpolation = scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR;
0781 }
0782 
0783 void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,
0784                const struct drm_format_info *format,
0785                int src_xres, int src_yres, int dst_xres, int dst_yres,
0786                u32 vrefresh_hz)
0787 {
0788     struct dcss_scaler_ch *ch = &scl->ch[ch_num];
0789     unsigned int pixel_depth = 0;
0790     bool rtr_8line_en = false;
0791     bool use_5_taps = false;
0792     enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444;
0793     enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444;
0794     u32 pix_format = format->format;
0795 
0796     if (format->is_yuv) {
0797         dcss_scaler_yuv_enable(ch, true);
0798 
0799         if (pix_format == DRM_FORMAT_NV12 ||
0800             pix_format == DRM_FORMAT_NV21) {
0801             rtr_8line_en = true;
0802             src_format = BUF_FMT_YUV420;
0803         } else if (pix_format == DRM_FORMAT_UYVY ||
0804                pix_format == DRM_FORMAT_VYUY ||
0805                pix_format == DRM_FORMAT_YUYV ||
0806                pix_format == DRM_FORMAT_YVYU) {
0807             src_format = BUF_FMT_YUV422;
0808         }
0809 
0810         use_5_taps = !rtr_8line_en;
0811     } else {
0812         dcss_scaler_yuv_enable(ch, false);
0813 
0814         pixel_depth = format->depth;
0815     }
0816 
0817     dcss_scaler_fractions_set(ch, src_xres, src_yres, dst_xres,
0818                   dst_yres, src_format, dst_format,
0819                   PSC_LOC_HORZ_0_VERT_1_OVER_4);
0820 
0821     if (format->is_yuv)
0822         dcss_scaler_yuv_coef_set(ch, src_format, dst_format,
0823                      use_5_taps, src_xres, src_yres,
0824                      dst_xres, dst_yres);
0825     else
0826         dcss_scaler_rgb_coef_set(ch, src_xres, src_yres,
0827                      dst_xres, dst_yres);
0828 
0829     dcss_scaler_rtr_8lines_enable(ch, rtr_8line_en);
0830     dcss_scaler_bit_depth_set(ch, pixel_depth);
0831     dcss_scaler_set_rgb10_order(ch, format);
0832     dcss_scaler_format_set(ch, src_format, dst_format);
0833     dcss_scaler_res_set(ch, src_xres, src_yres, dst_xres, dst_yres,
0834                 pix_format, dst_format);
0835 }
0836 
0837 /* This function will be called from interrupt context. */
0838 void dcss_scaler_write_sclctrl(struct dcss_scaler *scl)
0839 {
0840     int chnum;
0841 
0842     dcss_ctxld_assert_locked(scl->ctxld);
0843 
0844     for (chnum = 0; chnum < 3; chnum++) {
0845         struct dcss_scaler_ch *ch = &scl->ch[chnum];
0846 
0847         if (ch->scaler_ctrl_chgd) {
0848             dcss_ctxld_write_irqsafe(scl->ctxld, scl->ctx_id,
0849                          ch->scaler_ctrl,
0850                          ch->base_ofs +
0851                          DCSS_SCALER_CTRL);
0852             ch->scaler_ctrl_chgd = false;
0853         }
0854     }
0855 }