From: Yoshihiko Mori <[email protected]>

Add UDS control for R-Car Gen3. Up down scaler can be vertical and
horizontal scaling.

Signed-off-by: Yoshihiko Mori <[email protected]>
Signed-off-by: Koji Matsuoka <[email protected]>
Signed-off-by: Yoshihiro Kaneko <[email protected]>
---
 drivers/media/platform/soc_camera/rcar_vin.c | 175 +++++++++++++++++++++------
 1 file changed, 140 insertions(+), 35 deletions(-)

diff --git a/drivers/media/platform/soc_camera/rcar_vin.c 
b/drivers/media/platform/soc_camera/rcar_vin.c
index dc75a80..a22141b 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -90,6 +90,7 @@
 
 /* Register bit fields for R-Car VIN */
 /* Video n Main Control Register bits */
+#define VNMC_SCLE              (1 << 26)
 #define VNMC_FOC               (1 << 21)
 #define VNMC_YCAL              (1 << 19)
 #define VNMC_INF_YUV8_BT656    (0 << 16)
@@ -132,6 +133,17 @@
 #define VNDMR2_FTEV            (1 << 17)
 #define VNDMR2_VLV(n)          ((n & 0xf) << 12)
 
+/* UDS */
+#define VNUDS_CTRL_REG         0x80    /* Scaling Control Registers */
+#define VNUDS_CTRL_AMD         (1 << 30)
+#define VNUDS_CTRL_BC          (1 << 20)
+#define VNUDS_CTRL_TDIPC       (1 << 1)
+
+#define VNUDS_SCALE_REG                0x84    /* Scaling Factor Register */
+#define VNUDS_PASS_BWIDTH_REG  0x90    /* Passband Registers */
+#define VNUDS_IPC_REG          0x98    /* 2D IPC Setting Register */
+#define VNUDS_CLIP_SIZE_REG    0xA4    /* UDS Output Size Clipping Register */
+
 #define VIN_MAX_WIDTH          2048
 #define VIN_MAX_HEIGHT         2048
 
@@ -526,6 +538,14 @@ struct rcar_vin_cam {
        const struct soc_mbus_pixelfmt  *extra_fmt;
 };
 
+static inline int is_scaling(struct rcar_vin_cam *cam)
+{
+       if (cam->width != cam->out_width || cam->height != cam->out_height)
+               return 1;
+
+       return 0;
+}
+
 /*
  * .queue_setup() is called to check whether the driver can accept the 
requested
  * number of buffers and to fill in plane sizes for the current frame format if
@@ -667,6 +687,9 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
        if (input_is_yuv == output_is_yuv)
                vnmc |= VNMC_BPS;
 
+       if (priv->chip == RCAR_GEN3 && is_scaling(cam))
+               vnmc |= VNMC_SCLE;
+
        /* progressive or interlaced mode */
        interrupts = progressive ? VNIE_FIE : VNIE_EFE;
 
@@ -973,6 +996,75 @@ static void rcar_vin_remove_device(struct 
soc_camera_device *icd)
                icd->devnum);
 }
 
+struct rcar_vin_uds_regs {
+       unsigned long ctrl;
+       unsigned long scale;
+       unsigned long pass_bwidth;
+       unsigned long clip_size;
+};
+
+static unsigned long rcar_vin_get_bwidth(unsigned long ratio)
+{
+       unsigned long bwidth;
+       unsigned long mant, frac;
+
+       mant = (ratio & 0xF000) >> 12;
+       frac = ratio & 0x0FFF;
+       if (mant)
+               bwidth = 64 * 4096 * mant / (4096 * mant + frac);
+       else
+               bwidth = 64;
+
+       return bwidth;
+}
+
+static unsigned long rcar_vin_compute_ratio(unsigned int input,
+               unsigned int output)
+{
+#ifdef DISABLE_UDS_CTRL_AMD
+       return (input - 1) * 4096 / (output - 1);
+#else
+       if (output > input)
+               return input * 4096 / output;
+       else
+               return (input - 1) * 4096 / (output - 1);
+#endif
+}
+
+int rcar_vin_uds_set(struct rcar_vin_priv *priv, struct rcar_vin_cam *cam)
+{
+       struct rcar_vin_uds_regs regs;
+       unsigned long ratio_h, ratio_v;
+       unsigned long bwidth_h, bwidth_v;
+       unsigned long ctrl;
+       unsigned long clip_size;
+       struct v4l2_rect *cam_subrect = &cam->subrect;
+       u32 vnmc;
+
+       ratio_h = rcar_vin_compute_ratio(cam_subrect->width, cam->out_width);
+       ratio_v = rcar_vin_compute_ratio(cam_subrect->height, cam->out_height);
+
+       bwidth_h = rcar_vin_get_bwidth(ratio_h);
+       bwidth_v = rcar_vin_get_bwidth(ratio_v);
+
+       ctrl = VNUDS_CTRL_AMD;
+       clip_size = (cam->out_width << 16) | cam->out_height;
+
+       regs.ctrl = ctrl;
+       regs.scale = (ratio_h << 16) | ratio_v;
+       regs.pass_bwidth = (bwidth_h << 16) | bwidth_v;
+       regs.clip_size = clip_size;
+
+       vnmc = ioread32(priv->base + VNMC_REG);
+       iowrite32(vnmc | VNMC_SCLE, priv->base + VNMC_REG);
+       iowrite32(regs.ctrl, priv->base + VNUDS_CTRL_REG);
+       iowrite32(regs.scale, priv->base + VNUDS_SCALE_REG);
+       iowrite32(regs.pass_bwidth, priv->base + VNUDS_PASS_BWIDTH_REG);
+       iowrite32(regs.clip_size, priv->base + VNUDS_CLIP_SIZE_REG);
+
+       return 0;
+}
+
 static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
 {
        int i;
@@ -1037,6 +1129,7 @@ static int rcar_vin_set_rect(struct soc_camera_device 
*icd)
        unsigned char dsize = 0;
        struct v4l2_rect *cam_subrect = &cam->subrect;
        u32 value;
+       int ret;
 
        dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
                icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
@@ -1073,48 +1166,60 @@ static int rcar_vin_set_rect(struct soc_camera_device 
*icd)
                break;
        }
 
-       /* Set scaling coefficient */
-       value = 0;
-       if (cam_subrect->height != cam->out_height)
-               value = (4096 * cam_subrect->height) / cam->out_height;
-       dev_dbg(icd->parent, "YS Value: %x\n", value);
-       iowrite32(value, priv->base + VNYS_REG);
-
-       value = 0;
-       if (cam_subrect->width != cam->out_width)
-               value = (4096 * cam_subrect->width) / cam->out_width;
+       if (priv->chip == RCAR_GEN3 && is_scaling(cam) {
+               ret = rcar_vin_uds_set(priv, cam);
+               if (ret < 0)
+                       return ret;
+               iowrite32(ALIGN(cam->out_width, 0x20), priv->base + VNIS_REG);
+       } else {
+               /* Set scaling coefficient */
+               value = 0;
+               if (cam_subrect->height != cam->out_height)
+                       value = (4096 * cam_subrect->height) / cam->out_height;
+               dev_dbg(icd->parent, "YS Value: %x\n", value);
+               iowrite32(value, priv->base + VNYS_REG);
 
-       /* Horizontal upscaling is up to double size */
-       if (0 < value && value < 2048)
-               value = 2048;
+               value = 0;
+               if (cam_subrect->width != cam->out_width)
+                       value = (4096 * cam_subrect->width) / cam->out_width;
 
-       dev_dbg(icd->parent, "XS Value: %x\n", value);
-       iowrite32(value, priv->base + VNXS_REG);
+               /* Horizontal upscaling is up to double size */
+               if (value > 0 && value < 2048)
+                       value = 2048;
 
-       /* Horizontal upscaling is carried out by scaling down from double size 
*/
-       if (value < 4096)
-               value *= 2;
+               dev_dbg(icd->parent, "XS Value: %x\n", value);
+               iowrite32(value, priv->base + VNXS_REG);
 
-       set_coeff(priv, value);
+               /*
+                * Horizontal upscaling is carried out
+                * by scaling down from double size
+                */
+               if (value < 4096)
+                       value *= 2;
+
+               set_coeff(priv, value);
+
+               /* Set Start/End Pixel/Line Post-Clip */
+               iowrite32(0, priv->base + VNSPPOC_REG);
+               iowrite32(0, priv->base + VNSLPOC_REG);
+               iowrite32((cam->out_width - 1) << dsize,
+                       priv->base + VNEPPOC_REG);
+               switch (priv->field) {
+               case V4L2_FIELD_INTERLACED:
+               case V4L2_FIELD_INTERLACED_TB:
+               case V4L2_FIELD_INTERLACED_BT:
+                       iowrite32(cam->out_height / 2 - 1,
+                                 priv->base + VNELPOC_REG);
+                       break;
+               default:
+                       iowrite32(cam->out_height - 1,
+                               priv->base + VNELPOC_REG);
+                       break;
+               }
 
-       /* Set Start/End Pixel/Line Post-Clip */
-       iowrite32(0, priv->base + VNSPPOC_REG);
-       iowrite32(0, priv->base + VNSLPOC_REG);
-       iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG);
-       switch (priv->field) {
-       case V4L2_FIELD_INTERLACED:
-       case V4L2_FIELD_INTERLACED_TB:
-       case V4L2_FIELD_INTERLACED_BT:
-               iowrite32(cam->out_height / 2 - 1,
-                         priv->base + VNELPOC_REG);
-               break;
-       default:
-               iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG);
-               break;
+               iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
        }
 
-       iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
-
        return 0;
 }
 
-- 
1.9.1

Reply via email to