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