1517b03923
Display lists contain lists of registers and associated values to be applied atomically by the hardware. They lower the pressure on interrupt processing delays when reprogramming the device as settings can be prepared well in advance and queued to the hardware without waiting for the end of the current frame. Display list support is currently limited to the DRM pipeline. Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@renesas.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
239 lines
6.4 KiB
C
239 lines
6.4 KiB
C
/*
|
|
* vsp1_entity.c -- R-Car VSP1 Base Entity
|
|
*
|
|
* 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 <linux/device.h>
|
|
#include <linux/gfp.h>
|
|
|
|
#include <media/media-entity.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
#include "vsp1.h"
|
|
#include "vsp1_entity.h"
|
|
|
|
bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
|
|
{
|
|
unsigned long flags;
|
|
bool streaming;
|
|
|
|
spin_lock_irqsave(&entity->lock, flags);
|
|
streaming = entity->streaming;
|
|
spin_unlock_irqrestore(&entity->lock, flags);
|
|
|
|
return streaming;
|
|
}
|
|
|
|
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
|
|
{
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
spin_lock_irqsave(&entity->lock, flags);
|
|
entity->streaming = streaming;
|
|
spin_unlock_irqrestore(&entity->lock, flags);
|
|
|
|
if (!streaming)
|
|
return 0;
|
|
|
|
if (!entity->vsp1->pdata.uapi || !entity->subdev.ctrl_handler)
|
|
return 0;
|
|
|
|
ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
|
|
if (ret < 0) {
|
|
spin_lock_irqsave(&entity->lock, flags);
|
|
entity->streaming = false;
|
|
spin_unlock_irqrestore(&entity->lock, flags);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void vsp1_entity_route_setup(struct vsp1_entity *source)
|
|
{
|
|
struct vsp1_entity *sink;
|
|
|
|
if (source->route->reg == 0)
|
|
return;
|
|
|
|
sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
|
|
vsp1_mod_write(source, source->route->reg,
|
|
sink->route->inputs[source->sink_pad]);
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* V4L2 Subdevice Operations
|
|
*/
|
|
|
|
struct v4l2_mbus_framefmt *
|
|
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
|
|
struct v4l2_subdev_pad_config *cfg,
|
|
unsigned int pad, u32 which)
|
|
{
|
|
switch (which) {
|
|
case V4L2_SUBDEV_FORMAT_TRY:
|
|
return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad);
|
|
case V4L2_SUBDEV_FORMAT_ACTIVE:
|
|
return &entity->formats[pad];
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* vsp1_entity_init_formats - Initialize formats on all pads
|
|
* @subdev: V4L2 subdevice
|
|
* @cfg: V4L2 subdev pad configuration
|
|
*
|
|
* Initialize all pad formats with default values. If cfg is not NULL, try
|
|
* formats are initialized on the file handle. Otherwise active formats are
|
|
* initialized on the device.
|
|
*/
|
|
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_pad_config *cfg)
|
|
{
|
|
struct v4l2_subdev_format format;
|
|
unsigned int pad;
|
|
|
|
for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
|
|
memset(&format, 0, sizeof(format));
|
|
|
|
format.pad = pad;
|
|
format.which = cfg ? V4L2_SUBDEV_FORMAT_TRY
|
|
: V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
|
|
v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format);
|
|
}
|
|
}
|
|
|
|
static int vsp1_entity_open(struct v4l2_subdev *subdev,
|
|
struct v4l2_subdev_fh *fh)
|
|
{
|
|
vsp1_entity_init_formats(subdev, fh->pad);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
|
|
.open = vsp1_entity_open,
|
|
};
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Media Operations
|
|
*/
|
|
|
|
int vsp1_entity_link_setup(struct media_entity *entity,
|
|
const struct media_pad *local,
|
|
const struct media_pad *remote, u32 flags)
|
|
{
|
|
struct vsp1_entity *source;
|
|
|
|
if (!(local->flags & MEDIA_PAD_FL_SOURCE))
|
|
return 0;
|
|
|
|
source = container_of(local->entity, struct vsp1_entity, subdev.entity);
|
|
|
|
if (!source->route)
|
|
return 0;
|
|
|
|
if (flags & MEDIA_LNK_FL_ENABLED) {
|
|
if (source->sink)
|
|
return -EBUSY;
|
|
source->sink = remote->entity;
|
|
source->sink_pad = remote->index;
|
|
} else {
|
|
source->sink = NULL;
|
|
source->sink_pad = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
* Initialization
|
|
*/
|
|
|
|
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), { VI6_DPR_NODE_RPF(0), } },
|
|
{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } },
|
|
{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } },
|
|
{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } },
|
|
{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } },
|
|
{ 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), } },
|
|
};
|
|
|
|
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
|
|
unsigned int num_pads)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) {
|
|
if (vsp1_routes[i].type == entity->type &&
|
|
vsp1_routes[i].index == entity->index) {
|
|
entity->route = &vsp1_routes[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ARRAY_SIZE(vsp1_routes))
|
|
return -EINVAL;
|
|
|
|
spin_lock_init(&entity->lock);
|
|
|
|
entity->vsp1 = vsp1;
|
|
entity->source_pad = num_pads - 1;
|
|
|
|
/* Allocate formats and pads. */
|
|
entity->formats = devm_kzalloc(vsp1->dev,
|
|
num_pads * sizeof(*entity->formats),
|
|
GFP_KERNEL);
|
|
if (entity->formats == NULL)
|
|
return -ENOMEM;
|
|
|
|
entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
|
|
GFP_KERNEL);
|
|
if (entity->pads == NULL)
|
|
return -ENOMEM;
|
|
|
|
/* Initialize pads. */
|
|
for (i = 0; i < num_pads - 1; ++i)
|
|
entity->pads[i].flags = MEDIA_PAD_FL_SINK;
|
|
|
|
entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
|
|
|
|
/* Initialize the media entity. */
|
|
return media_entity_pads_init(&entity->subdev.entity, num_pads,
|
|
entity->pads);
|
|
}
|
|
|
|
void vsp1_entity_destroy(struct vsp1_entity *entity)
|
|
{
|
|
if (entity->subdev.ctrl_handler)
|
|
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
|
|
media_entity_cleanup(&entity->subdev.entity);
|
|
}
|