mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-24 21:24:00 +08:00
4d346be55d
The RPF source memory pointers need to be offset to take the crop rectangle into account. Offsets are computed in the RPF stream start, which can happen (when using the DRM pipeline) after calling the RPF .set_memory() operation that programs the buffer addresses. The .set_memory() operation tries to guard against the problem by skipping programming of the registers when the module isn't streaming. This will however only protect the first use of an RPF in a DRM pipeline, as in all subsequent uses the module streaming flag will be set and the .set_memory() operation will use potentially incorrect offsets. Fix this by allowing the caller to decide whether to program the hardware immediately or just cache the addresses. While at it refactor the memory set code and create a new vsp1_rwpf_set_memory() that cache addresses and calls the .set_memory() operation to apply them to the hardware. As a side effect the driver now writes all three DMA address registers regardless of the number of planes, and initializes unused addresses to zero. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
294 lines
7.7 KiB
C
294 lines
7.7 KiB
C
/*
|
|
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
|
|
*
|
|
* Copyright (C) 2013-2014 Renesas Electronics Corporation
|
|
*
|
|
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
#include "vsp1.h"
|
|
#include "vsp1_rwpf.h"
|
|
#include "vsp1_video.h"
|
|
|
|
#define RWPF_MIN_WIDTH 1
|
|
#define RWPF_MIN_HEIGHT 1
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* V4L2 Subdevice Pad Operations
|
|
*/
|
|
|
|
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_mbus_code_enum *code)
|
|
{
|
|
static const unsigned int codes[] = {
|
|
MEDIA_BUS_FMT_ARGB8888_1X32,
|
|
MEDIA_BUS_FMT_AYUV8_1X32,
|
|
};
|
|
|
|
if (code->index >= ARRAY_SIZE(codes))
|
|
return -EINVAL;
|
|
|
|
code->code = codes[code->index];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_frame_size_enum *fse)
|
|
{
|
|
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
|
|
struct v4l2_mbus_framefmt *format;
|
|
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fse->pad,
|
|
fse->which);
|
|
|
|
if (fse->index || fse->code != format->code)
|
|
return -EINVAL;
|
|
|
|
if (fse->pad == RWPF_PAD_SINK) {
|
|
fse->min_width = RWPF_MIN_WIDTH;
|
|
fse->max_width = rwpf->max_width;
|
|
fse->min_height = RWPF_MIN_HEIGHT;
|
|
fse->max_height = rwpf->max_height;
|
|
} else {
|
|
/* The size on the source pad are fixed and always identical to
|
|
* the size on the sink pad.
|
|
*/
|
|
fse->min_width = format->width;
|
|
fse->max_width = format->width;
|
|
fse->min_height = format->height;
|
|
fse->max_height = format->height;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct v4l2_rect *
|
|
vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_pad_config *cfg, u32 which)
|
|
{
|
|
switch (which) {
|
|
case V4L2_SUBDEV_FORMAT_TRY:
|
|
return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, cfg, RWPF_PAD_SINK);
|
|
case V4L2_SUBDEV_FORMAT_ACTIVE:
|
|
return &rwpf->crop;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
|
|
|
|
fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
|
|
fmt->which);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_format *fmt)
|
|
{
|
|
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
|
|
struct v4l2_mbus_framefmt *format;
|
|
struct v4l2_rect *crop;
|
|
|
|
/* Default to YUV if the requested format is not supported. */
|
|
if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
|
|
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
|
|
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
|
|
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, fmt->pad,
|
|
fmt->which);
|
|
|
|
if (fmt->pad == RWPF_PAD_SOURCE) {
|
|
/* The RWPF performs format conversion but can't scale, only the
|
|
* format code can be changed on the source pad.
|
|
*/
|
|
format->code = fmt->format.code;
|
|
fmt->format = *format;
|
|
return 0;
|
|
}
|
|
|
|
format->code = fmt->format.code;
|
|
format->width = clamp_t(unsigned int, fmt->format.width,
|
|
RWPF_MIN_WIDTH, rwpf->max_width);
|
|
format->height = clamp_t(unsigned int, fmt->format.height,
|
|
RWPF_MIN_HEIGHT, rwpf->max_height);
|
|
format->field = V4L2_FIELD_NONE;
|
|
format->colorspace = V4L2_COLORSPACE_SRGB;
|
|
|
|
fmt->format = *format;
|
|
|
|
/* Update the sink crop rectangle. */
|
|
crop = vsp1_rwpf_get_crop(rwpf, cfg, fmt->which);
|
|
crop->left = 0;
|
|
crop->top = 0;
|
|
crop->width = fmt->format.width;
|
|
crop->height = fmt->format.height;
|
|
|
|
/* Propagate the format to the source pad. */
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
|
|
fmt->which);
|
|
*format = fmt->format;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_selection *sel)
|
|
{
|
|
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
|
|
struct v4l2_mbus_framefmt *format;
|
|
|
|
/* Cropping is implemented on the sink pad. */
|
|
if (sel->pad != RWPF_PAD_SINK)
|
|
return -EINVAL;
|
|
|
|
switch (sel->target) {
|
|
case V4L2_SEL_TGT_CROP:
|
|
sel->r = *vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
|
|
break;
|
|
|
|
case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg,
|
|
RWPF_PAD_SINK, sel->which);
|
|
sel->r.left = 0;
|
|
sel->r.top = 0;
|
|
sel->r.width = format->width;
|
|
sel->r.height = format->height;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
struct v4l2_subdev_selection *sel)
|
|
{
|
|
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
|
|
struct v4l2_mbus_framefmt *format;
|
|
struct v4l2_rect *crop;
|
|
|
|
/* Cropping is implemented on the sink pad. */
|
|
if (sel->pad != RWPF_PAD_SINK)
|
|
return -EINVAL;
|
|
|
|
if (sel->target != V4L2_SEL_TGT_CROP)
|
|
return -EINVAL;
|
|
|
|
/* Make sure the crop rectangle is entirely contained in the image. The
|
|
* WPF top and left offsets are limited to 255.
|
|
*/
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK,
|
|
sel->which);
|
|
|
|
/* Restrict the crop rectangle coordinates to multiples of 2 to avoid
|
|
* shifting the color plane.
|
|
*/
|
|
if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
|
|
sel->r.left = ALIGN(sel->r.left, 2);
|
|
sel->r.top = ALIGN(sel->r.top, 2);
|
|
sel->r.width = round_down(sel->r.width, 2);
|
|
sel->r.height = round_down(sel->r.height, 2);
|
|
}
|
|
|
|
sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
|
|
sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
|
|
if (rwpf->entity.type == VSP1_ENTITY_WPF) {
|
|
sel->r.left = min_t(unsigned int, sel->r.left, 255);
|
|
sel->r.top = min_t(unsigned int, sel->r.top, 255);
|
|
}
|
|
sel->r.width = min_t(unsigned int, sel->r.width,
|
|
format->width - sel->r.left);
|
|
sel->r.height = min_t(unsigned int, sel->r.height,
|
|
format->height - sel->r.top);
|
|
|
|
crop = vsp1_rwpf_get_crop(rwpf, cfg, sel->which);
|
|
*crop = sel->r;
|
|
|
|
/* Propagate the format to the source pad. */
|
|
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SOURCE,
|
|
sel->which);
|
|
format->width = crop->width;
|
|
format->height = crop->height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Controls
|
|
*/
|
|
|
|
static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct vsp1_rwpf *rwpf =
|
|
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_ALPHA_COMPONENT:
|
|
rwpf->alpha = ctrl->val;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
|
|
.s_ctrl = vsp1_rwpf_s_ctrl,
|
|
};
|
|
|
|
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf)
|
|
{
|
|
rwpf->alpha = 255;
|
|
|
|
v4l2_ctrl_handler_init(&rwpf->ctrls, 1);
|
|
v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
|
|
V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
|
|
|
|
rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
|
|
|
|
return rwpf->ctrls.error;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Buffers
|
|
*/
|
|
|
|
/**
|
|
* vsp1_rwpf_set_memory - Configure DMA addresses for a [RW]PF
|
|
* @rwpf: the [RW]PF instance
|
|
* @mem: DMA memory addresses
|
|
* @apply: whether to apply the configuration to the hardware
|
|
*
|
|
* This function stores the DMA addresses for all planes in the rwpf instance
|
|
* and optionally applies the configuration to hardware registers if the apply
|
|
* argument is set to true.
|
|
*/
|
|
void vsp1_rwpf_set_memory(struct vsp1_rwpf *rwpf, struct vsp1_rwpf_memory *mem,
|
|
bool apply)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
rwpf->buf_addr[i] = mem->addr[i];
|
|
|
|
if (apply)
|
|
rwpf->ops->set_memory(rwpf);
|
|
}
|