diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_entity.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_entity.c | 288 |
1 files changed, 211 insertions, 77 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 20a78fbd3691..3d070bcc6053 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -19,46 +19,11 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" +#include "vsp1_dl.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->info->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) +void vsp1_entity_route_setup(struct vsp1_entity *source, + struct vsp1_dl_list *dl) { struct vsp1_entity *sink; @@ -66,40 +31,74 @@ void vsp1_entity_route_setup(struct vsp1_entity *source) return; sink = container_of(source->sink, struct vsp1_entity, subdev.entity); - vsp1_mod_write(source, source->route->reg, - sink->route->inputs[source->sink_pad]); + vsp1_dl_list_write(dl, 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, +/** + * vsp1_entity_get_pad_config - Get the pad configuration for an entity + * @entity: the entity + * @cfg: the TRY pad configuration + * @which: configuration selector (ACTIVE or TRY) + * + * Return the pad configuration requested by the which argument. The TRY + * configuration is passed explicitly to the function through the cfg argument + * and simply returned when requested. The ACTIVE configuration comes from the + * entity structure. + */ +struct v4l2_subdev_pad_config * +vsp1_entity_get_pad_config(struct vsp1_entity *entity, struct v4l2_subdev_pad_config *cfg, - unsigned int pad, u32 which) + enum v4l2_subdev_format_whence 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]; + return entity->config; + case V4L2_SUBDEV_FORMAT_TRY: default: - return NULL; + return cfg; } } +/** + * vsp1_entity_get_pad_format - Get a pad format from storage for an entity + * @entity: the entity + * @cfg: the configuration storage + * @pad: the pad number + * + * Return the format stored in the given configuration for an entity's pad. The + * configuration can be an ACTIVE or TRY configuration. + */ +struct v4l2_mbus_framefmt * +vsp1_entity_get_pad_format(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) +{ + return v4l2_subdev_get_try_format(&entity->subdev, cfg, pad); +} + +struct v4l2_rect * +vsp1_entity_get_pad_compose(struct vsp1_entity *entity, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad) +{ + return v4l2_subdev_get_try_compose(&entity->subdev, cfg, pad); +} + /* - * vsp1_entity_init_formats - Initialize formats on all pads + * vsp1_entity_init_cfg - 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. + * Initialize all pad formats with default values in the given pad config. This + * function can be used as a handler for the subdev pad::init_cfg operation. */ -void vsp1_entity_init_formats(struct v4l2_subdev *subdev, - struct v4l2_subdev_pad_config *cfg) +int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) { struct v4l2_subdev_format format; unsigned int pad; @@ -113,19 +112,132 @@ void vsp1_entity_init_formats(struct v4l2_subdev *subdev, v4l2_subdev_call(subdev, pad, set_fmt, cfg, &format); } + + return 0; } -static int vsp1_entity_open(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh) +/* + * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: V4L2 subdev format + * + * This function implements the subdev get_fmt pad operation. It can be used as + * a direct drop-in for the operation handler. + */ +int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { - vsp1_entity_init_formats(subdev, fh->pad); + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + + config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + if (!config) + return -EINVAL; + + fmt->format = *vsp1_entity_get_pad_format(entity, config, fmt->pad); return 0; } -const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { - .open = vsp1_entity_open, -}; +/* + * vsp1_subdev_enum_mbus_code - Subdev pad enum_mbus_code handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @code: Media bus code enumeration + * @codes: Array of supported media bus codes + * @ncodes: Number of supported media bus codes + * + * This function implements the subdev enum_mbus_code pad operation for entities + * that do not support format conversion. It enumerates the given supported + * media bus codes on the sink pad and reports a source pad format identical to + * the sink pad. + */ +int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code, + const unsigned int *codes, unsigned int ncodes) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + + if (code->pad == 0) { + if (code->index >= ncodes) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + /* The entity can't perform format conversion, the sink format + * is always identical to the source format. + */ + if (code->index) + return -EINVAL; + + config = vsp1_entity_get_pad_config(entity, cfg, code->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(entity, config, 0); + code->code = format->code; + } + + return 0; +} + +/* + * vsp1_subdev_enum_frame_size - Subdev pad enum_frame_size handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fse: Frame size enumeration + * @min_width: Minimum image width + * @min_height: Minimum image height + * @max_width: Maximum image width + * @max_height: Maximum image height + * + * This function implements the subdev enum_frame_size pad operation for + * entities that do not support scaling or cropping. It reports the given + * minimum and maximum frame width and height on the sink pad, and a fixed + * source pad size identical to the sink pad. + */ +int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + + config = vsp1_entity_get_pad_config(entity, cfg, fse->which); + if (!config) + return -EINVAL; + + format = vsp1_entity_get_pad_format(entity, config, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == 0) { + fse->min_width = min_width; + fse->max_width = max_width; + fse->min_height = min_height; + fse->max_height = max_height; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} /* ----------------------------------------------------------------------------- * Media Operations @@ -171,11 +283,11 @@ static const struct vsp1_route vsp1_routes[] = { { 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_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), } }, @@ -187,9 +299,12 @@ static const struct vsp1_route vsp1_routes[] = { }; int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, - unsigned int num_pads) + const char *name, unsigned int num_pads, + const struct v4l2_subdev_ops *ops) { + struct v4l2_subdev *subdev; unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { if (vsp1_routes[i].type == entity->type && @@ -202,37 +317,56 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, 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; - + /* Allocate and initialize pads. */ 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); + ret = media_entity_pads_init(&entity->subdev.entity, num_pads, + entity->pads); + if (ret < 0) + return ret; + + /* Initialize the V4L2 subdev. */ + subdev = &entity->subdev; + v4l2_subdev_init(subdev, ops); + + subdev->entity.ops = &vsp1->media_ops; + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + snprintf(subdev->name, sizeof(subdev->name), "%s %s", + dev_name(vsp1->dev), name); + + vsp1_entity_init_cfg(subdev, NULL); + + /* Allocate the pad configuration to store formats and selection + * rectangles. + */ + entity->config = v4l2_subdev_alloc_pad_config(&entity->subdev); + if (entity->config == NULL) { + media_entity_cleanup(&entity->subdev.entity); + return -ENOMEM; + } + + return 0; } void vsp1_entity_destroy(struct vsp1_entity *entity) { + if (entity->ops && entity->ops->destroy) + entity->ops->destroy(entity); if (entity->subdev.ctrl_handler) v4l2_ctrl_handler_free(entity->subdev.ctrl_handler); + v4l2_subdev_free_pad_config(entity->config); media_entity_cleanup(&entity->subdev.entity); } |