Merge branch 'topic/vsp1' into patchwork

* topic/vsp1: (36 commits)
  [media] v4l: vsp1: wpf: Add flipping support
  [media] v4l: vsp1: rwpf: Support runtime modification of controls
  [media] v4l: vsp1: Simplify alpha propagation
  [media] v4l: vsp1: clu: Support runtime modification of controls
  [media] v4l: vsp1: lut: Support runtime modification of controls
  [media] v4l: vsp1: Support runtime modification of controls
  [media] v4l: vsp1: Add Cubic Look Up Table (CLU) support
  [media] v4l: vsp1: lut: Expose configuration through a control
  [media] v4l: vsp1: lut: Initialize the mutex
  [media] v4l: vsp1: dl: Don't free fragments with interrupts disabled
  [media] v4l: vsp1: Set entities functions
  [media] v4l: vsp1: Don't create LIF entity when the userspace API is enabled
  [media] v4l: vsp1: Don't register media device when userspace API is disabled
  [media] v4l: vsp1: Base link creation on availability of entities
  [media] media: Add video statistics computation functions
  [media] media: Add video processing entity functions
  [media] v4l: vsp1: sru: Fix intensity control ID
  [media] v4l: vsp1: Stop the pipeline upon the first STREAMOFF
  [media] v4l: vsp1: Constify operation structures
  [media] v4l: vsp1: pipe: Fix typo in comment
  ...
This commit is contained in:
Mauro Carvalho Chehab 2016-07-13 13:43:48 -03:00
commit c9a3429bcc
39 changed files with 1389 additions and 385 deletions

View File

@ -121,6 +121,70 @@
<entry><constant>MEDIA_ENT_F_AUDIO_MIXER</constant></entry>
<entry>Audio Mixer Function Entity.</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_COMPOSER</constant></entry>
<entry>Video composer (blender). An entity capable of video
composing must have at least two sink pads and one source
pad, and composes input video frames onto output video
frames. Composition can be performed using alpha blending,
color keying, raster operations (ROP), stitching or any other
means.
</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER</constant></entry>
<entry>Video pixel formatter. An entity capable of pixel formatting
must have at least one sink pad and one source pad. Read
pixel formatters read pixels from memory and perform a subset
of unpacking, cropping, color keying, alpha multiplication
and pixel encoding conversion. Write pixel formatters perform
a subset of dithering, pixel encoding conversion and packing
and write pixels to memory.
</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV</constant></entry>
<entry>Video pixel encoding converter. An entity capable of pixel
enconding conversion must have at least one sink pad and one
source pad, and convert the encoding of pixels received on
its sink pad(s) to a different encoding output on its source
pad(s). Pixel encoding conversion includes but isn't limited
to RGB to/from HSV, RGB to/from YUV and CFA (Bayer) to RGB
conversions.
</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_LUT</constant></entry>
<entry>Video look-up table. An entity capable of video lookup table
processing must have one sink pad and one source pad. It uses
the values of the pixels received on its sink pad to look up
entries in internal tables and output them on its source pad.
The lookup processing can be performed on all components
separately or combine them for multi-dimensional table
lookups.
</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_SCALER</constant></entry>
<entry>Video scaler. An entity capable of video scaling must have
at least one sink pad and one source pad, and scale the
video frame(s) received on its sink pad(s) to a different
resolution output on its source pad(s). The range of
supported scaling ratios is entity-specific and can differ
between the horizontal and vertical directions (in particular
scaling can be supported in one direction only). Binning and
skipping are considered as scaling.
</entry>
</row>
<row>
<entry><constant>MEDIA_ENT_F_PROC_VIDEO_STATISTICS</constant></entry>
<entry>Video statistics computation (histogram, 3A, ...). An entity
capable of statistics computation must have one sink pad and
one source pad. It computes statistics over the frames
received on its sink pad and outputs the statistics data on
its source pad.
</entry>
</row>
</tbody>
</tgroup>
</table>

View File

@ -0,0 +1,32 @@
Renesas R-Car Frame Compression Processor (FCP)
-----------------------------------------------
The FCP is a companion module of video processing modules in the Renesas R-Car
Gen3 SoCs. It provides data compression and decompression, data caching, and
conversion of AXI transactions in order to reduce the memory bandwidth.
There are three types of FCP: FCP for Codec (FCPC), FCP for VSP (FCPV) and FCP
for FDP (FCPF). Their configuration and behaviour depend on the module they
are paired with. These DT bindings currently support the FCPV only.
- compatible: Must be one or more of the following
- "renesas,r8a7795-fcpv" for R8A7795 (R-Car H3) compatible 'FCP for VSP'
- "renesas,fcpv" for generic compatible 'FCP for VSP'
When compatible with the generic version, nodes must list the
SoC-specific version corresponding to the platform first, followed by the
family-specific and/or generic versions.
- reg: the register base and size for the device registers
- clocks: Reference to the functional clock
Device node example
-------------------
fcpvd1: fcp@fea2f000 {
compatible = "renesas,r8a7795-fcpv", "renesas,fcpv";
reg = <0 0xfea2f000 0 0x200>;
clocks = <&cpg CPG_MOD 602>;
};

View File

@ -14,6 +14,11 @@ Required properties:
- interrupts: VSP interrupt specifier.
- clocks: A phandle + clock-specifier pair for the VSP functional clock.
Optional properties:
- renesas,fcp: A phandle referencing the FCP that handles memory accesses
for the VSP. Not needed on Gen2, mandatory on Gen3.
Example: R8A7790 (R-Car H2) VSP1-S node

View File

@ -7343,6 +7343,16 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/potentiometer/mcp4531.c
MEDIA DRIVERS FOR RENESAS - FCP
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: linux-media@vger.kernel.org
L: linux-renesas-soc@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
S: Supported
F: Documentation/devicetree/bindings/media/renesas,fcp.txt
F: drivers/media/platform/rcar-fcp.c
F: include/media/rcar-fcp.h
MEDIA DRIVERS FOR RENESAS - VSP1
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: linux-media@vger.kernel.org

View File

@ -148,40 +148,39 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
struct rcar_du_vsp_plane_state *state =
to_rcar_vsp_plane_state(plane->plane.state);
struct drm_framebuffer *fb = plane->plane.state->fb;
struct v4l2_rect src;
struct v4l2_rect dst;
dma_addr_t paddr[2] = { 0, };
u32 pixelformat = 0;
struct vsp1_du_atomic_config cfg = {
.pixelformat = 0,
.pitch = fb->pitches[0],
.alpha = state->alpha,
.zpos = state->zpos,
};
unsigned int i;
src.left = state->state.src_x >> 16;
src.top = state->state.src_y >> 16;
src.width = state->state.src_w >> 16;
src.height = state->state.src_h >> 16;
cfg.src.left = state->state.src_x >> 16;
cfg.src.top = state->state.src_y >> 16;
cfg.src.width = state->state.src_w >> 16;
cfg.src.height = state->state.src_h >> 16;
dst.left = state->state.crtc_x;
dst.top = state->state.crtc_y;
dst.width = state->state.crtc_w;
dst.height = state->state.crtc_h;
cfg.dst.left = state->state.crtc_x;
cfg.dst.top = state->state.crtc_y;
cfg.dst.width = state->state.crtc_w;
cfg.dst.height = state->state.crtc_h;
for (i = 0; i < state->format->planes; ++i) {
struct drm_gem_cma_object *gem;
gem = drm_fb_cma_get_gem_obj(fb, i);
paddr[i] = gem->paddr + fb->offsets[i];
cfg.mem[i] = gem->paddr + fb->offsets[i];
}
for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
if (formats_kms[i] == state->format->fourcc) {
pixelformat = formats_v4l2[i];
cfg.pixelformat = formats_v4l2[i];
break;
}
}
WARN_ON(!pixelformat);
vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat,
fb->pitches[0], paddr, &src, &dst);
vsp1_du_atomic_update(plane->vsp->vsp, plane->index, &cfg);
}
static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
@ -220,8 +219,7 @@ static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
if (plane->state->crtc)
rcar_du_vsp_plane_setup(rplane);
else
vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0,
NULL, NULL);
vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, NULL);
}
static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
@ -269,6 +267,7 @@ static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
return;
state->alpha = 255;
state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
plane->state = &state->state;
plane->state->plane = plane;
@ -283,6 +282,8 @@ static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane,
if (property == rcdu->props.alpha)
rstate->alpha = val;
else if (property == rcdu->props.zpos)
rstate->zpos = val;
else
return -EINVAL;
@ -299,6 +300,8 @@ static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane,
if (property == rcdu->props.alpha)
*val = rstate->alpha;
else if (property == rcdu->props.zpos)
*val = rstate->zpos;
else
return -EINVAL;
@ -378,6 +381,8 @@ int rcar_du_vsp_init(struct rcar_du_vsp *vsp)
drm_object_attach_property(&plane->plane.base,
rcdu->props.alpha, 255);
drm_object_attach_property(&plane->plane.base,
rcdu->props.zpos, 1);
}
return 0;

View File

@ -44,6 +44,7 @@ static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p)
* @state: base DRM plane state
* @format: information about the pixel format used by the plane
* @alpha: value of the plane alpha property
* @zpos: value of the plane zpos property
*/
struct rcar_du_vsp_plane_state {
struct drm_plane_state state;
@ -51,6 +52,7 @@ struct rcar_du_vsp_plane_state {
const struct rcar_du_format_info *format;
unsigned int alpha;
unsigned int zpos;
};
static inline struct rcar_du_vsp_plane_state *

View File

@ -278,10 +278,24 @@ config VIDEO_RENESAS_JPU
To compile this driver as a module, choose M here: the module
will be called rcar_jpu.
config VIDEO_RENESAS_FCP
tristate "Renesas Frame Compression Processor"
depends on ARCH_RENESAS || COMPILE_TEST
depends on OF
---help---
This is a driver for the Renesas Frame Compression Processor (FCP).
The FCP is a companion module of video processing modules in the
Renesas R-Car Gen3 SoCs. It handles memory access for the codec,
VSP and FDP modules.
To compile this driver as a module, choose M here: the module
will be called rcar-fcp.
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
depends on (ARCH_RENESAS && OF) || COMPILE_TEST
depends on !ARM64 || VIDEO_RENESAS_FCP
select VIDEOBUF2_DMA_CONTIG
---help---
This is a V4L2 driver for the Renesas VSP1 video processing engine.

View File

@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/

View File

@ -0,0 +1,181 @@
/*
* rcar-fcp.c -- R-Car Frame Compression Processor Driver
*
* Copyright (C) 2016 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 <linux/device.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <media/rcar-fcp.h>
struct rcar_fcp_device {
struct list_head list;
struct device *dev;
};
static LIST_HEAD(fcp_devices);
static DEFINE_MUTEX(fcp_lock);
/* -----------------------------------------------------------------------------
* Public API
*/
/**
* rcar_fcp_get - Find and acquire a reference to an FCP instance
* @np: Device node of the FCP instance
*
* Search the list of registered FCP instances for the instance corresponding to
* the given device node.
*
* Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be
* found.
*/
struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
{
struct rcar_fcp_device *fcp;
mutex_lock(&fcp_lock);
list_for_each_entry(fcp, &fcp_devices, list) {
if (fcp->dev->of_node != np)
continue;
/*
* Make sure the module won't be unloaded behind our back. This
* is a poor man's safety net, the module should really not be
* unloaded while FCP users can be active.
*/
if (!try_module_get(fcp->dev->driver->owner))
fcp = NULL;
goto done;
}
fcp = ERR_PTR(-EPROBE_DEFER);
done:
mutex_unlock(&fcp_lock);
return fcp;
}
EXPORT_SYMBOL_GPL(rcar_fcp_get);
/**
* rcar_fcp_put - Release a reference to an FCP instance
* @fcp: The FCP instance
*
* Release the FCP instance acquired by a call to rcar_fcp_get().
*/
void rcar_fcp_put(struct rcar_fcp_device *fcp)
{
if (fcp)
module_put(fcp->dev->driver->owner);
}
EXPORT_SYMBOL_GPL(rcar_fcp_put);
/**
* rcar_fcp_enable - Enable an FCP
* @fcp: The FCP instance
*
* Before any memory access through an FCP is performed by a module, the FCP
* must be enabled by a call to this function. The enable calls are reference
* counted, each successful call must be followed by one rcar_fcp_disable()
* call when no more memory transfer can occur through the FCP.
*
* Return 0 on success or a negative error code if an error occurs. The enable
* reference count isn't increased when this function returns an error.
*/
int rcar_fcp_enable(struct rcar_fcp_device *fcp)
{
if (!fcp)
return 0;
return pm_runtime_get_sync(fcp->dev);
}
EXPORT_SYMBOL_GPL(rcar_fcp_enable);
/**
* rcar_fcp_disable - Disable an FCP
* @fcp: The FCP instance
*
* This function is the counterpart of rcar_fcp_enable(). As enable calls are
* reference counted a disable call may not disable the FCP synchronously.
*/
void rcar_fcp_disable(struct rcar_fcp_device *fcp)
{
if (fcp)
pm_runtime_put(fcp->dev);
}
EXPORT_SYMBOL_GPL(rcar_fcp_disable);
/* -----------------------------------------------------------------------------
* Platform Driver
*/
static int rcar_fcp_probe(struct platform_device *pdev)
{
struct rcar_fcp_device *fcp;
fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL);
if (fcp == NULL)
return -ENOMEM;
fcp->dev = &pdev->dev;
pm_runtime_enable(&pdev->dev);
mutex_lock(&fcp_lock);
list_add_tail(&fcp->list, &fcp_devices);
mutex_unlock(&fcp_lock);
platform_set_drvdata(pdev, fcp);
return 0;
}
static int rcar_fcp_remove(struct platform_device *pdev)
{
struct rcar_fcp_device *fcp = platform_get_drvdata(pdev);
mutex_lock(&fcp_lock);
list_del(&fcp->list);
mutex_unlock(&fcp_lock);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id rcar_fcp_of_match[] = {
{ .compatible = "renesas,fcpv" },
{ },
};
static struct platform_driver rcar_fcp_platform_driver = {
.probe = rcar_fcp_probe,
.remove = rcar_fcp_remove,
.driver = {
.name = "rcar-fcp",
.of_match_table = rcar_fcp_of_match,
.suppress_bind_attrs = true,
},
};
module_platform_driver(rcar_fcp_platform_driver);
MODULE_ALIAS("rcar-fcp");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Renesas FCP Driver");
MODULE_LICENSE("GPL");

View File

@ -1,7 +1,8 @@
vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o
vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o
vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_lif.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o

View File

@ -25,11 +25,13 @@
struct clk;
struct device;
struct rcar_fcp_device;
struct vsp1_drm;
struct vsp1_entity;
struct vsp1_platform_data;
struct vsp1_bru;
struct vsp1_clu;
struct vsp1_hsit;
struct vsp1_lif;
struct vsp1_lut;
@ -45,6 +47,9 @@ struct vsp1_uds;
#define VSP1_HAS_LUT (1 << 1)
#define VSP1_HAS_SRU (1 << 2)
#define VSP1_HAS_BRU (1 << 3)
#define VSP1_HAS_CLU (1 << 4)
#define VSP1_HAS_WPF_VFLIP (1 << 5)
#define VSP1_HAS_WPF_HFLIP (1 << 6)
struct vsp1_device_info {
u32 version;
@ -62,12 +67,10 @@ struct vsp1_device {
const struct vsp1_device_info *info;
void __iomem *mmio;
struct clk *clock;
struct mutex lock;
int ref_count;
struct rcar_fcp_device *fcp;
struct vsp1_bru *bru;
struct vsp1_clu *clu;
struct vsp1_hsit *hsi;
struct vsp1_hsit *hst;
struct vsp1_lif *lif;

View File

@ -249,7 +249,7 @@ static int bru_set_selection(struct v4l2_subdev *subdev,
return 0;
}
static struct v4l2_subdev_pad_ops bru_pad_ops = {
static const struct v4l2_subdev_pad_ops bru_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = bru_enum_mbus_code,
.enum_frame_size = bru_enum_frame_size,
@ -259,7 +259,7 @@ static struct v4l2_subdev_pad_ops bru_pad_ops = {
.set_selection = bru_set_selection,
};
static struct v4l2_subdev_ops bru_ops = {
static const struct v4l2_subdev_ops bru_ops = {
.pad = &bru_pad_ops,
};
@ -269,13 +269,16 @@ static struct v4l2_subdev_ops bru_ops = {
static void bru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_bru *bru = to_bru(&entity->subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
if (!full)
return;
format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config,
bru->entity.source_pad);
@ -390,7 +393,8 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
bru->entity.type = VSP1_ENTITY_BRU;
ret = vsp1_entity_init(vsp1, &bru->entity, "bru",
vsp1->info->num_bru_inputs + 1, &bru_ops);
vsp1->info->num_bru_inputs + 1, &bru_ops,
MEDIA_ENT_F_PROC_VIDEO_COMPOSER);
if (ret < 0)
return ERR_PTR(ret);

View File

@ -0,0 +1,292 @@
/*
* vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table
*
* Copyright (C) 2015-2016 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 <linux/device.h>
#include <linux/slab.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_clu.h"
#include "vsp1_dl.h"
#define CLU_MIN_SIZE 4U
#define CLU_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl,
u32 reg, u32 data)
{
vsp1_dl_list_write(dl, reg, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
#define V4L2_CID_VSP1_CLU_TABLE (V4L2_CID_USER_BASE | 0x1001)
#define V4L2_CID_VSP1_CLU_MODE (V4L2_CID_USER_BASE | 0x1002)
#define V4L2_CID_VSP1_CLU_MODE_2D 0
#define V4L2_CID_VSP1_CLU_MODE_3D 1
static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
{
struct vsp1_dl_body *dlb;
unsigned int i;
dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17);
if (!dlb)
return -ENOMEM;
vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0);
for (i = 0; i < 17 * 17 * 17; ++i)
vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
spin_lock_irq(&clu->lock);
swap(clu->clu, dlb);
spin_unlock_irq(&clu->lock);
vsp1_dl_fragment_free(dlb);
return 0;
}
static int clu_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_clu *clu =
container_of(ctrl->handler, struct vsp1_clu, ctrls);
switch (ctrl->id) {
case V4L2_CID_VSP1_CLU_TABLE:
clu_set_table(clu, ctrl);
break;
case V4L2_CID_VSP1_CLU_MODE:
clu->mode = ctrl->val;
break;
}
return 0;
}
static const struct v4l2_ctrl_ops clu_ctrl_ops = {
.s_ctrl = clu_s_ctrl,
};
static const struct v4l2_ctrl_config clu_table_control = {
.ops = &clu_ctrl_ops,
.id = V4L2_CID_VSP1_CLU_TABLE,
.name = "Look-Up Table",
.type = V4L2_CTRL_TYPE_U32,
.min = 0x00000000,
.max = 0x00ffffff,
.step = 1,
.def = 0,
.dims = { 17, 17, 17 },
};
static const char * const clu_mode_menu[] = {
"2D",
"3D",
NULL,
};
static const struct v4l2_ctrl_config clu_mode_control = {
.ops = &clu_ctrl_ops,
.id = V4L2_CID_VSP1_CLU_MODE,
.name = "Mode",
.type = V4L2_CTRL_TYPE_MENU,
.min = 0,
.max = 1,
.def = 1,
.qmenu = clu_mode_menu,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int clu_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_AHSV8888_1X32,
MEDIA_BUS_FMT_AYUV8_1X32,
};
return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes,
ARRAY_SIZE(codes));
}
static int clu_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
return vsp1_subdev_enum_frame_size(subdev, cfg, fse, CLU_MIN_SIZE,
CLU_MIN_SIZE, CLU_MAX_SIZE,
CLU_MAX_SIZE);
}
static int clu_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct vsp1_clu *clu = to_clu(subdev);
struct v4l2_subdev_pad_config *config;
struct v4l2_mbus_framefmt *format;
config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which);
if (!config)
return -EINVAL;
/* 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_AHSV8888_1X32 &&
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad);
if (fmt->pad == CLU_PAD_SOURCE) {
/* The CLU output format can't be modified. */
fmt->format = *format;
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
CLU_MIN_SIZE, CLU_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
CLU_MIN_SIZE, CLU_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&clu->entity, config,
CLU_PAD_SOURCE);
*format = fmt->format;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static const struct v4l2_subdev_pad_ops clu_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = clu_enum_mbus_code,
.enum_frame_size = clu_enum_frame_size,
.get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = clu_set_format,
};
static const struct v4l2_subdev_ops clu_ops = {
.pad = &clu_pad_ops,
};
/* -----------------------------------------------------------------------------
* VSP1 Entity Operations
*/
static void clu_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_clu *clu = to_clu(&entity->subdev);
struct vsp1_dl_body *dlb;
unsigned long flags;
u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
/* The format can't be changed during streaming, only verify it at
* stream start and store the information internally for future partial
* reconfiguration calls.
*/
if (full) {
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&clu->entity,
clu->entity.config,
CLU_PAD_SINK);
clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
return;
}
/* 2D mode can only be used with the YCbCr pixel encoding. */
if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode)
ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D
| VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D
| VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D;
vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl);
spin_lock_irqsave(&clu->lock, flags);
dlb = clu->clu;
clu->clu = NULL;
spin_unlock_irqrestore(&clu->lock, flags);
if (dlb)
vsp1_dl_list_add_fragment(dl, dlb);
}
static const struct vsp1_entity_operations clu_entity_ops = {
.configure = clu_configure,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1)
{
struct vsp1_clu *clu;
int ret;
clu = devm_kzalloc(vsp1->dev, sizeof(*clu), GFP_KERNEL);
if (clu == NULL)
return ERR_PTR(-ENOMEM);
spin_lock_init(&clu->lock);
clu->entity.ops = &clu_entity_ops;
clu->entity.type = VSP1_ENTITY_CLU;
ret = vsp1_entity_init(vsp1, &clu->entity, "clu", 2, &clu_ops,
MEDIA_ENT_F_PROC_VIDEO_LUT);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&clu->ctrls, 2);
v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL);
v4l2_ctrl_new_custom(&clu->ctrls, &clu_mode_control, NULL);
clu->entity.subdev.ctrl_handler = &clu->ctrls;
if (clu->ctrls.error) {
dev_err(vsp1->dev, "clu: failed to initialize controls\n");
ret = clu->ctrls.error;
vsp1_entity_destroy(&clu->entity);
return ERR_PTR(ret);
}
v4l2_ctrl_handler_setup(&clu->ctrls);
return clu;
}

View File

@ -0,0 +1,48 @@
/*
* vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table
*
* Copyright (C) 2015 Renesas 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.
*/
#ifndef __VSP1_CLU_H__
#define __VSP1_CLU_H__
#include <linux/spinlock.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
struct vsp1_dl_body;
#define CLU_PAD_SINK 0
#define CLU_PAD_SOURCE 1
struct vsp1_clu {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
bool yuv_mode;
spinlock_t lock;
unsigned int mode;
struct vsp1_dl_body *clu;
};
static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_clu, entity.subdev);
}
struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1);
#endif /* __VSP1_CLU_H__ */

View File

@ -15,6 +15,7 @@
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "vsp1.h"
#include "vsp1_dl.h"
@ -92,11 +93,13 @@ enum vsp1_dl_mode {
* @index: index of the related WPF
* @mode: display list operation mode (header or headerless)
* @vsp1: the VSP1 device
* @lock: protects the active, queued and pending lists
* @lock: protects the free, active, queued, pending and gc_fragments lists
* @free: array of all free display lists
* @active: list currently being processed (loaded) by hardware
* @queued: list queued to the hardware (written to the DL registers)
* @pending: list waiting to be queued to the hardware
* @gc_work: fragments garbage collector work struct
* @gc_fragments: array of display list fragments waiting to be freed
*/
struct vsp1_dl_manager {
unsigned int index;
@ -108,6 +111,9 @@ struct vsp1_dl_manager {
struct vsp1_dl_list *active;
struct vsp1_dl_list *queued;
struct vsp1_dl_list *pending;
struct work_struct gc_work;
struct list_head gc_fragments;
};
/* -----------------------------------------------------------------------------
@ -262,21 +268,10 @@ static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm)
return dl;
}
static void vsp1_dl_list_free_fragments(struct vsp1_dl_list *dl)
{
struct vsp1_dl_body *dlb, *next;
list_for_each_entry_safe(dlb, next, &dl->fragments, list) {
list_del(&dlb->list);
vsp1_dl_body_cleanup(dlb);
kfree(dlb);
}
}
static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
{
vsp1_dl_body_cleanup(&dl->body0);
vsp1_dl_list_free_fragments(dl);
list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
kfree(dl);
}
@ -311,7 +306,16 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
if (!dl)
return;
vsp1_dl_list_free_fragments(dl);
/* We can't free fragments here as DMA memory can only be freed in
* interruptible context. Move all fragments to the display list
* manager's list of fragments to be freed, they will be
* garbage-collected by the work queue.
*/
if (!list_empty(&dl->fragments)) {
list_splice_init(&dl->fragments, &dl->dlm->gc_fragments);
schedule_work(&dl->dlm->gc_work);
}
dl->body0.num_entries = 0;
list_add_tail(&dl->list, &dl->dlm->free);
@ -550,6 +554,40 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
dlm->pending = NULL;
}
/*
* Free all fragments awaiting to be garbage-collected.
*
* This function must be called without the display list manager lock held.
*/
static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm)
{
unsigned long flags;
spin_lock_irqsave(&dlm->lock, flags);
while (!list_empty(&dlm->gc_fragments)) {
struct vsp1_dl_body *dlb;
dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body,
list);
list_del(&dlb->list);
spin_unlock_irqrestore(&dlm->lock, flags);
vsp1_dl_fragment_free(dlb);
spin_lock_irqsave(&dlm->lock, flags);
}
spin_unlock_irqrestore(&dlm->lock, flags);
}
static void vsp1_dlm_garbage_collect(struct work_struct *work)
{
struct vsp1_dl_manager *dlm =
container_of(work, struct vsp1_dl_manager, gc_work);
vsp1_dlm_fragments_free(dlm);
}
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int index,
unsigned int prealloc)
@ -568,6 +606,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
spin_lock_init(&dlm->lock);
INIT_LIST_HEAD(&dlm->free);
INIT_LIST_HEAD(&dlm->gc_fragments);
INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect);
for (i = 0; i < prealloc; ++i) {
struct vsp1_dl_list *dl;
@ -589,8 +629,12 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm)
if (!dlm)
return;
cancel_work_sync(&dlm->gc_work);
list_for_each_entry_safe(dl, next, &dlm->free, list) {
list_del(&dl->list);
vsp1_dl_list_free(dl);
}
vsp1_dlm_fragments_free(dlm);
}

View File

@ -230,42 +230,33 @@ EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
* vsp1_du_atomic_update - Setup one RPF input of the VSP pipeline
* @dev: the VSP device
* @rpf_index: index of the RPF to setup (0-based)
* @pixelformat: V4L2 pixel format for the RPF memory input
* @pitch: number of bytes per line in the image stored in memory
* @mem: DMA addresses of the memory buffers (one per plane)
* @src: the source crop rectangle for the RPF
* @dst: the destination compose rectangle for the BRU input
* @alpha: global alpha value for the input
* @zpos: the Z-order position of the input
* @cfg: the RPF configuration
*
* Configure the VSP to perform composition of the image referenced by @mem
* through RPF @rpf_index, using the @src crop rectangle and the @dst
* Configure the VSP to perform image composition through RPF @rpf_index as
* described by the @cfg configuration. The image to compose is referenced by
* @cfg.mem and composed using the @cfg.src crop rectangle and the @cfg.dst
* composition rectangle. The Z-order is configurable with higher @zpos values
* displayed on top.
*
* Image format as stored in memory is expressed as a V4L2 @pixelformat value.
* As a special case, setting the pixel format to 0 will disable the RPF. The
* @pitch, @mem, @src and @dst parameters are ignored in that case. Calling the
* If the @cfg configuration is NULL, the RPF will be disabled. Calling the
* function on a disabled RPF is allowed.
*
* The memory pitch is configurable to allow for padding at end of lines, or
* simple for images that extend beyond the crop rectangle boundaries. The
* @pitch value is expressed in bytes and applies to all planes for multiplanar
* formats.
* Image format as stored in memory is expressed as a V4L2 @cfg.pixelformat
* value. The memory pitch is configurable to allow for padding at end of lines,
* or simply for images that extend beyond the crop rectangle boundaries. The
* @cfg.pitch value is expressed in bytes and applies to all planes for
* multiplanar formats.
*
* The source memory buffer is referenced by the DMA address of its planes in
* the @mem array. Up to two planes are supported. The second plane DMA address
* is ignored for formats using a single plane.
* the @cfg.mem array. Up to two planes are supported. The second plane DMA
* address is ignored for formats using a single plane.
*
* This function isn't reentrant, the caller needs to serialize calls.
*
* Return 0 on success or a negative error code on failure.
*/
int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
u32 pixelformat, unsigned int pitch,
dma_addr_t mem[2], const struct v4l2_rect *src,
const struct v4l2_rect *dst, unsigned int alpha,
unsigned int zpos)
int vsp1_du_atomic_update(struct device *dev, unsigned int rpf_index,
const struct vsp1_du_atomic_config *cfg)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
const struct vsp1_format_info *fmtinfo;
@ -276,7 +267,7 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
rpf = vsp1->rpf[rpf_index];
if (pixelformat == 0) {
if (!cfg) {
dev_dbg(vsp1->dev, "%s: RPF%u: disable requested\n", __func__,
rpf_index);
@ -287,38 +278,39 @@ int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf_index,
dev_dbg(vsp1->dev,
"%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad } zpos %u\n",
__func__, rpf_index,
src->left, src->top, src->width, src->height,
dst->left, dst->top, dst->width, dst->height,
pixelformat, pitch, &mem[0], &mem[1], zpos);
cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
cfg->zpos);
/* Store the format, stride, memory buffer address, crop and compose
* rectangles and Z-order position and for the input.
*/
fmtinfo = vsp1_get_format_info(pixelformat);
fmtinfo = vsp1_get_format_info(cfg->pixelformat);
if (!fmtinfo) {
dev_dbg(vsp1->dev, "Unsupport pixel format %08x for RPF\n",
pixelformat);
cfg->pixelformat);
return -EINVAL;
}
rpf->fmtinfo = fmtinfo;
rpf->format.num_planes = fmtinfo->planes;
rpf->format.plane_fmt[0].bytesperline = pitch;
rpf->format.plane_fmt[1].bytesperline = pitch;
rpf->alpha = alpha;
rpf->format.plane_fmt[0].bytesperline = cfg->pitch;
rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
rpf->alpha = cfg->alpha;
rpf->mem.addr[0] = mem[0];
rpf->mem.addr[1] = mem[1];
rpf->mem.addr[0] = cfg->mem[0];
rpf->mem.addr[1] = cfg->mem[1];
rpf->mem.addr[2] = 0;
vsp1->drm->inputs[rpf_index].crop = *src;
vsp1->drm->inputs[rpf_index].compose = *dst;
vsp1->drm->inputs[rpf_index].zpos = zpos;
vsp1->drm->inputs[rpf_index].crop = cfg->src;
vsp1->drm->inputs[rpf_index].compose = cfg->dst;
vsp1->drm->inputs[rpf_index].zpos = cfg->zpos;
vsp1->drm->inputs[rpf_index].enabled = true;
return 0;
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update_ext);
EXPORT_SYMBOL_GPL(vsp1_du_atomic_update);
static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1,
struct vsp1_rwpf *rpf, unsigned int bru_input)
@ -499,8 +491,10 @@ void vsp1_du_atomic_flush(struct device *dev)
vsp1_entity_route_setup(entity, pipe->dl);
if (entity->ops->configure)
entity->ops->configure(entity, pipe, pipe->dl);
if (entity->ops->configure) {
entity->ops->configure(entity, pipe, pipe->dl, true);
entity->ops->configure(entity, pipe, pipe->dl, false);
}
/* The memory buffer address must be applied after configuring
* the RPF to make sure the crop offset are computed.

View File

@ -19,12 +19,15 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/videodev2.h>
#include <media/rcar-fcp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_clu.h"
#include "vsp1_dl.h"
#include "vsp1_drm.h"
#include "vsp1_hsit.h"
@ -145,7 +148,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
return ret;
}
if (vsp1->info->features & VSP1_HAS_LIF) {
if (vsp1->lif) {
ret = media_create_pad_link(&vsp1->wpf[0]->entity.subdev.entity,
RWPF_PAD_SOURCE,
&vsp1->lif->entity.subdev.entity,
@ -168,19 +171,15 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1)
for (i = 0; i < vsp1->info->wpf_count; ++i) {
/* Connect the video device to the WPF. All connections are
* immutable except for the WPF0 source link if a LIF is
* present.
* immutable.
*/
struct vsp1_rwpf *wpf = vsp1->wpf[i];
unsigned int flags = MEDIA_LNK_FL_ENABLED;
if (!(vsp1->info->features & VSP1_HAS_LIF) || i != 0)
flags |= MEDIA_LNK_FL_IMMUTABLE;
ret = media_create_pad_link(&wpf->entity.subdev.entity,
RWPF_PAD_SOURCE,
&wpf->video->video.entity, 0,
flags);
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
if (ret < 0)
return ret;
}
@ -204,7 +203,8 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
}
v4l2_device_unregister(&vsp1->v4l2_dev);
media_device_unregister(&vsp1->media_dev);
if (vsp1->info->uapi)
media_device_unregister(&vsp1->media_dev);
media_device_cleanup(&vsp1->media_dev);
if (!vsp1->info->uapi)
@ -252,6 +252,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
}
if (vsp1->info->features & VSP1_HAS_CLU) {
vsp1->clu = vsp1_clu_create(vsp1);
if (IS_ERR(vsp1->clu)) {
ret = PTR_ERR(vsp1->clu);
goto done;
}
list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
}
vsp1->hsi = vsp1_hsit_create(vsp1, true);
if (IS_ERR(vsp1->hsi)) {
ret = PTR_ERR(vsp1->hsi);
@ -268,7 +278,11 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
if (vsp1->info->features & VSP1_HAS_LIF) {
/* The LIF is only supported when used in conjunction with the DU, in
* which case the userspace API is disabled. If the userspace API is
* enabled skip the LIF, even when present.
*/
if (vsp1->info->features & VSP1_HAS_LIF && !vsp1->info->uapi) {
vsp1->lif = vsp1_lif_create(vsp1);
if (IS_ERR(vsp1->lif)) {
ret = PTR_ERR(vsp1->lif);
@ -379,14 +393,15 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
/* Register subdev nodes if the userspace API is enabled or initialize
* the DRM pipeline otherwise.
*/
if (vsp1->info->uapi)
if (vsp1->info->uapi) {
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
else
ret = vsp1_drm_init(vsp1);
if (ret < 0)
goto done;
if (ret < 0)
goto done;
ret = media_device_register(mdev);
ret = media_device_register(mdev);
} else {
ret = vsp1_drm_init(vsp1);
}
done:
if (ret < 0)
@ -462,35 +477,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
/*
* vsp1_device_get - Acquire the VSP1 device
*
* Increment the VSP1 reference count and initialize the device if the first
* reference is taken.
* Make sure the device is not suspended and initialize it if needed.
*
* Return 0 on success or a negative error code otherwise.
*/
int vsp1_device_get(struct vsp1_device *vsp1)
{
int ret = 0;
int ret;
mutex_lock(&vsp1->lock);
if (vsp1->ref_count > 0)
goto done;
ret = clk_prepare_enable(vsp1->clock);
if (ret < 0)
goto done;
ret = vsp1_device_init(vsp1);
if (ret < 0) {
clk_disable_unprepare(vsp1->clock);
goto done;
}
done:
if (!ret)
vsp1->ref_count++;
mutex_unlock(&vsp1->lock);
return ret;
ret = pm_runtime_get_sync(vsp1->dev);
return ret < 0 ? ret : 0;
}
/*
@ -501,12 +497,7 @@ done:
*/
void vsp1_device_put(struct vsp1_device *vsp1)
{
mutex_lock(&vsp1->lock);
if (--vsp1->ref_count == 0)
clk_disable_unprepare(vsp1->clock);
mutex_unlock(&vsp1->lock);
pm_runtime_put_sync(vsp1->dev);
}
/* -----------------------------------------------------------------------------
@ -518,14 +509,8 @@ static int vsp1_pm_suspend(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
WARN_ON(mutex_is_locked(&vsp1->lock));
if (vsp1->ref_count == 0)
return 0;
vsp1_pipelines_suspend(vsp1);
clk_disable_unprepare(vsp1->clock);
pm_runtime_force_suspend(vsp1->dev);
return 0;
}
@ -534,21 +519,39 @@ static int vsp1_pm_resume(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
WARN_ON(mutex_is_locked(&vsp1->lock));
if (vsp1->ref_count == 0)
return 0;
clk_prepare_enable(vsp1->clock);
pm_runtime_force_resume(vsp1->dev);
vsp1_pipelines_resume(vsp1);
return 0;
}
#endif
static int vsp1_pm_runtime_suspend(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
rcar_fcp_disable(vsp1->fcp);
return 0;
}
static int vsp1_pm_runtime_resume(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
int ret;
if (vsp1->info) {
ret = vsp1_device_init(vsp1);
if (ret < 0)
return ret;
}
return rcar_fcp_enable(vsp1->fcp);
}
static const struct dev_pm_ops vsp1_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
SET_RUNTIME_PM_OPS(vsp1_pm_runtime_suspend, vsp1_pm_runtime_resume, NULL)
};
/* -----------------------------------------------------------------------------
@ -559,7 +562,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
{
.version = VI6_IP_VERSION_MODEL_VSPS_H2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.wpf_count = 4,
@ -568,9 +572,9 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
}, {
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_SRU,
.features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 1,
.uds_count = 3,
.wpf_count = 4,
.num_bru_inputs = 4,
.uapi = true,
@ -580,22 +584,24 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_LUT,
.rpf_count = 4,
.uds_count = 1,
.wpf_count = 4,
.wpf_count = 1,
.num_bru_inputs = 4,
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPS_M2,
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT | VSP1_HAS_SRU,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.uds_count = 1,
.wpf_count = 4,
.num_bru_inputs = 4,
.uapi = true,
}, {
.version = VI6_IP_VERSION_MODEL_VSPI_GEN3,
.gen = 3,
.features = VSP1_HAS_LUT | VSP1_HAS_SRU,
.features = VSP1_HAS_CLU | VSP1_HAS_LUT | VSP1_HAS_SRU
| VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
.rpf_count = 1,
.uds_count = 1,
.wpf_count = 1,
@ -603,7 +609,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
}, {
.version = VI6_IP_VERSION_MODEL_VSPBD_GEN3,
.gen = 3,
.features = VSP1_HAS_BRU,
.features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.wpf_count = 1,
.num_bru_inputs = 5,
@ -611,7 +617,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
}, {
.version = VI6_IP_VERSION_MODEL_VSPBC_GEN3,
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_LUT,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
| VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.wpf_count = 1,
.num_bru_inputs = 5,
@ -619,7 +626,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
}, {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN3,
.gen = 3,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF,
.features = VSP1_HAS_BRU | VSP1_HAS_LIF | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.wpf_count = 2,
.num_bru_inputs = 5,
@ -629,6 +636,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
static int vsp1_probe(struct platform_device *pdev)
{
struct vsp1_device *vsp1;
struct device_node *fcp_node;
struct resource *irq;
struct resource *io;
unsigned int i;
@ -640,22 +648,17 @@ static int vsp1_probe(struct platform_device *pdev)
return -ENOMEM;
vsp1->dev = &pdev->dev;
mutex_init(&vsp1->lock);
INIT_LIST_HEAD(&vsp1->entities);
INIT_LIST_HEAD(&vsp1->videos);
/* I/O, IRQ and clock resources */
platform_set_drvdata(pdev, vsp1);
/* I/O and IRQ resources (clock managed by the clock PM domain) */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
if (IS_ERR(vsp1->mmio))
return PTR_ERR(vsp1->mmio);
vsp1->clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(vsp1->clock)) {
dev_err(&pdev->dev, "failed to get clock\n");
return PTR_ERR(vsp1->clock);
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "missing IRQ\n");
@ -669,13 +672,27 @@ static int vsp1_probe(struct platform_device *pdev)
return ret;
}
/* FCP (optional) */
fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
if (fcp_node) {
vsp1->fcp = rcar_fcp_get(fcp_node);
of_node_put(fcp_node);
if (IS_ERR(vsp1->fcp)) {
dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
PTR_ERR(vsp1->fcp));
return PTR_ERR(vsp1->fcp);
}
}
/* Configure device parameters based on the version register. */
ret = clk_prepare_enable(vsp1->clock);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
return ret;
goto done;
version = vsp1_read(vsp1, VI6_IP_VERSION);
clk_disable_unprepare(vsp1->clock);
pm_runtime_put_sync(&pdev->dev);
for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
if ((version & VI6_IP_VERSION_MODEL_MASK) ==
@ -687,7 +704,8 @@ static int vsp1_probe(struct platform_device *pdev)
if (!vsp1->info) {
dev_err(&pdev->dev, "unsupported IP version 0x%08x\n", version);
return -ENXIO;
ret = -ENXIO;
goto done;
}
dev_dbg(&pdev->dev, "IP version 0x%08x\n", version);
@ -696,12 +714,14 @@ static int vsp1_probe(struct platform_device *pdev)
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create entities\n");
return ret;
goto done;
}
platform_set_drvdata(pdev, vsp1);
done:
if (ret)
pm_runtime_disable(&pdev->dev);
return 0;
return ret;
}
static int vsp1_remove(struct platform_device *pdev)
@ -709,6 +729,9 @@ static int vsp1_remove(struct platform_device *pdev)
struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
vsp1_destroy_entities(vsp1);
rcar_fcp_put(vsp1->fcp);
pm_runtime_disable(&pdev->dev);
return 0;
}

View File

@ -22,6 +22,12 @@
#include "vsp1_dl.h"
#include "vsp1_entity.h"
static inline struct vsp1_entity *
media_entity_to_vsp1_entity(struct media_entity *entity)
{
return container_of(entity, struct vsp1_entity, subdev.entity);
}
void vsp1_entity_route_setup(struct vsp1_entity *source,
struct vsp1_dl_list *dl)
{
@ -30,7 +36,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *source,
if (source->route->reg == 0)
return;
sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
sink = media_entity_to_vsp1_entity(source->sink);
vsp1_dl_list_write(dl, source->route->reg,
sink->route->inputs[source->sink_pad]);
}
@ -81,12 +87,30 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity,
return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
}
/**
* vsp1_entity_get_pad_selection - Get a pad selection from storage for entity
* @entity: the entity
* @cfg: the configuration storage
* @pad: the pad number
* @target: the selection target
*
* Return the selection rectangle stored in the given configuration for an
* entity's pad. The configuration can be an ACTIVE or TRY configuration. The
* selection target can be COMPOSE or CROP.
*/
struct v4l2_rect *
vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad)
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad, unsigned int target)
{
return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad);
switch (target) {
case V4L2_SEL_TGT_COMPOSE:
return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad);
case V4L2_SEL_TGT_CROP:
return v4l2_subdev_get_try_crop(&entity->subdev, cfg, pad);
default:
return NULL;
}
}
/*
@ -252,7 +276,7 @@ int vsp1_entity_link_setup(struct media_entity *entity,
if (!(local->flags & MEDIA_PAD_FL_SOURCE))
return 0;
source = container_of(local->entity, struct vsp1_entity, subdev.entity);
source = media_entity_to_vsp1_entity(local->entity);
if (!source->route)
return 0;
@ -274,33 +298,50 @@ int vsp1_entity_link_setup(struct media_entity *entity,
* Initialization
*/
#define VSP1_ENTITY_ROUTE(ent) \
{ VSP1_ENTITY_##ent, 0, VI6_DPR_##ent##_ROUTE, \
{ VI6_DPR_NODE_##ent }, VI6_DPR_NODE_##ent }
#define VSP1_ENTITY_ROUTE_RPF(idx) \
{ VSP1_ENTITY_RPF, idx, VI6_DPR_RPF_ROUTE(idx), \
{ 0, }, VI6_DPR_NODE_RPF(idx) }
#define VSP1_ENTITY_ROUTE_UDS(idx) \
{ VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \
{ VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) }
#define VSP1_ENTITY_ROUTE_WPF(idx) \
{ VSP1_ENTITY_WPF, idx, 0, \
{ VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
static const struct vsp1_route vsp1_routes[] = {
{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
{ VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3),
VI6_DPR_NODE_BRU_IN(4) } },
{ VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
{ VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
{ VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } },
{ VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { 0, } },
{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { 0, } },
{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { 0, } },
{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { 0, } },
{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { 0, } },
{ VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } },
{ VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } },
{ VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } },
{ VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } },
{ VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } },
{ VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } },
{ VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } },
{ VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } },
VI6_DPR_NODE_BRU_IN(4) }, VI6_DPR_NODE_BRU_OUT },
VSP1_ENTITY_ROUTE(CLU),
VSP1_ENTITY_ROUTE(HSI),
VSP1_ENTITY_ROUTE(HST),
{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, }, VI6_DPR_NODE_LIF },
VSP1_ENTITY_ROUTE(LUT),
VSP1_ENTITY_ROUTE_RPF(0),
VSP1_ENTITY_ROUTE_RPF(1),
VSP1_ENTITY_ROUTE_RPF(2),
VSP1_ENTITY_ROUTE_RPF(3),
VSP1_ENTITY_ROUTE_RPF(4),
VSP1_ENTITY_ROUTE(SRU),
VSP1_ENTITY_ROUTE_UDS(0),
VSP1_ENTITY_ROUTE_UDS(1),
VSP1_ENTITY_ROUTE_UDS(2),
VSP1_ENTITY_ROUTE_WPF(0),
VSP1_ENTITY_ROUTE_WPF(1),
VSP1_ENTITY_ROUTE_WPF(2),
VSP1_ENTITY_ROUTE_WPF(3),
};
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
const char *name, unsigned int num_pads,
const struct v4l2_subdev_ops *ops)
const struct v4l2_subdev_ops *ops, u32 function)
{
struct v4l2_subdev *subdev;
unsigned int i;
@ -341,6 +382,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
subdev = &entity->subdev;
v4l2_subdev_init(subdev, ops);
subdev->entity.function = function;
subdev->entity.ops = &vsp1->media_ops;
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;

View File

@ -24,6 +24,7 @@ struct vsp1_pipeline;
enum vsp1_entity_type {
VSP1_ENTITY_BRU,
VSP1_ENTITY_CLU,
VSP1_ENTITY_HSI,
VSP1_ENTITY_HST,
VSP1_ENTITY_LIF,
@ -42,17 +43,21 @@ enum vsp1_entity_type {
* @index: Entity index this routing entry is associated with
* @reg: Output routing configuration register
* @inputs: Target node value for each input
* @output: Target node value for entity output
*
* Each $vsp1_route entry describes routing configuration for the entity
* specified by the entry's @type and @index. @reg indicates the register that
* holds output routing configuration for the entity, and the @inputs array
* store the target node value for each input of the entity.
* store the target node value for each input of the entity. The @output field
* stores the target node value of the entity output when used as a source for
* histogram generation.
*/
struct vsp1_route {
enum vsp1_entity_type type;
unsigned int index;
unsigned int reg;
unsigned int inputs[VSP1_ENTITY_MAX_INPUTS];
unsigned int output;
};
/**
@ -68,7 +73,7 @@ struct vsp1_entity_operations {
void (*destroy)(struct vsp1_entity *);
void (*set_memory)(struct vsp1_entity *, struct vsp1_dl_list *dl);
void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *,
struct vsp1_dl_list *);
struct vsp1_dl_list *, bool);
};
struct vsp1_entity {
@ -100,7 +105,7 @@ static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
const char *name, unsigned int num_pads,
const struct v4l2_subdev_ops *ops);
const struct v4l2_subdev_ops *ops, u32 function);
void vsp1_entity_destroy(struct vsp1_entity *entity);
extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
@ -118,9 +123,9 @@ vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad);
struct v4l2_rect *
vsp1_entity_get_pad_compose(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad);
vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
struct v4l2_subdev_pad_config *cfg,
unsigned int pad, unsigned int target);
int vsp1_entity_init_cfg(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg);

View File

@ -107,7 +107,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
return 0;
}
static struct v4l2_subdev_pad_ops hsit_pad_ops = {
static const struct v4l2_subdev_pad_ops hsit_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = hsit_enum_mbus_code,
.enum_frame_size = hsit_enum_frame_size,
@ -115,7 +115,7 @@ static struct v4l2_subdev_pad_ops hsit_pad_ops = {
.set_fmt = hsit_set_format,
};
static struct v4l2_subdev_ops hsit_ops = {
static const struct v4l2_subdev_ops hsit_ops = {
.pad = &hsit_pad_ops,
};
@ -125,10 +125,13 @@ static struct v4l2_subdev_ops hsit_ops = {
static void hsit_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_hsit *hsit = to_hsit(&entity->subdev);
if (!full)
return;
if (hsit->inverse)
vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
@ -161,8 +164,9 @@ struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
else
hsit->entity.type = VSP1_ENTITY_HST;
ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst", 2,
&hsit_ops);
ret = vsp1_entity_init(vsp1, &hsit->entity, inverse ? "hsi" : "hst",
2, &hsit_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV);
if (ret < 0)
return ERR_PTR(ret);

View File

@ -104,7 +104,7 @@ static int lif_set_format(struct v4l2_subdev *subdev,
return 0;
}
static struct v4l2_subdev_pad_ops lif_pad_ops = {
static const struct v4l2_subdev_pad_ops lif_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = lif_enum_mbus_code,
.enum_frame_size = lif_enum_frame_size,
@ -112,7 +112,7 @@ static struct v4l2_subdev_pad_ops lif_pad_ops = {
.set_fmt = lif_set_format,
};
static struct v4l2_subdev_ops lif_ops = {
static const struct v4l2_subdev_ops lif_ops = {
.pad = &lif_pad_ops,
};
@ -122,7 +122,7 @@ static struct v4l2_subdev_ops lif_ops = {
static void lif_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(&entity->subdev);
@ -130,6 +130,9 @@ static void lif_configure(struct vsp1_entity *entity,
unsigned int obth = 400;
unsigned int lbth = 200;
if (!full)
return;
format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config,
LIF_PAD_SOURCE);
@ -165,7 +168,12 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
lif->entity.ops = &lif_entity_ops;
lif->entity.type = VSP1_ENTITY_LIF;
ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops);
/* The LIF is never exposed to userspace, but media entity registration
* requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to
* avoid triggering a WARN_ON(), the value won't be seen anywhere.
*/
ret = vsp1_entity_init(vsp1, &lif->entity, "lif", 2, &lif_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
if (ret < 0)
return ERR_PTR(ret);

View File

@ -13,7 +13,6 @@
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/vsp1.h>
#include <media/v4l2-subdev.h>
@ -35,43 +34,62 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl,
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
* Controls
*/
static int lut_set_table(struct vsp1_lut *lut, struct vsp1_lut_config *config)
#define V4L2_CID_VSP1_LUT_TABLE (V4L2_CID_USER_BASE | 0x1001)
static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
{
struct vsp1_dl_body *dlb;
unsigned int i;
dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, ARRAY_SIZE(config->lut));
dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256);
if (!dlb)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(config->lut); ++i)
for (i = 0; i < 256; ++i)
vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i,
config->lut[i]);
ctrl->p_new.p_u32[i]);
mutex_lock(&lut->lock);
spin_lock_irq(&lut->lock);
swap(lut->lut, dlb);
mutex_unlock(&lut->lock);
spin_unlock_irq(&lut->lock);
vsp1_dl_fragment_free(dlb);
return 0;
}
static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
static int lut_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_lut *lut = to_lut(subdev);
struct vsp1_lut *lut =
container_of(ctrl->handler, struct vsp1_lut, ctrls);
switch (cmd) {
case VIDIOC_VSP1_LUT_CONFIG:
return lut_set_table(lut, arg);
default:
return -ENOIOCTLCMD;
switch (ctrl->id) {
case V4L2_CID_VSP1_LUT_TABLE:
lut_set_table(lut, ctrl);
break;
}
return 0;
}
static const struct v4l2_ctrl_ops lut_ctrl_ops = {
.s_ctrl = lut_s_ctrl,
};
static const struct v4l2_ctrl_config lut_table_control = {
.ops = &lut_ctrl_ops,
.id = V4L2_CID_VSP1_LUT_TABLE,
.name = "Look-Up Table",
.type = V4L2_CTRL_TYPE_U32,
.min = 0x00000000,
.max = 0x00ffffff,
.step = 1,
.def = 0,
.dims = { 256},
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
@ -147,11 +165,7 @@ static int lut_set_format(struct v4l2_subdev *subdev,
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_core_ops lut_core_ops = {
.ioctl = lut_ioctl,
};
static struct v4l2_subdev_pad_ops lut_pad_ops = {
static const struct v4l2_subdev_pad_ops lut_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = lut_enum_mbus_code,
.enum_frame_size = lut_enum_frame_size,
@ -159,8 +173,7 @@ static struct v4l2_subdev_pad_ops lut_pad_ops = {
.set_fmt = lut_set_format,
};
static struct v4l2_subdev_ops lut_ops = {
.core = &lut_core_ops,
static const struct v4l2_subdev_ops lut_ops = {
.pad = &lut_pad_ops,
};
@ -170,18 +183,24 @@ static struct v4l2_subdev_ops lut_ops = {
static void lut_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
struct vsp1_dl_body *dlb;
unsigned long flags;
vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
mutex_lock(&lut->lock);
if (lut->lut) {
vsp1_dl_list_add_fragment(dl, lut->lut);
lut->lut = NULL;
if (full) {
vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
return;
}
mutex_unlock(&lut->lock);
spin_lock_irqsave(&lut->lock, flags);
dlb = lut->lut;
lut->lut = NULL;
spin_unlock_irqrestore(&lut->lock, flags);
if (dlb)
vsp1_dl_list_add_fragment(dl, dlb);
}
static const struct vsp1_entity_operations lut_entity_ops = {
@ -201,12 +220,30 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
if (lut == NULL)
return ERR_PTR(-ENOMEM);
spin_lock_init(&lut->lock);
lut->entity.ops = &lut_entity_ops;
lut->entity.type = VSP1_ENTITY_LUT;
ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops);
ret = vsp1_entity_init(vsp1, &lut->entity, "lut", 2, &lut_ops,
MEDIA_ENT_F_PROC_VIDEO_LUT);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&lut->ctrls, 1);
v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL);
lut->entity.subdev.ctrl_handler = &lut->ctrls;
if (lut->ctrls.error) {
dev_err(vsp1->dev, "lut: failed to initialize controls\n");
ret = lut->ctrls.error;
vsp1_entity_destroy(&lut->entity);
return ERR_PTR(ret);
}
v4l2_ctrl_handler_setup(&lut->ctrls);
return lut;
}

View File

@ -13,9 +13,10 @@
#ifndef __VSP1_LUT_H__
#define __VSP1_LUT_H__
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
@ -28,7 +29,9 @@ struct vsp1_device;
struct vsp1_lut {
struct vsp1_entity entity;
struct mutex lock;
struct v4l2_ctrl_handler ctrls;
spinlock_t lock;
struct vsp1_dl_body *lut;
};

View File

@ -172,13 +172,17 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
bru->inputs[i].rpf = NULL;
}
for (i = 0; i < pipe->num_inputs; ++i) {
pipe->inputs[i]->pipe = NULL;
pipe->inputs[i] = NULL;
for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) {
if (pipe->inputs[i]) {
pipe->inputs[i]->pipe = NULL;
pipe->inputs[i] = NULL;
}
}
pipe->output->pipe = NULL;
pipe->output = NULL;
if (pipe->output) {
pipe->output->pipe = NULL;
pipe->output = NULL;
}
INIT_LIST_HEAD(&pipe->entities);
pipe->state = VSP1_PIPELINE_STOPPED;
@ -286,6 +290,8 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
if (pipe->frame_end)
pipe->frame_end(pipe);
pipe->sequence++;
}
/*
@ -295,42 +301,20 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
* to be scaled, we disable alpha scaling when the UDS input has a fixed alpha
* value. The UDS then outputs a fixed alpha value which needs to be programmed
* from the input RPF alpha.
*
* This function can only be called from a subdev s_stream handler as it
* requires a valid display list context.
*/
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
struct vsp1_dl_list *dl,
unsigned int alpha)
struct vsp1_dl_list *dl, unsigned int alpha)
{
struct vsp1_entity *entity;
struct media_pad *pad;
if (!pipe->uds)
return;
pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
/* The BRU background color has a fixed alpha value set to 255, the
* output alpha value is thus always equal to 255.
*/
if (pipe->uds_input->type == VSP1_ENTITY_BRU)
alpha = 255;
while (pad) {
if (!is_media_entity_v4l2_subdev(pad->entity))
break;
entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
/* The BRU background color has a fixed alpha value set to 255,
* the output alpha value is thus always equal to 255.
*/
if (entity->type == VSP1_ENTITY_BRU)
alpha = 255;
if (entity->type == VSP1_ENTITY_UDS) {
struct vsp1_uds *uds = to_uds(&entity->subdev);
vsp1_uds_set_alpha(uds, dl, alpha);
break;
}
pad = &entity->pads[entity->source_pad];
pad = media_entity_remote_pad(pad);
}
vsp1_uds_set_alpha(pipe->uds, dl, alpha);
}
void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
@ -383,7 +367,7 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1)
{
unsigned int i;
/* Resume pipeline all running pipelines. */
/* Resume all running pipelines. */
for (i = 0; i < vsp1->info->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;

View File

@ -61,12 +61,13 @@ enum vsp1_pipeline_state {
* @pipe: the media pipeline
* @irqlock: protects the pipeline state
* @state: current state
* @wq: work queue to wait for state change completion
* @wq: wait queue to wait for state change completion
* @frame_end: frame end interrupt handler
* @lock: protects the pipeline use count and stream count
* @kref: pipeline reference count
* @stream_count: number of streaming video nodes
* @buffers_ready: bitmask of RPFs and WPFs with at least one buffer available
* @sequence: frame sequence number
* @num_inputs: number of RPFs
* @inputs: array of RPFs in the pipeline (indexed by RPF index)
* @output: WPF at the output of the pipeline
@ -90,6 +91,7 @@ struct vsp1_pipeline {
struct kref kref;
unsigned int stream_count;
unsigned int buffers_ready;
unsigned int sequence;
unsigned int num_inputs;
struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
@ -115,9 +117,7 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe);
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
struct vsp1_dl_list *dl,
unsigned int alpha);
struct vsp1_dl_list *dl, unsigned int alpha);
void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
void vsp1_pipelines_resume(struct vsp1_device *vsp1);

View File

@ -154,10 +154,10 @@
#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18)
#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18)
#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18)
#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8)
#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8
#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0)
#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0
#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 8)
#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 8
#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 0)
#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 0
#define VI6_RPF_VRTCOL_SET 0x0318
#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24)
@ -255,6 +255,8 @@
#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24)
#define VI6_WPF_OUTFMT_PDV_SHIFT 24
#define VI6_WPF_OUTFMT_PXA (1 << 23)
#define VI6_WPF_OUTFMT_ROT (1 << 18)
#define VI6_WPF_OUTFMT_HFLP (1 << 17)
#define VI6_WPF_OUTFMT_FLP (1 << 16)
#define VI6_WPF_OUTFMT_SPYCS (1 << 15)
#define VI6_WPF_OUTFMT_SPUVS (1 << 14)
@ -289,6 +291,11 @@
#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12)
#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12)
#define VI6_WPF_ROT_CTRL 0x1018
#define VI6_WPF_ROT_CTRL_LN16 (1 << 17)
#define VI6_WPF_ROT_CTRL_LMEM_WD_MASK (0x1fff << 0)
#define VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT 0
#define VI6_WPF_DSTM_STRIDE_Y 0x101c
#define VI6_WPF_DSTM_STRIDE_C 0x1020
#define VI6_WPF_DSTM_ADDR_Y 0x1024
@ -444,6 +451,15 @@
*/
#define VI6_CLU_CTRL 0x2900
#define VI6_CLU_CTRL_AAI (1 << 28)
#define VI6_CLU_CTRL_MVS (1 << 24)
#define VI6_CLU_CTRL_AX1I_2D (3 << 14)
#define VI6_CLU_CTRL_AX2I_2D (1 << 12)
#define VI6_CLU_CTRL_OS0_2D (3 << 8)
#define VI6_CLU_CTRL_OS1_2D (1 << 6)
#define VI6_CLU_CTRL_OS2_2D (3 << 4)
#define VI6_CLU_CTRL_M2D (1 << 1)
#define VI6_CLU_CTRL_EN (1 << 0)
/* -----------------------------------------------------------------------------
* HST Control Registers

View File

@ -38,7 +38,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_ops rpf_ops = {
static const struct v4l2_subdev_ops rpf_ops = {
.pad = &vsp1_rwpf_pad_ops,
};
@ -60,7 +60,7 @@ static void rpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
static void rpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
@ -73,6 +73,16 @@ static void rpf_configure(struct vsp1_entity *entity,
u32 pstride;
u32 infmt;
if (!full) {
vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha |
(rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT));
vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha);
return;
}
/* Source size, stride and crop offsets.
*
* The crop offsets correspond to the location of the crop rectangle top
@ -130,9 +140,10 @@ static void rpf_configure(struct vsp1_entity *entity,
if (pipe->bru) {
const struct v4l2_rect *compose;
compose = vsp1_entity_get_pad_compose(pipe->bru,
pipe->bru->config,
rpf->bru_input);
compose = vsp1_entity_get_pad_selection(pipe->bru,
pipe->bru->config,
rpf->bru_input,
V4L2_SEL_TGT_COMPOSE);
left = compose->left;
top = compose->top;
}
@ -167,9 +178,6 @@ static void rpf_configure(struct vsp1_entity *entity,
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET,
rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
if (entity->vsp1->info->gen == 3) {
u32 mult;
@ -187,8 +195,7 @@ static void rpf_configure(struct vsp1_entity *entity,
mult = VI6_RPF_MULT_ALPHA_A_MMD_RATIO
| (premultiplied ?
VI6_RPF_MULT_ALPHA_P_MMD_RATIO :
VI6_RPF_MULT_ALPHA_P_MMD_NONE)
| (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT);
VI6_RPF_MULT_ALPHA_P_MMD_NONE);
} else {
/* When the input doesn't contain an alpha channel the
* global alpha value is applied in the unpacking unit,
@ -199,11 +206,9 @@ static void rpf_configure(struct vsp1_entity *entity,
| VI6_RPF_MULT_ALPHA_P_MMD_NONE;
}
vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, mult);
rpf->mult_alpha = mult;
}
vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, dl, rpf->alpha);
vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0);
@ -236,18 +241,21 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
rpf->entity.index = index;
sprintf(name, "rpf.%u", index);
ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops);
ret = vsp1_entity_init(vsp1, &rpf->entity, name, 2, &rpf_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the control handler. */
ret = vsp1_rwpf_init_ctrls(rpf);
ret = vsp1_rwpf_init_ctrls(rpf, 0);
if (ret < 0) {
dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
index);
goto error;
}
v4l2_ctrl_handler_setup(&rpf->ctrls);
return rpf;
error:

View File

@ -241,11 +241,9 @@ 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)
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
{
rwpf->alpha = 255;
v4l2_ctrl_handler_init(&rwpf->ctrls, 1);
v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);

View File

@ -13,6 +13,8 @@
#ifndef __VSP1_RWPF_H__
#define __VSP1_RWPF_H__
#include <linux/spinlock.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
@ -49,6 +51,16 @@ struct vsp1_rwpf {
unsigned int alpha;
u32 mult_alpha;
u32 outfmt;
struct {
spinlock_t lock;
struct v4l2_ctrl *ctrls[2];
unsigned int pending;
unsigned int active;
} flip;
unsigned int offsets[2];
struct vsp1_rwpf_memory mem;
@ -68,7 +80,7 @@ static inline struct vsp1_rwpf *entity_to_rwpf(struct vsp1_entity *entity)
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf);
int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols);
extern const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops;

View File

@ -37,7 +37,7 @@ static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl,
* Controls
*/
#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1)
#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE | 0x1001)
struct vsp1_sru_param {
u32 ctrl0;
@ -239,7 +239,7 @@ static int sru_set_format(struct v4l2_subdev *subdev,
return 0;
}
static struct v4l2_subdev_pad_ops sru_pad_ops = {
static const struct v4l2_subdev_pad_ops sru_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = sru_enum_mbus_code,
.enum_frame_size = sru_enum_frame_size,
@ -247,7 +247,7 @@ static struct v4l2_subdev_pad_ops sru_pad_ops = {
.set_fmt = sru_set_format,
};
static struct v4l2_subdev_ops sru_ops = {
static const struct v4l2_subdev_ops sru_ops = {
.pad = &sru_pad_ops,
};
@ -257,7 +257,7 @@ static struct v4l2_subdev_ops sru_ops = {
static void sru_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
const struct vsp1_sru_param *param;
struct vsp1_sru *sru = to_sru(&entity->subdev);
@ -265,6 +265,9 @@ static void sru_configure(struct vsp1_entity *entity,
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
if (!full)
return;
input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
SRU_PAD_SINK);
output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config,
@ -308,7 +311,8 @@ struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
sru->entity.ops = &sru_entity_ops;
sru->entity.type = VSP1_ENTITY_SRU;
ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops);
ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops,
MEDIA_ENT_F_PROC_VIDEO_SCALER);
if (ret < 0)
return ERR_PTR(ret);

View File

@ -40,9 +40,11 @@ static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
* Scaling Computation
*/
void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl,
unsigned int alpha)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL,
alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
}
@ -226,7 +228,7 @@ static int uds_set_format(struct v4l2_subdev *subdev,
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_pad_ops uds_pad_ops = {
static const struct v4l2_subdev_pad_ops uds_pad_ops = {
.init_cfg = vsp1_entity_init_cfg,
.enum_mbus_code = uds_enum_mbus_code,
.enum_frame_size = uds_enum_frame_size,
@ -234,7 +236,7 @@ static struct v4l2_subdev_pad_ops uds_pad_ops = {
.set_fmt = uds_set_format,
};
static struct v4l2_subdev_ops uds_ops = {
static const struct v4l2_subdev_ops uds_ops = {
.pad = &uds_pad_ops,
};
@ -244,7 +246,7 @@ static struct v4l2_subdev_ops uds_ops = {
static void uds_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
@ -253,6 +255,9 @@ static void uds_configure(struct vsp1_entity *entity,
unsigned int vscale;
bool multitap;
if (!full)
return;
input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
UDS_PAD_SINK);
output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config,
@ -314,7 +319,8 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
uds->entity.index = index;
sprintf(name, "uds.%u", index);
ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops);
ret = vsp1_entity_init(vsp1, &uds->entity, name, 2, &uds_ops,
MEDIA_ENT_F_PROC_VIDEO_SCALER);
if (ret < 0)
return ERR_PTR(ret);

View File

@ -35,7 +35,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
void vsp1_uds_set_alpha(struct vsp1_uds *uds, struct vsp1_dl_list *dl,
void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl,
unsigned int alpha);
#endif /* __VSP1_UDS_H__ */

View File

@ -219,7 +219,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
spin_unlock_irqrestore(&video->irqlock, flags);
done->buf.sequence = video->sequence++;
done->buf.sequence = pipe->sequence;
done->buf.vb2_buf.timestamp = ktime_get_ns();
for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
vb2_set_plane_payload(&done->buf.vb2_buf, i,
@ -251,11 +251,17 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
struct vsp1_entity *entity;
unsigned int i;
if (!pipe->dl)
pipe->dl = vsp1_dl_list_get(pipe->output->dlm);
list_for_each_entry(entity, &pipe->entities, list_pipe) {
if (entity->ops->configure)
entity->ops->configure(entity, pipe, pipe->dl, false);
}
for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *rwpf = pipe->inputs[i];
@ -628,7 +634,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
vsp1_entity_route_setup(entity, pipe->dl);
if (entity->ops->configure)
entity->ops->configure(entity, pipe, pipe->dl);
entity->ops->configure(entity, pipe, pipe->dl, true);
}
return 0;
@ -670,7 +676,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
int ret;
mutex_lock(&pipe->lock);
if (--pipe->stream_count == 0) {
if (--pipe->stream_count == pipe->num_inputs) {
/* Stop the pipeline. */
ret = vsp1_pipeline_stop(pipe);
if (ret == -ETIMEDOUT)
@ -692,7 +698,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
spin_unlock_irqrestore(&video->irqlock, flags);
}
static struct vb2_ops vsp1_video_queue_qops = {
static const struct vb2_ops vsp1_video_queue_qops = {
.queue_setup = vsp1_video_queue_setup,
.buf_prepare = vsp1_video_buffer_prepare,
.buf_queue = vsp1_video_buffer_queue,
@ -801,8 +807,6 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
if (video->queue.owner && video->queue.owner != file->private_data)
return -EBUSY;
video->sequence = 0;
/* Get a pipeline for the video node and start streaming on it. No link
* touching an entity in the pipeline can be activated or deactivated
* once streaming is started.
@ -911,7 +915,7 @@ static int vsp1_video_release(struct file *file)
return 0;
}
static struct v4l2_file_operations vsp1_video_fops = {
static const struct v4l2_file_operations vsp1_video_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
.open = vsp1_video_open,

View File

@ -48,7 +48,6 @@ struct vsp1_video {
struct vb2_queue queue;
spinlock_t irqlock;
struct list_head irqqueue;
unsigned int sequence;
};
static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)

View File

@ -36,6 +36,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
enum wpf_flip_ctrl {
WPF_CTRL_VFLIP = 0,
WPF_CTRL_HFLIP = 1,
WPF_CTRL_MAX,
};
static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_rwpf *wpf =
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
unsigned int i;
u32 flip = 0;
switch (ctrl->id) {
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
for (i = 0; i < WPF_CTRL_MAX; ++i) {
if (wpf->flip.ctrls[i])
flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0;
}
spin_lock_irq(&wpf->flip.lock);
wpf->flip.pending = flip;
spin_unlock_irq(&wpf->flip.lock);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = {
.s_ctrl = vsp1_wpf_s_ctrl,
};
static int wpf_init_controls(struct vsp1_rwpf *wpf)
{
struct vsp1_device *vsp1 = wpf->entity.vsp1;
unsigned int num_flip_ctrls;
spin_lock_init(&wpf->flip.lock);
if (wpf->entity.index != 0) {
/* Only WPF0 supports flipping. */
num_flip_ctrls = 0;
} else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) {
/* When horizontal flip is supported the WPF implements two
* controls (horizontal flip and vertical flip).
*/
num_flip_ctrls = 2;
} else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) {
/* When only vertical flip is supported the WPF implements a
* single control (vertical flip).
*/
num_flip_ctrls = 1;
} else {
/* Otherwise flipping is not supported. */
num_flip_ctrls = 0;
}
vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
if (num_flip_ctrls >= 1) {
wpf->flip.ctrls[WPF_CTRL_VFLIP] =
v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
}
if (num_flip_ctrls == 2) {
wpf->flip.ctrls[WPF_CTRL_HFLIP] =
v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
v4l2_ctrl_cluster(2, wpf->flip.ctrls);
}
if (wpf->ctrls.error) {
dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
wpf->entity.index);
return wpf->ctrls.error;
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
@ -62,11 +153,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops wpf_video_ops = {
static const struct v4l2_subdev_video_ops wpf_video_ops = {
.s_stream = wpf_s_stream,
};
static struct v4l2_subdev_ops wpf_ops = {
static const struct v4l2_subdev_ops wpf_ops = {
.video = &wpf_video_ops,
.pad = &vsp1_rwpf_pad_ops,
};
@ -85,15 +176,37 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)
static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)
{
struct vsp1_rwpf *wpf = entity_to_rwpf(entity);
const struct v4l2_pix_format_mplane *format = &wpf->format;
struct vsp1_rwpf_memory mem = wpf->mem;
unsigned int flip = wpf->flip.active;
unsigned int offset;
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]);
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]);
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]);
/* Update the memory offsets based on flipping configuration. The
* destination addresses point to the locations where the VSP starts
* writing to memory, which can be different corners of the image
* depending on vertical flipping. Horizontal flipping is handled
* through a line buffer and doesn't modify the start address.
*/
if (flip & BIT(WPF_CTRL_VFLIP)) {
mem.addr[0] += (format->height - 1)
* format->plane_fmt[0].bytesperline;
if (format->num_planes > 1) {
offset = (format->height / wpf->fmtinfo->vsub - 1)
* format->plane_fmt[1].bytesperline;
mem.addr[1] += offset;
mem.addr[2] += offset;
}
}
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
}
static void wpf_configure(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl)
struct vsp1_dl_list *dl, bool full)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
@ -104,6 +217,26 @@ static void wpf_configure(struct vsp1_entity *entity,
u32 outfmt = 0;
u32 srcrpf = 0;
if (!full) {
const unsigned int mask = BIT(WPF_CTRL_VFLIP)
| BIT(WPF_CTRL_HFLIP);
spin_lock(&wpf->flip.lock);
wpf->flip.active = (wpf->flip.active & ~mask)
| (wpf->flip.pending & mask);
spin_unlock(&wpf->flip.lock);
outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
outfmt |= VI6_WPF_OUTFMT_FLP;
if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
outfmt |= VI6_WPF_OUTFMT_HFLP;
vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt);
return;
}
/* Cropping */
crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config);
@ -143,13 +276,18 @@ static void wpf_configure(struct vsp1_entity *entity,
format->plane_fmt[1].bytesperline);
vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap);
if (vsp1->info->features & VSP1_HAS_WPF_HFLIP &&
wpf->entity.index == 0)
vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL,
VI6_WPF_ROT_CTRL_LN16 |
(256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
}
if (sink_format->code != source_format->code)
outfmt |= VI6_WPF_OUTFMT_CSC;
outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT;
vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt);
wpf->outfmt = outfmt;
vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
@ -216,7 +354,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
wpf->entity.index = index;
sprintf(name, "wpf.%u", index);
ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops);
ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops,
MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
if (ret < 0)
return ERR_PTR(ret);
@ -228,13 +367,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
}
/* Initialize the control handler. */
ret = vsp1_rwpf_init_ctrls(wpf);
ret = wpf_init_controls(wpf);
if (ret < 0) {
dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
index);
goto error;
}
v4l2_ctrl_handler_setup(&wpf->ctrls);
return wpf;
error:

37
include/media/rcar-fcp.h Normal file
View File

@ -0,0 +1,37 @@
/*
* rcar-fcp.h -- R-Car Frame Compression Processor Driver
*
* Copyright (C) 2016 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.
*/
#ifndef __MEDIA_RCAR_FCP_H__
#define __MEDIA_RCAR_FCP_H__
struct device_node;
struct rcar_fcp_device;
#if IS_ENABLED(CONFIG_VIDEO_RENESAS_FCP)
struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np);
void rcar_fcp_put(struct rcar_fcp_device *fcp);
int rcar_fcp_enable(struct rcar_fcp_device *fcp);
void rcar_fcp_disable(struct rcar_fcp_device *fcp);
#else
static inline struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
{
return ERR_PTR(-ENOENT);
}
static inline void rcar_fcp_put(struct rcar_fcp_device *fcp) { }
static inline int rcar_fcp_enable(struct rcar_fcp_device *fcp)
{
return -ENOSYS;
}
static inline void rcar_fcp_disable(struct rcar_fcp_device *fcp) { }
#endif
#endif /* __MEDIA_RCAR_FCP_H__ */

View File

@ -14,31 +14,28 @@
#define __MEDIA_VSP1_H__
#include <linux/types.h>
#include <linux/videodev2.h>
struct device;
struct v4l2_rect;
int vsp1_du_init(struct device *dev);
int vsp1_du_setup_lif(struct device *dev, unsigned int width,
unsigned int height);
struct vsp1_du_atomic_config {
u32 pixelformat;
unsigned int pitch;
dma_addr_t mem[2];
struct v4l2_rect src;
struct v4l2_rect dst;
unsigned int alpha;
unsigned int zpos;
};
void vsp1_du_atomic_begin(struct device *dev);
int vsp1_du_atomic_update_ext(struct device *dev, unsigned int rpf,
u32 pixelformat, unsigned int pitch,
dma_addr_t mem[2], const struct v4l2_rect *src,
const struct v4l2_rect *dst, unsigned int alpha,
unsigned int zpos);
int vsp1_du_atomic_update(struct device *dev, unsigned int rpf,
const struct vsp1_du_atomic_config *cfg);
void vsp1_du_atomic_flush(struct device *dev);
static inline int vsp1_du_atomic_update(struct device *dev,
unsigned int rpf_index, u32 pixelformat,
unsigned int pitch, dma_addr_t mem[2],
const struct v4l2_rect *src,
const struct v4l2_rect *dst)
{
return vsp1_du_atomic_update_ext(dev, rpf_index, pixelformat, pitch,
mem, src, dst, 255, 0);
}
#endif /* __MEDIA_VSP1_H__ */

View File

@ -94,6 +94,16 @@ struct media_device_info {
#define MEDIA_ENT_F_AUDIO_PLAYBACK (MEDIA_ENT_F_BASE + 0x03002)
#define MEDIA_ENT_F_AUDIO_MIXER (MEDIA_ENT_F_BASE + 0x03003)
/*
* Processing entities
*/
#define MEDIA_ENT_F_PROC_VIDEO_COMPOSER (MEDIA_ENT_F_BASE + 0x4001)
#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER (MEDIA_ENT_F_BASE + 0x4002)
#define MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV (MEDIA_ENT_F_BASE + 0x4003)
#define MEDIA_ENT_F_PROC_VIDEO_LUT (MEDIA_ENT_F_BASE + 0x4004)
#define MEDIA_ENT_F_PROC_VIDEO_SCALER (MEDIA_ENT_F_BASE + 0x4005)
#define MEDIA_ENT_F_PROC_VIDEO_STATISTICS (MEDIA_ENT_F_BASE + 0x4006)
/*
* Connectors
*/

View File

@ -1,34 +0,0 @@
/*
* vsp1.h
*
* Renesas R-Car VSP1 - User-space API
*
* Copyright (C) 2013 Renesas Corporation
*
* Contacts: 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 version 2 as
* published by the Free Software Foundation.
*/
#ifndef __VSP1_USER_H__
#define __VSP1_USER_H__
#include <linux/types.h>
#include <linux/videodev2.h>
/*
* Private IOCTLs
*
* VIDIOC_VSP1_LUT_CONFIG - Configure the lookup table
*/
#define VIDIOC_VSP1_LUT_CONFIG \
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct vsp1_lut_config)
struct vsp1_lut_config {
__u32 lut[256];
};
#endif /* __VSP1_USER_H__ */