1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (c) 2013 Texas Instruments Inc.
7 * David Griego, <dagriego@biglakesoftware.com>
8 * Dale Farnsworth, <dale@farnsworth.org>
9 * Archit Taneja, <archit@ti.com>
12 #include <linux/err.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/slab.h>
21 void sc_dump_regs(struct sc_data
*sc
)
23 struct device
*dev
= &sc
->pdev
->dev
;
25 #define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \
26 ioread32(sc->base + CFG_##r))
28 dev_dbg(dev
, "SC Registers @ %pa:\n", &sc
->res
->start
);
55 EXPORT_SYMBOL(sc_dump_regs
);
58 * set the horizontal scaler coefficients according to the ratio of output to
59 * input widths, after accounting for up to two levels of decimation
61 void sc_set_hs_coeffs(struct sc_data
*sc
, void *addr
, unsigned int src_w
,
73 if ((dst_w
<< 1) < src_w
)
74 dst_w
<<= 1; /* first level decimation */
75 if ((dst_w
<< 1) < src_w
)
76 dst_w
<<= 1; /* second level decimation */
79 idx
= HS_LE_16_16_SCALE
;
81 sixteenths
= (dst_w
<< 4) / src_w
;
84 idx
= HS_LT_9_16_SCALE
+ sixteenths
- 8;
88 cp
= scaler_hs_coeffs
[idx
];
90 for (i
= 0; i
< SC_NUM_PHASES
* 2; i
++) {
91 for (j
= 0; j
< SC_H_NUM_TAPS
; j
++)
94 * for each phase, the scaler expects space for 8 coefficients
95 * in it's memory. For the horizontal scaler, we copy the first
96 * 7 coefficients and skip the last slot to move to the next
97 * row to hold coefficients for the next phase
99 coeff_h
+= SC_NUM_TAPS_MEM_ALIGN
- SC_H_NUM_TAPS
;
102 sc
->load_coeff_h
= true;
104 EXPORT_SYMBOL(sc_set_hs_coeffs
);
107 * set the vertical scaler coefficients according to the ratio of output to
110 void sc_set_vs_coeffs(struct sc_data
*sc
, void *addr
, unsigned int src_h
,
121 } else if (dst_h
== src_h
) {
122 idx
= VS_1_TO_1_SCALE
;
124 sixteenths
= (dst_h
<< 4) / src_h
;
127 idx
= VS_LT_9_16_SCALE
+ sixteenths
- 8;
130 cp
= scaler_vs_coeffs
[idx
];
132 for (i
= 0; i
< SC_NUM_PHASES
* 2; i
++) {
133 for (j
= 0; j
< SC_V_NUM_TAPS
; j
++)
136 * for the vertical scaler, we copy the first 5 coefficients and
137 * skip the last 3 slots to move to the next row to hold
138 * coefficients for the next phase
140 coeff_v
+= SC_NUM_TAPS_MEM_ALIGN
- SC_V_NUM_TAPS
;
143 sc
->load_coeff_v
= true;
145 EXPORT_SYMBOL(sc_set_vs_coeffs
);
147 void sc_config_scaler(struct sc_data
*sc
, u32
*sc_reg0
, u32
*sc_reg8
,
148 u32
*sc_reg17
, unsigned int src_w
, unsigned int src_h
,
149 unsigned int dst_w
, unsigned int dst_h
)
151 struct device
*dev
= &sc
->pdev
->dev
;
153 int dcm_x
, dcm_shift
;
156 u32 lin_acc_inc
, lin_acc_inc_u
;
159 int row_acc_init_rav
= 0, row_acc_init_rav_b
= 0;
160 u32 row_acc_inc
= 0, row_acc_offset
= 0, row_acc_offset_b
= 0;
162 * location of SC register in payload memory with respect to the first
163 * register in the mmr address data block
165 u32
*sc_reg9
= sc_reg8
+ 1;
166 u32
*sc_reg12
= sc_reg8
+ 4;
167 u32
*sc_reg13
= sc_reg8
+ 5;
168 u32
*sc_reg24
= sc_reg17
+ 7;
172 /* clear all the features(they may get enabled elsewhere later) */
173 val
&= ~(CFG_SELFGEN_FID
| CFG_TRIM
| CFG_ENABLE_SIN2_VER_INTP
|
174 CFG_INTERLACE_I
| CFG_DCM_4X
| CFG_DCM_2X
| CFG_AUTO_HS
|
175 CFG_ENABLE_EV
| CFG_USE_RAV
| CFG_INVT_FID
| CFG_SC_BYPASS
|
176 CFG_INTERLACE_O
| CFG_Y_PK_EN
| CFG_HP_BYPASS
| CFG_LINEAR
);
178 if (src_w
== dst_w
&& src_h
== dst_h
) {
179 val
|= CFG_SC_BYPASS
;
184 /* we only support linear scaling for now */
187 /* configure horizontal scaler */
189 /* enable 2X or 4X decimation */
190 dcm_x
= src_w
/ dst_w
;
194 } else if (dcm_x
> 2) {
202 lin_acc_inc
= div64_u64(((u64
)(src_w
>> dcm_shift
) - 1) << 24, lltmp
);
206 dev_dbg(dev
, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n",
207 src_w
, dst_w
, dcm_shift
== 2 ? "4x" :
208 (dcm_shift
== 1 ? "2x" : "none"), lin_acc_inc
);
210 /* configure vertical scaler */
212 /* use RAV for vertical scaler if vertical downscaling is > 4x */
213 if (dst_h
< (src_h
>> 2)) {
222 factor
= (u16
) ((dst_h
<< 10) / src_h
);
224 row_acc_init_rav
= factor
+ ((1 + factor
) >> 1);
225 if (row_acc_init_rav
>= 1024)
226 row_acc_init_rav
-= 1024;
228 row_acc_init_rav_b
= row_acc_init_rav
+
229 (1 + (row_acc_init_rav
>> 1)) -
232 if (row_acc_init_rav_b
< 0) {
233 row_acc_init_rav_b
+= row_acc_init_rav
;
234 row_acc_init_rav
*= 2;
237 dev_dbg(dev
, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n",
238 src_h
, dst_h
, factor
, row_acc_init_rav
,
242 row_acc_inc
= ((src_h
- 1) << 16) / (dst_h
- 1);
244 row_acc_offset_b
= 0;
246 dev_dbg(dev
, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n",
247 src_h
, dst_h
, row_acc_inc
);
252 sc_reg0
[1] = row_acc_inc
;
253 sc_reg0
[2] = row_acc_offset
;
254 sc_reg0
[3] = row_acc_offset_b
;
256 sc_reg0
[4] = ((lin_acc_inc_u
& CFG_LIN_ACC_INC_U_MASK
) <<
257 CFG_LIN_ACC_INC_U_SHIFT
) | (dst_w
<< CFG_TAR_W_SHIFT
) |
258 (dst_h
<< CFG_TAR_H_SHIFT
);
260 sc_reg0
[5] = (src_w
<< CFG_SRC_W_SHIFT
) | (src_h
<< CFG_SRC_H_SHIFT
);
262 sc_reg0
[6] = (row_acc_init_rav_b
<< CFG_ROW_ACC_INIT_RAV_B_SHIFT
) |
263 (row_acc_init_rav
<< CFG_ROW_ACC_INIT_RAV_SHIFT
);
265 *sc_reg9
= lin_acc_inc
;
267 *sc_reg12
= col_acc_offset
<< CFG_COL_ACC_OFFSET_SHIFT
;
271 *sc_reg24
= (src_w
<< CFG_ORG_W_SHIFT
) | (src_h
<< CFG_ORG_H_SHIFT
);
273 EXPORT_SYMBOL(sc_config_scaler
);
275 struct sc_data
*sc_create(struct platform_device
*pdev
, const char *res_name
)
279 dev_dbg(&pdev
->dev
, "sc_create\n");
281 sc
= devm_kzalloc(&pdev
->dev
, sizeof(*sc
), GFP_KERNEL
);
283 dev_err(&pdev
->dev
, "couldn't alloc sc_data\n");
284 return ERR_PTR(-ENOMEM
);
289 sc
->res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, res_name
);
291 dev_err(&pdev
->dev
, "missing '%s' platform resources data\n",
293 return ERR_PTR(-ENODEV
);
296 sc
->base
= devm_ioremap_resource(&pdev
->dev
, sc
->res
);
297 if (IS_ERR(sc
->base
)) {
298 dev_err(&pdev
->dev
, "failed to ioremap\n");
299 return ERR_CAST(sc
->base
);
304 EXPORT_SYMBOL(sc_create
);
306 MODULE_DESCRIPTION("TI VIP/VPE Scaler");
307 MODULE_AUTHOR("Texas Instruments Inc.");
308 MODULE_LICENSE("GPL v2");