From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753560AbdLMPd6 (ORCPT ); Wed, 13 Dec 2017 10:33:58 -0500 Received: from mail.free-electrons.com ([62.4.15.54]:37727 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753441AbdLMPdl (ORCPT ); Wed, 13 Dec 2017 10:33:41 -0500 From: Maxime Ripard To: Daniel Vetter , David Airlie , Chen-Yu Tsai , Maxime Ripard Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Thomas Petazzoni , thomas@vitsch.nl Subject: [PATCH 7/8] drm/sun4i: sun4i_layer: Add a custom atomic_check for the frontend Date: Wed, 13 Dec 2017 16:33:31 +0100 Message-Id: <4fbc349e2c3aa2efeadb55ea120c64b56c707cb4.1513178989.git-series.maxime.ripard@free-electrons.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Now that we have everything in place, we can start enabling the frontend. This is more difficult than one would assume since there can only be one plane using the frontend per-backend. We therefore need to make sure that the userspace will not try to setup multiple planes using it, since that would be impossible. In order to prevent that, we can create an atomic_check callback that will check that only one plane will effectively make use of the frontend in a given configuration, and will toggle the switch in that plane state so that the proper setup function can do their role. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/sun4i/sun4i_backend.c | 65 ++++++++++++++++++++++++++++- drivers/gpu/drm/sun4i/sun4i_backend.h | 2 +- 2 files changed, 67 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index f1d19767c55d..a7b87a12990e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -271,6 +272,69 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, return 0; } +static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state) +{ + u16 src_h = state->src_h >> 16; + u16 src_w = state->src_w >> 16; + + DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n", + src_w, src_h, state->crtc_w, state->crtc_h); + + if ((state->crtc_h != src_h) || (state->crtc_w != src_w)) + return true; + + return false; +} + +static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state) +{ + struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane); + struct sun4i_backend *backend = layer->backend; + + if (IS_ERR(backend->frontend)) + return false; + + return sun4i_backend_plane_uses_scaler(state); +} + +static int sun4i_backend_atomic_check(struct sunxi_engine *engine, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_device *drm = state->dev; + struct drm_plane *plane; + unsigned int num_frontend_planes = 0; + + DRM_DEBUG_DRIVER("Starting checking our planes\n"); + + if (!crtc_state->planes_changed) + return 0; + + drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + struct sun4i_layer_state *layer_state = + state_to_sun4i_layer_state(plane_state); + + if (sun4i_backend_plane_uses_frontend(plane_state)) { + DRM_DEBUG_DRIVER("Using the frontend for plane %d\n", + plane->index); + + layer_state->uses_frontend = true; + num_frontend_planes++; + } else { + layer_state->uses_frontend = false; + } + } + + if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { + DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); + return -EINVAL; + } + + return 0; +} + static int sun4i_backend_init_sat(struct device *dev) { struct sun4i_backend *backend = dev_get_drvdata(dev); int ret; @@ -384,6 +448,7 @@ static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv, } static const struct sunxi_engine_ops sun4i_backend_engine_ops = { + .atomic_check = sun4i_backend_atomic_check, .commit = sun4i_backend_commit, .layers_init = sun4i_layers_init, .apply_color_correction = sun4i_backend_apply_color_correction, diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 636a51521e77..3b5531440fa3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -144,6 +144,8 @@ #define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00 #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) +#define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 + struct sun4i_backend { struct sunxi_engine engine; struct sun4i_frontend *frontend; -- git-series 0.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: maxime.ripard@free-electrons.com (Maxime Ripard) Date: Wed, 13 Dec 2017 16:33:31 +0100 Subject: [PATCH 7/8] drm/sun4i: sun4i_layer: Add a custom atomic_check for the frontend In-Reply-To: References: Message-ID: <4fbc349e2c3aa2efeadb55ea120c64b56c707cb4.1513178989.git-series.maxime.ripard@free-electrons.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Now that we have everything in place, we can start enabling the frontend. This is more difficult than one would assume since there can only be one plane using the frontend per-backend. We therefore need to make sure that the userspace will not try to setup multiple planes using it, since that would be impossible. In order to prevent that, we can create an atomic_check callback that will check that only one plane will effectively make use of the frontend in a given configuration, and will toggle the switch in that plane state so that the proper setup function can do their role. Signed-off-by: Maxime Ripard --- drivers/gpu/drm/sun4i/sun4i_backend.c | 65 ++++++++++++++++++++++++++++- drivers/gpu/drm/sun4i/sun4i_backend.h | 2 +- 2 files changed, 67 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index f1d19767c55d..a7b87a12990e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -271,6 +272,69 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, return 0; } +static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state) +{ + u16 src_h = state->src_h >> 16; + u16 src_w = state->src_w >> 16; + + DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n", + src_w, src_h, state->crtc_w, state->crtc_h); + + if ((state->crtc_h != src_h) || (state->crtc_w != src_w)) + return true; + + return false; +} + +static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state) +{ + struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane); + struct sun4i_backend *backend = layer->backend; + + if (IS_ERR(backend->frontend)) + return false; + + return sun4i_backend_plane_uses_scaler(state); +} + +static int sun4i_backend_atomic_check(struct sunxi_engine *engine, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_device *drm = state->dev; + struct drm_plane *plane; + unsigned int num_frontend_planes = 0; + + DRM_DEBUG_DRIVER("Starting checking our planes\n"); + + if (!crtc_state->planes_changed) + return 0; + + drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + struct sun4i_layer_state *layer_state = + state_to_sun4i_layer_state(plane_state); + + if (sun4i_backend_plane_uses_frontend(plane_state)) { + DRM_DEBUG_DRIVER("Using the frontend for plane %d\n", + plane->index); + + layer_state->uses_frontend = true; + num_frontend_planes++; + } else { + layer_state->uses_frontend = false; + } + } + + if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { + DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); + return -EINVAL; + } + + return 0; +} + static int sun4i_backend_init_sat(struct device *dev) { struct sun4i_backend *backend = dev_get_drvdata(dev); int ret; @@ -384,6 +448,7 @@ static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv, } static const struct sunxi_engine_ops sun4i_backend_engine_ops = { + .atomic_check = sun4i_backend_atomic_check, .commit = sun4i_backend_commit, .layers_init = sun4i_layers_init, .apply_color_correction = sun4i_backend_apply_color_correction, diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index 636a51521e77..3b5531440fa3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -144,6 +144,8 @@ #define SUN4I_BACKEND_HWCCOLORTAB_OFF 0x4c00 #define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) +#define SUN4I_BACKEND_NUM_FRONTEND_LAYERS 1 + struct sun4i_backend { struct sunxi_engine engine; struct sun4i_frontend *frontend; -- git-series 0.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: Maxime Ripard Subject: [PATCH 7/8] drm/sun4i: sun4i_layer: Add a custom atomic_check for the frontend Date: Wed, 13 Dec 2017 16:33:31 +0100 Message-ID: <4fbc349e2c3aa2efeadb55ea120c64b56c707cb4.1513178989.git-series.maxime.ripard@free-electrons.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: Received: from mail.free-electrons.com (mail.free-electrons.com [62.4.15.54]) by gabe.freedesktop.org (Postfix) with ESMTP id 03E346E51D for ; Wed, 13 Dec 2017 15:33:41 +0000 (UTC) In-Reply-To: In-Reply-To: References: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Daniel Vetter , David Airlie , Chen-Yu Tsai , Maxime Ripard Cc: linux-arm-kernel@lists.infradead.org, Thomas Petazzoni , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, thomas@vitsch.nl List-Id: dri-devel@lists.freedesktop.org Tm93IHRoYXQgd2UgaGF2ZSBldmVyeXRoaW5nIGluIHBsYWNlLCB3ZSBjYW4gc3RhcnQgZW5hYmxp bmcgdGhlIGZyb250ZW5kLgpUaGlzIGlzIG1vcmUgZGlmZmljdWx0IHRoYW4gb25lIHdvdWxkIGFz c3VtZSBzaW5jZSB0aGVyZSBjYW4gb25seSBiZSBvbmUKcGxhbmUgdXNpbmcgdGhlIGZyb250ZW5k IHBlci1iYWNrZW5kLgoKV2UgdGhlcmVmb3JlIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIHVz ZXJzcGFjZSB3aWxsIG5vdCB0cnkgdG8gc2V0dXAKbXVsdGlwbGUgcGxhbmVzIHVzaW5nIGl0LCBz aW5jZSB0aGF0IHdvdWxkIGJlIGltcG9zc2libGUuIEluIG9yZGVyIHRvCnByZXZlbnQgdGhhdCwg d2UgY2FuIGNyZWF0ZSBhbiBhdG9taWNfY2hlY2sgY2FsbGJhY2sgdGhhdCB3aWxsIGNoZWNrIHRo YXQKb25seSBvbmUgcGxhbmUgd2lsbCBlZmZlY3RpdmVseSBtYWtlIHVzZSBvZiB0aGUgZnJvbnRl bmQgaW4gYSBnaXZlbgpjb25maWd1cmF0aW9uLCBhbmQgd2lsbCB0b2dnbGUgdGhlIHN3aXRjaCBp biB0aGF0IHBsYW5lIHN0YXRlIHNvIHRoYXQgdGhlCnByb3BlciBzZXR1cCBmdW5jdGlvbiBjYW4g ZG8gdGhlaXIgcm9sZS4KClNpZ25lZC1vZmYtYnk6IE1heGltZSBSaXBhcmQgPG1heGltZS5yaXBh cmRAZnJlZS1lbGVjdHJvbnMuY29tPgotLS0KIGRyaXZlcnMvZ3B1L2RybS9zdW40aS9zdW40aV9i YWNrZW5kLmMgfCA2NSArKysrKysrKysrKysrKysrKysrKysrKysrKysrLQogZHJpdmVycy9ncHUv ZHJtL3N1bjRpL3N1bjRpX2JhY2tlbmQuaCB8ICAyICstCiAyIGZpbGVzIGNoYW5nZWQsIDY3IGlu c2VydGlvbnMoKykKCmRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfYmFj a2VuZC5jIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2JhY2tlbmQuYwppbmRleCBmMWQx OTc2N2M1NWQuLmE3Yjg3YTEyOTkwZSAxMDA2NDQKLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRp L3N1bjRpX2JhY2tlbmQuYworKysgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfYmFja2Vu ZC5jCkBAIC0xMSw2ICsxMSw3IEBACiAgKi8KIAogI2luY2x1ZGUgPGRybS9kcm1QLmg+CisjaW5j bHVkZSA8ZHJtL2RybV9hdG9taWMuaD4KICNpbmNsdWRlIDxkcm0vZHJtX2F0b21pY19oZWxwZXIu aD4KICNpbmNsdWRlIDxkcm0vZHJtX2NydGMuaD4KICNpbmNsdWRlIDxkcm0vZHJtX2NydGNfaGVs cGVyLmg+CkBAIC0yNzEsNiArMjcyLDY5IEBAIGludCBzdW40aV9iYWNrZW5kX3VwZGF0ZV9sYXll cl9idWZmZXIoc3RydWN0IHN1bjRpX2JhY2tlbmQgKmJhY2tlbmQsCiAJcmV0dXJuIDA7CiB9CiAK K3N0YXRpYyBib29sIHN1bjRpX2JhY2tlbmRfcGxhbmVfdXNlc19zY2FsZXIoc3RydWN0IGRybV9w bGFuZV9zdGF0ZSAqc3RhdGUpCit7CisJdTE2IHNyY19oID0gc3RhdGUtPnNyY19oID4+IDE2Owor CXUxNiBzcmNfdyA9IHN0YXRlLT5zcmNfdyA+PiAxNjsKKworCURSTV9ERUJVR19EUklWRVIoIklu cHV0IHNpemUgJWR4JWQsIG91dHB1dCBzaXplICVkeCVkXG4iLAorCQkJIHNyY193LCBzcmNfaCwg c3RhdGUtPmNydGNfdywgc3RhdGUtPmNydGNfaCk7CisKKwlpZiAoKHN0YXRlLT5jcnRjX2ggIT0g c3JjX2gpIHx8IChzdGF0ZS0+Y3J0Y193ICE9IHNyY193KSkKKwkJcmV0dXJuIHRydWU7CisKKwly ZXR1cm4gZmFsc2U7Cit9CisKK3N0YXRpYyBib29sIHN1bjRpX2JhY2tlbmRfcGxhbmVfdXNlc19m cm9udGVuZChzdHJ1Y3QgZHJtX3BsYW5lX3N0YXRlICpzdGF0ZSkKK3sKKwlzdHJ1Y3Qgc3VuNGlf bGF5ZXIgKmxheWVyID0gcGxhbmVfdG9fc3VuNGlfbGF5ZXIoc3RhdGUtPnBsYW5lKTsKKwlzdHJ1 Y3Qgc3VuNGlfYmFja2VuZCAqYmFja2VuZCA9IGxheWVyLT5iYWNrZW5kOworCisJaWYgKElTX0VS UihiYWNrZW5kLT5mcm9udGVuZCkpCisJCXJldHVybiBmYWxzZTsKKworCXJldHVybiBzdW40aV9i YWNrZW5kX3BsYW5lX3VzZXNfc2NhbGVyKHN0YXRlKTsKK30KKworc3RhdGljIGludCBzdW40aV9i YWNrZW5kX2F0b21pY19jaGVjayhzdHJ1Y3Qgc3VueGlfZW5naW5lICplbmdpbmUsCisJCQkJICAg ICAgc3RydWN0IGRybV9jcnRjX3N0YXRlICpjcnRjX3N0YXRlKQoreworCXN0cnVjdCBkcm1fYXRv bWljX3N0YXRlICpzdGF0ZSA9IGNydGNfc3RhdGUtPnN0YXRlOworCXN0cnVjdCBkcm1fZGV2aWNl ICpkcm0gPSBzdGF0ZS0+ZGV2OworCXN0cnVjdCBkcm1fcGxhbmUgKnBsYW5lOworCXVuc2lnbmVk IGludCBudW1fZnJvbnRlbmRfcGxhbmVzID0gMDsKKworCURSTV9ERUJVR19EUklWRVIoIlN0YXJ0 aW5nIGNoZWNraW5nIG91ciBwbGFuZXNcbiIpOworCisJaWYgKCFjcnRjX3N0YXRlLT5wbGFuZXNf Y2hhbmdlZCkKKwkJcmV0dXJuIDA7CisKKwlkcm1fZm9yX2VhY2hfcGxhbmVfbWFzayhwbGFuZSwg ZHJtLCBjcnRjX3N0YXRlLT5wbGFuZV9tYXNrKSB7CisJCXN0cnVjdCBkcm1fcGxhbmVfc3RhdGUg KnBsYW5lX3N0YXRlID0KKwkJCWRybV9hdG9taWNfZ2V0X3BsYW5lX3N0YXRlKHN0YXRlLCBwbGFu ZSk7CisJCXN0cnVjdCBzdW40aV9sYXllcl9zdGF0ZSAqbGF5ZXJfc3RhdGUgPQorCQkJc3RhdGVf dG9fc3VuNGlfbGF5ZXJfc3RhdGUocGxhbmVfc3RhdGUpOworCisJCWlmIChzdW40aV9iYWNrZW5k X3BsYW5lX3VzZXNfZnJvbnRlbmQocGxhbmVfc3RhdGUpKSB7CisJCQlEUk1fREVCVUdfRFJJVkVS KCJVc2luZyB0aGUgZnJvbnRlbmQgZm9yIHBsYW5lICVkXG4iLAorCQkJCQkgcGxhbmUtPmluZGV4 KTsKKworCQkJbGF5ZXJfc3RhdGUtPnVzZXNfZnJvbnRlbmQgPSB0cnVlOworCQkJbnVtX2Zyb250 ZW5kX3BsYW5lcysrOworCQl9IGVsc2UgeworCQkJbGF5ZXJfc3RhdGUtPnVzZXNfZnJvbnRlbmQg PSBmYWxzZTsKKwkJfQorCX0KKworCWlmIChudW1fZnJvbnRlbmRfcGxhbmVzID4gU1VONElfQkFD S0VORF9OVU1fRlJPTlRFTkRfTEFZRVJTKSB7CisJCURSTV9ERUJVR19EUklWRVIoIlRvbyBtYW55 IHBsYW5lcyBnb2luZyB0aHJvdWdoIHRoZSBmcm9udGVuZCwgcmVqZWN0aW5nXG4iKTsKKwkJcmV0 dXJuIC1FSU5WQUw7CisJfQorCisJcmV0dXJuIDA7Cit9CisKIHN0YXRpYyBpbnQgc3VuNGlfYmFj a2VuZF9pbml0X3NhdChzdHJ1Y3QgZGV2aWNlICpkZXYpIHsKIAlzdHJ1Y3Qgc3VuNGlfYmFja2Vu ZCAqYmFja2VuZCA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOwogCWludCByZXQ7CkBAIC0zODQsNiAr NDQ4LDcgQEAgc3RhdGljIHN0cnVjdCBzdW40aV9mcm9udGVuZCAqc3VuNGlfYmFja2VuZF9maW5k X2Zyb250ZW5kKHN0cnVjdCBzdW40aV9kcnYgKmRydiwKIH0KIAogc3RhdGljIGNvbnN0IHN0cnVj dCBzdW54aV9lbmdpbmVfb3BzIHN1bjRpX2JhY2tlbmRfZW5naW5lX29wcyA9IHsKKwkuYXRvbWlj X2NoZWNrCQkJPSBzdW40aV9iYWNrZW5kX2F0b21pY19jaGVjaywKIAkuY29tbWl0CQkJCT0gc3Vu NGlfYmFja2VuZF9jb21taXQsCiAJLmxheWVyc19pbml0CQkJPSBzdW40aV9sYXllcnNfaW5pdCwK IAkuYXBwbHlfY29sb3JfY29ycmVjdGlvbgkJPSBzdW40aV9iYWNrZW5kX2FwcGx5X2NvbG9yX2Nv cnJlY3Rpb24sCmRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfYmFja2Vu ZC5oIGIvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1bjRpX2JhY2tlbmQuaAppbmRleCA2MzZhNTE1 MjFlNzcuLjNiNTUzMTQ0MGZhMyAxMDA2NDQKLS0tIGEvZHJpdmVycy9ncHUvZHJtL3N1bjRpL3N1 bjRpX2JhY2tlbmQuaAorKysgYi9kcml2ZXJzL2dwdS9kcm0vc3VuNGkvc3VuNGlfYmFja2VuZC5o CkBAIC0xNDQsNiArMTQ0LDggQEAKICNkZWZpbmUgU1VONElfQkFDS0VORF9IV0NDT0xPUlRBQl9P RkYJCTB4NGMwMAogI2RlZmluZSBTVU40SV9CQUNLRU5EX1BJUEVfT0ZGKHApCQkoMHg1MDAwICsg KDB4NDAwICogKHApKSkKIAorI2RlZmluZSBTVU40SV9CQUNLRU5EX05VTV9GUk9OVEVORF9MQVlF UlMJMQorCiBzdHJ1Y3Qgc3VuNGlfYmFja2VuZCB7CiAJc3RydWN0IHN1bnhpX2VuZ2luZQllbmdp bmU7CiAJc3RydWN0IHN1bjRpX2Zyb250ZW5kCSpmcm9udGVuZDsKLS0gCmdpdC1zZXJpZXMgMC45 LjEKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KZHJpLWRl dmVsIG1haWxpbmcgbGlzdApkcmktZGV2ZWxAbGlzdHMuZnJlZWRlc2t0b3Aub3JnCmh0dHBzOi8v bGlzdHMuZnJlZWRlc2t0b3Aub3JnL21haWxtYW4vbGlzdGluZm8vZHJpLWRldmVsCg==