]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/media/platform/vsp1/vsp1_entity.c
[media] v4l: vsp1: Factorize media bus codes enumeration code
[karo-tx-linux.git] / drivers / media / platform / vsp1 / vsp1_entity.c
index 20a78fbd36910cf3358bd6083d9449bb1a31c1f7..9e848260d85e30191be79b53b8a57010106d0672 100644 (file)
 #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,80 @@ 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;
+}
 
 /* -----------------------------------------------------------------------------
  * Media Operations
@@ -171,11 +231,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 +247,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 +265,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);
 }