From patchwork Wed Nov 2 13:23:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Niklas_S=C3=B6derlund?= X-Patchwork-Id: 9409101 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8828C60234 for ; Wed, 2 Nov 2016 13:29:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 757B22A17A for ; Wed, 2 Nov 2016 13:29:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6A2212A17C; Wed, 2 Nov 2016 13:29:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D36FB2A17B for ; Wed, 2 Nov 2016 13:29:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754793AbcKBN3z (ORCPT ); Wed, 2 Nov 2016 09:29:55 -0400 Received: from smtp-4.sys.kth.se ([130.237.48.193]:49486 "EHLO smtp-4.sys.kth.se" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754175AbcKBN3q (ORCPT ); Wed, 2 Nov 2016 09:29:46 -0400 Received: from smtp-4.sys.kth.se (localhost.localdomain [127.0.0.1]) by smtp-4.sys.kth.se (Postfix) with ESMTP id 5D96B32A9; Wed, 2 Nov 2016 14:29:45 +0100 (CET) X-Virus-Scanned: by amavisd-new at kth.se Received: from smtp-4.sys.kth.se ([127.0.0.1]) by smtp-4.sys.kth.se (smtp-4.sys.kth.se [127.0.0.1]) (amavisd-new, port 10024) with LMTP id plqI4FHCW6zL; Wed, 2 Nov 2016 14:29:44 +0100 (CET) X-KTH-Auth: niso [89.233.230.99] X-KTH-mail-from: niklas.soderlund+renesas@ragnatech.se Received: from bismarck.berto.se (unknown [89.233.230.99]) by smtp-4.sys.kth.se (Postfix) with ESMTPSA id 2FA8E1E7E; Wed, 2 Nov 2016 14:29:44 +0100 (CET) From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= To: Laurent Pinchart , Hans Verkuil Cc: linux-media@vger.kernel.org, linux-renesas-soc@vger.kernel.org, tomoharu.fukawa.eb@renesas.com, Sakari Ailus , =?UTF-8?q?Niklas=20S=C3=B6derlund?= Subject: [PATCH 24/32] media: rcar-vin: add link notify for Gen3 Date: Wed, 2 Nov 2016 14:23:21 +0100 Message-Id: <20161102132329.436-25-niklas.soderlund+renesas@ragnatech.se> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161102132329.436-1-niklas.soderlund+renesas@ragnatech.se> References: <20161102132329.436-1-niklas.soderlund+renesas@ragnatech.se> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add the ability to process media device link change request. Link enablement are a bit complicated on Gen3, if it's possible to enable a link depends on what other links already are enabled. On Gen3 the 8 VIN are split into two subgroups (VIN0-3 and VIN4-7) and from a routing perspective these two groups are independent of each other. Each subgroups 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 -EBUSY. Signed-off-by: Niklas Söderlund --- 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 20fe377..06876a8 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -26,6 +26,209 @@ #include "rcar-vin.h" /* ----------------------------------------------------------------------------- + * Media Controller link notification + */ + +static unsigned int rvin_group_csi_pad_to_chan(unsigned int pad) +{ + /* + * The CSI2 driver is rcar-csi2 and we know it's pad layout are + * 0: Source 1-4: Sinks so if we remove one from the pad we + * get the rcar-vin internal CSI2 channel 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->bridge[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[RVIN_SINK]; + + 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 match 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 satisfy the links have 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 exists 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 are in use, no new link can be enabled */ + if (rvin_group_in_use(group)) + goto error; + + /* If the VIN already have a active link it's busy */ + if (media_entity_remote_pad(&link->sink->entity->pads[RVIN_SINK])) + 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 match our link selection */ + chsel = __rvin_group_try_get_chsel(group, chsel_map, vin_master, 4); + + /* No chsel can provide the request links */ + if (chsel == -1) + goto error; + + /* Update chsel value at group master */ + if (rvin_set_chsel(group->vin[vin_master], chsel)) + goto error; + +out: + mutex_unlock(&group->lock); + + return v4l2_pipeline_link_notify(link, flags, notification); +error: + mutex_unlock(&group->lock); + + return -EBUSY; +} + + +static const struct media_device_ops rvin_media_ops = { + .link_notify = rvin_group_link_notify, +}; + +/* ----------------------------------------------------------------------------- * Gen3 CSI2 Group Allocator */ @@ -100,6 +303,8 @@ static struct rvin_group *rvin_group_allocate(struct rvin_dev *vin) mdev->driver_version = LINUX_VERSION_CODE; 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");