From: Yoshihiko Mori <yoshihiko.mori.nx@xxxxxxxxxxx> Add UDS control for R-Car Gen3. Up down scaler can be vertical and horizontal scaling. Signed-off-by: Yoshihiko Mori <yoshihiko.mori.nx@xxxxxxxxxxx> Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@xxxxxxxxxxx> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@xxxxxxxxx> --- 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