On 11/11/17 01:38, Niklas Söderlund wrote:
> Add the ability to process media device link change request. Link
> enabling is a bit complicated on Gen3, whether or not it's possible to
> enable a link depends on what other links already are enabled. On Gen3
> the 8 VINs are split into two subgroup's (VIN0-3 and VIN4-7) and from a
> routing perspective these two groups are independent of each other.
> Each subgroup's routing is controlled by the subgroup VIN master
> instance (VIN0 and VIN4).
> 
> There are a limited number of possible route setups available for each
> subgroup and the configuration of each setup is dictated by the
> hardware. On H3 for example there are 6 possible route setups for each
> subgroup to choose from.
> 
> This leads to the media device link notification code being rather large
> since it will find the best routing configuration to try and accommodate
> as many links as possible. When it's not possible to enable a new link
> due to hardware constrains the link_notifier callback will return
> -EMLINK.
> 
> Signed-off-by: Niklas Söderlund <niklas.soderlund+rene...@ragnatech.se>

Reviewed-by: Hans Verkuil <hans.verk...@cisco.com>

Regards,

        Hans

> ---
>  drivers/media/platform/rcar-vin/rcar-core.c | 205 
> ++++++++++++++++++++++++++++
>  1 file changed, 205 insertions(+)
> 
> diff --git a/drivers/media/platform/rcar-vin/rcar-core.c 
> b/drivers/media/platform/rcar-vin/rcar-core.c
> index 78a9766eb7114959..79b0334d8c563328 100644
> --- a/drivers/media/platform/rcar-vin/rcar-core.c
> +++ b/drivers/media/platform/rcar-vin/rcar-core.c
> @@ -27,6 +27,209 @@
>  
>  #include "rcar-vin.h"
>  
> +/* 
> -----------------------------------------------------------------------------
> + * Media Controller link notification
> + */
> +
> +static unsigned int rvin_group_csi_pad_to_chan(unsigned int pad)
> +{
> +     /*
> +      * The companion CSI-2 receiver driver (rcar-csi2) is known
> +      * and we know it have one source pad (pad 0) and four sink
> +      * pads (pad 1-4). So to translate a pad on the remote
> +      * CSI-2 receiver to the VIN internal channel number simply
> +      * subtract one from the pad number.
> +      */
> +     return pad - 1;
> +}
> +
> +/* group lock should be held when calling this function */
> +static int rvin_group_entity_to_vin_num(struct rvin_group *group,
> +                                     struct media_entity *entity)
> +{
> +     struct video_device *vdev;
> +     int i;
> +
> +     if (!is_media_entity_v4l2_video_device(entity))
> +             return -ENODEV;
> +
> +     vdev = media_entity_to_video_device(entity);
> +
> +     for (i = 0; i < RCAR_VIN_NUM; i++) {
> +             if (!group->vin[i])
> +                     continue;
> +
> +             if (&group->vin[i]->vdev == vdev)
> +                     return i;
> +     }
> +
> +     return -ENODEV;
> +}
> +
> +/* group lock should be held when calling this function */
> +static int rvin_group_entity_to_csi_num(struct rvin_group *group,
> +                                     struct media_entity *entity)
> +{
> +     struct v4l2_subdev *sd;
> +     int i;
> +
> +     if (!is_media_entity_v4l2_subdev(entity))
> +             return -ENODEV;
> +
> +     sd = media_entity_to_v4l2_subdev(entity);
> +
> +     for (i = 0; i < RVIN_CSI_MAX; i++)
> +             if (group->csi[i].subdev == sd)
> +                     return i;
> +
> +     return -ENODEV;
> +}
> +
> +/* group lock should be held when calling this function */
> +static void __rvin_group_build_link_list(struct rvin_group *group,
> +                                      struct rvin_group_chsel *map,
> +                                      int start, int len)
> +{
> +     struct media_pad *vin_pad, *remote_pad;
> +     unsigned int n;
> +
> +     for (n = 0; n < len; n++) {
> +             map[n].csi = -1;
> +             map[n].chan = -1;
> +
> +             if (!group->vin[start + n])
> +                     continue;
> +
> +             vin_pad = &group->vin[start + n]->vdev.entity.pads[0];
> +
> +             remote_pad = media_entity_remote_pad(vin_pad);
> +             if (!remote_pad)
> +                     continue;
> +
> +             map[n].csi =
> +                     rvin_group_entity_to_csi_num(group, remote_pad->entity);
> +             map[n].chan = rvin_group_csi_pad_to_chan(remote_pad->index);
> +     }
> +}
> +
> +/* group lock should be held when calling this function */
> +static int __rvin_group_try_get_chsel(struct rvin_group *group,
> +                                   struct rvin_group_chsel *map,
> +                                   int start, int len)
> +{
> +     const struct rvin_group_chsel *sel;
> +     unsigned int i, n;
> +     int chsel;
> +
> +     for (i = 0; i < group->vin[start]->info->num_chsels; i++) {
> +             chsel = i;
> +             for (n = 0; n < len; n++) {
> +
> +                     /* If the link is not active it's OK */
> +                     if (map[n].csi == -1)
> +                             continue;
> +
> +                     /* Check if chsel matches requested link */
> +                     sel = &group->vin[start]->info->chsels[start + n][i];
> +                     if (map[n].csi != sel->csi ||
> +                         map[n].chan != sel->chan) {
> +                             chsel = -1;
> +                             break;
> +                     }
> +             }
> +
> +             /* A chsel which satisfies the links has been found */
> +             if (chsel != -1)
> +                     return chsel;
> +     }
> +
> +     /* No chsel can satisfy the requested links */
> +     return -1;
> +}
> +
> +/* group lock should be held when calling this function */
> +static bool rvin_group_in_use(struct rvin_group *group)
> +{
> +     struct media_entity *entity;
> +
> +     media_device_for_each_entity(entity, &group->mdev)
> +             if (entity->use_count)
> +                     return true;
> +
> +     return false;
> +}
> +
> +static int rvin_group_link_notify(struct media_link *link, u32 flags,
> +                               unsigned int notification)
> +{
> +     struct rvin_group *group = container_of(link->graph_obj.mdev,
> +                                             struct rvin_group, mdev);
> +     struct rvin_group_chsel chsel_map[4];
> +     int vin_num, vin_master, csi_num, csi_chan;
> +     unsigned int chsel;
> +
> +     mutex_lock(&group->lock);
> +
> +     vin_num = rvin_group_entity_to_vin_num(group, link->sink->entity);
> +     csi_num = rvin_group_entity_to_csi_num(group, link->source->entity);
> +     csi_chan = rvin_group_csi_pad_to_chan(link->source->index);
> +
> +     /*
> +      * Figure out which VIN node is the subgroup master.
> +      *
> +      * VIN0-3 are controlled by VIN0
> +      * VIN4-7 are controlled by VIN4
> +      */
> +     vin_master = vin_num < 4 ? 0 : 4;
> +
> +     /* If not all devices exist something is horribly wrong */
> +     if (vin_num < 0 || csi_num < 0 || !group->vin[vin_master])
> +             goto error;
> +
> +     /* Special checking only needed for links which are to be enabled */
> +     if (notification != MEDIA_DEV_NOTIFY_PRE_LINK_CH ||
> +         !(flags & MEDIA_LNK_FL_ENABLED))
> +             goto out;
> +
> +     /* If any link in the group is in use, no new link can be enabled */
> +     if (rvin_group_in_use(group))
> +             goto error;
> +
> +     /* If the VIN already has an active link it's busy */
> +     if (media_entity_remote_pad(&link->sink->entity->pads[0]))
> +             goto error;
> +
> +     /* Build list of active links */
> +     __rvin_group_build_link_list(group, chsel_map, vin_master, 4);
> +
> +     /* Add the new proposed link */
> +     chsel_map[vin_num - vin_master].csi = csi_num;
> +     chsel_map[vin_num - vin_master].chan = csi_chan;
> +
> +     /* See if there is a chsel value which matches our link selection */
> +     chsel = __rvin_group_try_get_chsel(group, chsel_map, vin_master, 4);
> +
> +     /* No chsel can provide the requested links */
> +     if (chsel == -1)
> +             goto error;
> +
> +     /* Update chsel value at group master */
> +     rvin_set_chsel(group->vin[vin_master], chsel);
> +
> +out:
> +     mutex_unlock(&group->lock);
> +
> +     return v4l2_pipeline_link_notify(link, flags, notification);
> +error:
> +     mutex_unlock(&group->lock);
> +
> +     return -EMLINK;
> +}
> +
> +static const struct media_device_ops rvin_media_ops = {
> +     .link_notify = rvin_group_link_notify,
> +};
> +
>  /* 
> -----------------------------------------------------------------------------
>   * Gen3 CSI2 Group Allocator
>   */
> @@ -147,6 +350,8 @@ static int rvin_group_allocate(struct rvin_dev *vin)
>                       sizeof(mdev->bus_info));
>               media_device_init(mdev);
>  
> +             mdev->ops = &rvin_media_ops;
> +
>               ret = media_device_register(mdev);
>               if (ret) {
>                       vin_err(vin, "Failed to register media device\n");
> 

Reply via email to