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