@@ -0,0 +1,335 @@
+/* drivers/video/msm/mdp_ppp31.c
+ *
+ * Copyright (C) 2009, 2011 Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2009 Google Incorporated
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/msm_mdp.h>
+
+#include "mdp_hw.h"
+#include "mdp_ppp.h"
+
+#define NUM_COEFFS 32
+
+struct mdp_scale_coeffs {
+ /*
+ * signed 10-bit coeffs that need to be be 16-bit aligned
+ */
+ uint16_t c[4][NUM_COEFFS];
+};
+
+struct mdp_scale_tbl_info {
+ uint16_t offset;
+ uint32_t set:2;
+ int use_pr;
+ struct mdp_scale_coeffs coeffs;
+};
+
+enum {
+ MDP_SCALE_PT2TOPT4,
+ MDP_SCALE_PT4TOPT6,
+ MDP_SCALE_PT6TOPT8,
+ MDP_SCALE_PT8TO8,
+ MDP_SCALE_MAX,
+};
+
+static struct mdp_scale_coeffs mdp_scale_pr_coeffs = {
+ .c = {
+ [0] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ [1] = {
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ [2] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ 511, 511, 511, 511, 511, 511, 511, 511,
+ },
+ [3] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ },
+};
+
+static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = {
+ [MDP_SCALE_PT2TOPT4] = {
+ .offset = 0,
+ .set = MDP_PPP_SCALE_COEFF_D0_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 131, 131, 130, 129, 128, 127, 127, 126,
+ 125, 125, 124, 123, 123, 121, 120, 119,
+ 119, 118, 117, 117, 116, 115, 115, 114,
+ 113, 112, 111, 110, 109, 109, 108, 107,
+ },
+ [1] = {
+ 141, 140, 140, 140, 140, 139, 138, 138,
+ 138, 137, 137, 137, 136, 137, 137, 137,
+ 136, 136, 136, 135, 135, 135, 134, 134,
+ 134, 134, 134, 133, 133, 132, 132, 132,
+ },
+ [2] = {
+ 132, 132, 132, 133, 133, 134, 134, 134,
+ 134, 134, 135, 135, 135, 136, 136, 136,
+ 137, 137, 137, 136, 137, 137, 137, 138,
+ 138, 138, 139, 140, 140, 140, 140, 141,
+ },
+ [3] = {
+ 107, 108, 109, 109, 110, 111, 112, 113,
+ 114, 115, 115, 116, 117, 117, 118, 119,
+ 119, 120, 121, 123, 123, 124, 125, 125,
+ 126, 127, 127, 128, 129, 130, 131, 131,
+ }
+ },
+ },
+ [MDP_SCALE_PT4TOPT6] = {
+ .offset = 32,
+ .set = MDP_PPP_SCALE_COEFF_D1_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 136, 132, 128, 123, 119, 115, 111, 107,
+ 103, 98, 95, 91, 87, 84, 80, 76,
+ 73, 69, 66, 62, 59, 57, 54, 50,
+ 47, 44, 41, 39, 36, 33, 32, 29,
+ },
+ [1] = {
+ 206, 205, 204, 204, 201, 200, 199, 197,
+ 196, 194, 191, 191, 189, 185, 184, 182,
+ 180, 178, 176, 173, 170, 168, 165, 162,
+ 160, 157, 155, 152, 148, 146, 142, 140,
+ },
+ [2] = {
+ 140, 142, 146, 148, 152, 155, 157, 160,
+ 162, 165, 168, 170, 173, 176, 178, 180,
+ 182, 184, 185, 189, 191, 191, 194, 196,
+ 197, 199, 200, 201, 204, 204, 205, 206,
+ },
+ [3] = {
+ 29, 32, 33, 36, 39, 41, 44, 47,
+ 50, 54, 57, 59, 62, 66, 69, 73,
+ 76, 80, 84, 87, 91, 95, 98, 103,
+ 107, 111, 115, 119, 123, 128, 132, 136,
+ },
+ },
+ },
+ [MDP_SCALE_PT6TOPT8] = {
+ .offset = 64,
+ .set = MDP_PPP_SCALE_COEFF_D2_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 104, 96, 89, 82, 75, 68, 61, 55,
+ 49, 43, 38, 33, 28, 24, 20, 16,
+ 12, 9, 6, 4, 2, 0, -2, -4,
+ -5, -6, -7, -7, -8, -8, -8, -8,
+ },
+ [1] = {
+ 303, 303, 302, 300, 298, 296, 293, 289,
+ 286, 281, 276, 270, 265, 258, 252, 245,
+ 238, 230, 223, 214, 206, 197, 189, 180,
+ 172, 163, 154, 145, 137, 128, 120, 112,
+ },
+ [2] = {
+ 112, 120, 128, 137, 145, 154, 163, 172,
+ 180, 189, 197, 206, 214, 223, 230, 238,
+ 245, 252, 258, 265, 270, 276, 281, 286,
+ 289, 293, 296, 298, 300, 302, 303, 303,
+ },
+ [3] = {
+ -8, -8, -8, -8, -7, -7, -6, -5,
+ -4, -2, 0, 2, 4, 6, 9, 12,
+ 16, 20, 24, 28, 33, 38, 43, 49,
+ 55, 61, 68, 75, 82, 89, 96, 104,
+ },
+ },
+ },
+ [MDP_SCALE_PT8TO8] = {
+ .offset = 96,
+ .set = MDP_PPP_SCALE_COEFF_U1_SET,
+ .use_pr = -1,
+ .coeffs.c = {
+ [0] = {
+ 0, -7, -13, -19, -24, -28, -32, -34,
+ -37, -39, -40, -41, -41, -41, -40, -40,
+ -38, -37, -35, -33, -31, -29, -26, -24,
+ -21, -18, -15, -13, -10, -7, -5, -2,
+ },
+ [1] = {
+ 511, 507, 501, 494, 485, 475, 463, 450,
+ 436, 422, 405, 388, 370, 352, 333, 314,
+ 293, 274, 253, 233, 213, 193, 172, 152,
+ 133, 113, 95, 77, 60, 43, 28, 13,
+ },
+ [2] = {
+ 0, 13, 28, 43, 60, 77, 95, 113,
+ 133, 152, 172, 193, 213, 233, 253, 274,
+ 294, 314, 333, 352, 370, 388, 405, 422,
+ 436, 450, 463, 475, 485, 494, 501, 507,
+ },
+ [3] = {
+ 0, -2, -5, -7, -10, -13, -15, -18,
+ -21, -24, -26, -29, -31, -33, -35, -37,
+ -38, -40, -40, -41, -41, -41, -40, -39,
+ -37, -34, -32, -28, -24, -19, -13, -7,
+ },
+ },
+ },
+};
+
+static void load_table(const struct mdp_info *mdp, int scale, int use_pr)
+{
+ int i;
+ uint32_t val;
+ struct mdp_scale_coeffs *coeffs;
+ struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale];
+
+ if (use_pr == tbl->use_pr)
+ return;
+
+ tbl->use_pr = use_pr;
+ if (!use_pr)
+ coeffs = &tbl->coeffs;
+ else
+ coeffs = &mdp_scale_pr_coeffs;
+
+ for (i = 0; i < NUM_COEFFS; ++i) {
+ val = ((coeffs->c[1][i] & 0x3ff) << 16) |
+ (coeffs->c[0][i] & 0x3ff);
+ mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i));
+
+ val = ((coeffs->c[3][i] & 0x3ff) << 16) |
+ (coeffs->c[2][i] & 0x3ff);
+ mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i));
+ }
+}
+
+#define SCALER_PHASE_BITS 29
+static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler,
+ uint32_t *phase_init, uint32_t *phase_step)
+{
+ uint64_t src = dim_in;
+ uint64_t dst = dim_out;
+ uint64_t numer;
+ uint64_t denom;
+
+ *phase_init = 0;
+
+ if (dst == 1) {
+ /* if destination is 1 pixel wide, the value of phase_step
+ * is unimportant. */
+ *phase_step = (uint32_t) (src << SCALER_PHASE_BITS);
+ if (scaler == MDP_PPP_SCALER_FIR)
+ *phase_init =
+ (uint32_t) ((src - 1) << SCALER_PHASE_BITS);
+ return;
+ }
+
+ if (scaler == MDP_PPP_SCALER_FIR) {
+ numer = (src - 1) << SCALER_PHASE_BITS;
+ denom = dst - 1;
+ /* we want to round up the result*/
+ numer += denom - 1;
+ } else {
+ numer = src << SCALER_PHASE_BITS;
+ denom = dst;
+ }
+
+ do_div(numer, denom);
+ *phase_step = (uint32_t) numer;
+}
+
+static int scale_idx(int factor)
+{
+ int idx;
+
+ if (factor > 80)
+ idx = MDP_SCALE_PT8TO8;
+ else if (factor > 60)
+ idx = MDP_SCALE_PT6TOPT8;
+ else if (factor > 40)
+ idx = MDP_SCALE_PT4TOPT6;
+ else
+ idx = MDP_SCALE_PT2TOPT4;
+
+ return idx;
+}
+
+int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
+ struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
+ uint32_t src_format, uint32_t dst_format)
+{
+ uint32_t x_fac;
+ uint32_t y_fac;
+ uint32_t scaler_x = MDP_PPP_SCALER_FIR;
+ uint32_t scaler_y = MDP_PPP_SCALER_FIR;
+ /* Don't use pixel repeat mode, it looks bad */
+ int use_pr = 0;
+ int x_idx;
+ int y_idx;
+
+ if (unlikely(src_rect->w > 2048 || src_rect->h > 2048))
+ return -ENOTSUPP;
+
+ x_fac = (dst_rect->w * 100) / src_rect->w;
+ y_fac = (dst_rect->h * 100) / src_rect->h;
+
+ /* if down-scaling by a factor smaller than 1/4, use M/N */
+ scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+ scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR;
+ scale_params(src_rect->w, dst_rect->w, scaler_x, ®s->phasex_init,
+ ®s->phasex_step);
+ scale_params(src_rect->h, dst_rect->h, scaler_y, ®s->phasey_init,
+ ®s->phasey_step);
+
+ x_idx = scale_idx(x_fac);
+ y_idx = scale_idx(y_fac);
+ load_table(mdp, x_idx, use_pr);
+ load_table(mdp, y_idx, use_pr);
+
+ regs->scale_cfg = 0;
+ /* Enable SVI when source or destination is YUV */
+ if (!IS_RGB(src_format) && !IS_RGB(dst_format))
+ regs->scale_cfg |= (1 << 6);
+ regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) |
+ (mdp_scale_tbl[x_idx].set << 4);
+ regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1);
+
+ return 0;
+}
+
+int mdp_ppp_load_blur(const struct mdp_info *mdp)
+{
+ return -ENOTSUPP;
+}
+
+void mdp_ppp_init_scale(const struct mdp_info *mdp)
+{
+ int scale;
+ for (scale = 0; scale < MDP_SCALE_MAX; ++scale)
+ load_table(mdp, scale, 0);
+}