Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 1 | /* |
| 2 | * Scaler library |
| 3 | * |
| 4 | * Copyright (c) 2013 Texas Instruments Inc. |
| 5 | * |
| 6 | * David Griego, <dagriego@biglakesoftware.com> |
| 7 | * Dale Farnsworth, <dale@farnsworth.org> |
| 8 | * Archit Taneja, <archit@ti.com> |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify it |
| 11 | * under the terms of the GNU General Public License version 2 as published by |
| 12 | * the Free Software Foundation. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/err.h> |
| 16 | #include <linux/io.h> |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 17 | #include <linux/module.h> |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 18 | #include <linux/platform_device.h> |
| 19 | #include <linux/slab.h> |
| 20 | |
| 21 | #include "sc.h" |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 22 | #include "sc_coeff.h" |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 23 | |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 24 | void sc_dump_regs(struct sc_data *sc) |
| 25 | { |
| 26 | struct device *dev = &sc->pdev->dev; |
| 27 | |
Behan Webster | 61110fb | 2014-09-26 22:11:45 -0300 | [diff] [blame] | 28 | #define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \ |
| 29 | ioread32(sc->base + CFG_##r)) |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 30 | |
Benoit Parrot | d6a6178 | 2016-11-18 21:20:40 -0200 | [diff] [blame] | 31 | dev_dbg(dev, "SC Registers @ %pa:\n", &sc->res->start); |
| 32 | |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 33 | DUMPREG(SC0); |
| 34 | DUMPREG(SC1); |
| 35 | DUMPREG(SC2); |
| 36 | DUMPREG(SC3); |
| 37 | DUMPREG(SC4); |
| 38 | DUMPREG(SC5); |
| 39 | DUMPREG(SC6); |
| 40 | DUMPREG(SC8); |
| 41 | DUMPREG(SC9); |
| 42 | DUMPREG(SC10); |
| 43 | DUMPREG(SC11); |
| 44 | DUMPREG(SC12); |
| 45 | DUMPREG(SC13); |
| 46 | DUMPREG(SC17); |
| 47 | DUMPREG(SC18); |
| 48 | DUMPREG(SC19); |
| 49 | DUMPREG(SC20); |
| 50 | DUMPREG(SC21); |
| 51 | DUMPREG(SC22); |
| 52 | DUMPREG(SC23); |
| 53 | DUMPREG(SC24); |
| 54 | DUMPREG(SC25); |
| 55 | |
| 56 | #undef DUMPREG |
| 57 | } |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 58 | EXPORT_SYMBOL(sc_dump_regs); |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 59 | |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 60 | /* |
| 61 | * set the horizontal scaler coefficients according to the ratio of output to |
| 62 | * input widths, after accounting for up to two levels of decimation |
| 63 | */ |
| 64 | void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, |
| 65 | unsigned int dst_w) |
| 66 | { |
| 67 | int sixteenths; |
| 68 | int idx; |
| 69 | int i, j; |
| 70 | u16 *coeff_h = addr; |
| 71 | const u16 *cp; |
| 72 | |
| 73 | if (dst_w > src_w) { |
| 74 | idx = HS_UP_SCALE; |
| 75 | } else { |
| 76 | if ((dst_w << 1) < src_w) |
| 77 | dst_w <<= 1; /* first level decimation */ |
| 78 | if ((dst_w << 1) < src_w) |
| 79 | dst_w <<= 1; /* second level decimation */ |
| 80 | |
| 81 | if (dst_w == src_w) { |
| 82 | idx = HS_LE_16_16_SCALE; |
| 83 | } else { |
| 84 | sixteenths = (dst_w << 4) / src_w; |
| 85 | if (sixteenths < 8) |
| 86 | sixteenths = 8; |
| 87 | idx = HS_LT_9_16_SCALE + sixteenths - 8; |
| 88 | } |
| 89 | } |
| 90 | |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 91 | cp = scaler_hs_coeffs[idx]; |
| 92 | |
| 93 | for (i = 0; i < SC_NUM_PHASES * 2; i++) { |
| 94 | for (j = 0; j < SC_H_NUM_TAPS; j++) |
| 95 | *coeff_h++ = *cp++; |
| 96 | /* |
| 97 | * for each phase, the scaler expects space for 8 coefficients |
| 98 | * in it's memory. For the horizontal scaler, we copy the first |
| 99 | * 7 coefficients and skip the last slot to move to the next |
| 100 | * row to hold coefficients for the next phase |
| 101 | */ |
| 102 | coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS; |
| 103 | } |
| 104 | |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 105 | sc->load_coeff_h = true; |
| 106 | } |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 107 | EXPORT_SYMBOL(sc_set_hs_coeffs); |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 108 | |
| 109 | /* |
| 110 | * set the vertical scaler coefficients according to the ratio of output to |
| 111 | * input heights |
| 112 | */ |
| 113 | void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, |
| 114 | unsigned int dst_h) |
| 115 | { |
| 116 | int sixteenths; |
| 117 | int idx; |
| 118 | int i, j; |
| 119 | u16 *coeff_v = addr; |
| 120 | const u16 *cp; |
| 121 | |
| 122 | if (dst_h > src_h) { |
| 123 | idx = VS_UP_SCALE; |
| 124 | } else if (dst_h == src_h) { |
| 125 | idx = VS_1_TO_1_SCALE; |
| 126 | } else { |
| 127 | sixteenths = (dst_h << 4) / src_h; |
| 128 | if (sixteenths < 8) |
| 129 | sixteenths = 8; |
| 130 | idx = VS_LT_9_16_SCALE + sixteenths - 8; |
| 131 | } |
| 132 | |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 133 | cp = scaler_vs_coeffs[idx]; |
| 134 | |
| 135 | for (i = 0; i < SC_NUM_PHASES * 2; i++) { |
| 136 | for (j = 0; j < SC_V_NUM_TAPS; j++) |
| 137 | *coeff_v++ = *cp++; |
| 138 | /* |
| 139 | * for the vertical scaler, we copy the first 5 coefficients and |
| 140 | * skip the last 3 slots to move to the next row to hold |
| 141 | * coefficients for the next phase |
| 142 | */ |
| 143 | coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS; |
| 144 | } |
| 145 | |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 146 | sc->load_coeff_v = true; |
| 147 | } |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 148 | EXPORT_SYMBOL(sc_set_vs_coeffs); |
Archit Taneja | 0df20f9 | 2013-12-12 05:35:58 -0300 | [diff] [blame] | 149 | |
Archit Taneja | bbee8b3 | 2013-12-12 05:36:00 -0300 | [diff] [blame] | 150 | void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, |
| 151 | u32 *sc_reg17, unsigned int src_w, unsigned int src_h, |
| 152 | unsigned int dst_w, unsigned int dst_h) |
| 153 | { |
| 154 | struct device *dev = &sc->pdev->dev; |
| 155 | u32 val; |
| 156 | int dcm_x, dcm_shift; |
| 157 | bool use_rav; |
| 158 | unsigned long lltmp; |
| 159 | u32 lin_acc_inc, lin_acc_inc_u; |
| 160 | u32 col_acc_offset; |
| 161 | u16 factor = 0; |
| 162 | int row_acc_init_rav = 0, row_acc_init_rav_b = 0; |
| 163 | u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0; |
| 164 | /* |
| 165 | * location of SC register in payload memory with respect to the first |
| 166 | * register in the mmr address data block |
| 167 | */ |
| 168 | u32 *sc_reg9 = sc_reg8 + 1; |
| 169 | u32 *sc_reg12 = sc_reg8 + 4; |
| 170 | u32 *sc_reg13 = sc_reg8 + 5; |
| 171 | u32 *sc_reg24 = sc_reg17 + 7; |
| 172 | |
| 173 | val = sc_reg0[0]; |
| 174 | |
| 175 | /* clear all the features(they may get enabled elsewhere later) */ |
| 176 | val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP | |
| 177 | CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS | |
| 178 | CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS | |
| 179 | CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR); |
| 180 | |
| 181 | if (src_w == dst_w && src_h == dst_h) { |
| 182 | val |= CFG_SC_BYPASS; |
| 183 | sc_reg0[0] = val; |
| 184 | return; |
| 185 | } |
| 186 | |
| 187 | /* we only support linear scaling for now */ |
| 188 | val |= CFG_LINEAR; |
| 189 | |
| 190 | /* configure horizontal scaler */ |
| 191 | |
| 192 | /* enable 2X or 4X decimation */ |
| 193 | dcm_x = src_w / dst_w; |
| 194 | if (dcm_x > 4) { |
| 195 | val |= CFG_DCM_4X; |
| 196 | dcm_shift = 2; |
| 197 | } else if (dcm_x > 2) { |
| 198 | val |= CFG_DCM_2X; |
| 199 | dcm_shift = 1; |
| 200 | } else { |
| 201 | dcm_shift = 0; |
| 202 | } |
| 203 | |
| 204 | lltmp = dst_w - 1; |
| 205 | lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp); |
| 206 | lin_acc_inc_u = 0; |
| 207 | col_acc_offset = 0; |
| 208 | |
| 209 | dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n", |
| 210 | src_w, dst_w, dcm_shift == 2 ? "4x" : |
| 211 | (dcm_shift == 1 ? "2x" : "none"), lin_acc_inc); |
| 212 | |
| 213 | /* configure vertical scaler */ |
| 214 | |
| 215 | /* use RAV for vertical scaler if vertical downscaling is > 4x */ |
| 216 | if (dst_h < (src_h >> 2)) { |
| 217 | use_rav = true; |
| 218 | val |= CFG_USE_RAV; |
| 219 | } else { |
| 220 | use_rav = false; |
| 221 | } |
| 222 | |
| 223 | if (use_rav) { |
| 224 | /* use RAV */ |
| 225 | factor = (u16) ((dst_h << 10) / src_h); |
| 226 | |
| 227 | row_acc_init_rav = factor + ((1 + factor) >> 1); |
| 228 | if (row_acc_init_rav >= 1024) |
| 229 | row_acc_init_rav -= 1024; |
| 230 | |
| 231 | row_acc_init_rav_b = row_acc_init_rav + |
| 232 | (1 + (row_acc_init_rav >> 1)) - |
| 233 | (1024 >> 1); |
| 234 | |
| 235 | if (row_acc_init_rav_b < 0) { |
| 236 | row_acc_init_rav_b += row_acc_init_rav; |
| 237 | row_acc_init_rav *= 2; |
| 238 | } |
| 239 | |
| 240 | dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n", |
| 241 | src_h, dst_h, factor, row_acc_init_rav, |
| 242 | row_acc_init_rav_b); |
| 243 | } else { |
| 244 | /* use polyphase */ |
| 245 | row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1); |
| 246 | row_acc_offset = 0; |
| 247 | row_acc_offset_b = 0; |
| 248 | |
| 249 | dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n", |
| 250 | src_h, dst_h, row_acc_inc); |
| 251 | } |
| 252 | |
| 253 | |
| 254 | sc_reg0[0] = val; |
| 255 | sc_reg0[1] = row_acc_inc; |
| 256 | sc_reg0[2] = row_acc_offset; |
| 257 | sc_reg0[3] = row_acc_offset_b; |
| 258 | |
| 259 | sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) << |
| 260 | CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) | |
| 261 | (dst_h << CFG_TAR_H_SHIFT); |
| 262 | |
| 263 | sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT); |
| 264 | |
| 265 | sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) | |
| 266 | (row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT); |
| 267 | |
| 268 | *sc_reg9 = lin_acc_inc; |
| 269 | |
| 270 | *sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT; |
| 271 | |
| 272 | *sc_reg13 = factor; |
| 273 | |
| 274 | *sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT); |
| 275 | } |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 276 | EXPORT_SYMBOL(sc_config_scaler); |
Archit Taneja | bbee8b3 | 2013-12-12 05:36:00 -0300 | [diff] [blame] | 277 | |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 278 | struct sc_data *sc_create(struct platform_device *pdev, const char *res_name) |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 279 | { |
| 280 | struct sc_data *sc; |
| 281 | |
| 282 | dev_dbg(&pdev->dev, "sc_create\n"); |
| 283 | |
| 284 | sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); |
| 285 | if (!sc) { |
| 286 | dev_err(&pdev->dev, "couldn't alloc sc_data\n"); |
| 287 | return ERR_PTR(-ENOMEM); |
| 288 | } |
| 289 | |
| 290 | sc->pdev = pdev; |
| 291 | |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 292 | sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 293 | if (!sc->res) { |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 294 | dev_err(&pdev->dev, "missing '%s' platform resources data\n", |
| 295 | res_name); |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 296 | return ERR_PTR(-ENODEV); |
| 297 | } |
| 298 | |
| 299 | sc->base = devm_ioremap_resource(&pdev->dev, sc->res); |
Bartlomiej Zolnierkiewicz | 691903f | 2014-03-18 07:41:42 -0300 | [diff] [blame] | 300 | if (IS_ERR(sc->base)) { |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 301 | dev_err(&pdev->dev, "failed to ioremap\n"); |
Hans Verkuil | 3f79913 | 2014-11-05 05:03:00 -0300 | [diff] [blame] | 302 | return ERR_CAST(sc->base); |
Archit Taneja | 44687b2 | 2013-12-12 05:35:57 -0300 | [diff] [blame] | 303 | } |
| 304 | |
| 305 | return sc; |
| 306 | } |
Benoit Parrot | 1c6e817 | 2016-11-18 21:20:39 -0200 | [diff] [blame] | 307 | EXPORT_SYMBOL(sc_create); |
| 308 | |
| 309 | MODULE_DESCRIPTION("TI VIP/VPE Scaler"); |
| 310 | MODULE_AUTHOR("Texas Instruments Inc."); |
| 311 | MODULE_LICENSE("GPL v2"); |