From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sebastian Reichel Subject: Re: [PATCH v2 45/49] drm/omap: Add support for drm_bridge Date: Sat, 9 Feb 2019 04:26:12 +0100 Message-ID: <20190209032612.brqeuplekkccf52t@earth.universe> References: <20190111035120.20668-1-laurent.pinchart@ideasonboard.com> <20190111035120.20668-46-laurent.pinchart@ideasonboard.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0620708457==" Return-path: Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by gabe.freedesktop.org (Postfix) with ESMTPS id 60B1A6E429 for ; Sat, 9 Feb 2019 18:57:41 +0000 (UTC) In-Reply-To: <20190111035120.20668-46-laurent.pinchart@ideasonboard.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Laurent Pinchart Cc: Tomi Valkeinen , dri-devel@lists.freedesktop.org List-Id: dri-devel@lists.freedesktop.org --===============0620708457== Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="j55q2tppkg7tv7zk" Content-Disposition: inline --j55q2tppkg7tv7zk Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi, On Fri, Jan 11, 2019 at 05:51:16AM +0200, Laurent Pinchart wrote: > Hook up drm_bridge support in the omapdrm driver. Despite the recent > extensive preparation work, this is a rather intrusive change, as the > management of outputs needs to be adapted through the driver to handle > both omap_dss_device and drm_bridge. >=20 > Connector creation is skipped when using a drm_bridge, as the bridge > creates the connector internally. This creates issues with systems that > split connector operations (such as modes retrieval and hot-plug > detection) across different bridges. These systems can't be supported > using drm_bridge for now (their support through the omap_dss_device > infrastructure is not affected), this will be fixed in subsequent > changes. >=20 > Signed-off-by: Laurent Pinchart > Reviewed-by: Sebastian Reichel > --- Tested-by: Sebastian Reichel -- Sebastian > Changes since v1: >=20 > - Fix typo in function name (updata -> update) > --- > drivers/gpu/drm/omapdrm/dss/base.c | 27 ++++++++-- > drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 + > drivers/gpu/drm/omapdrm/dss/output.c | 21 +++++--- > drivers/gpu/drm/omapdrm/omap_connector.c | 16 ++++-- > drivers/gpu/drm/omapdrm/omap_connector.h | 1 - > drivers/gpu/drm/omapdrm/omap_crtc.c | 2 +- > drivers/gpu/drm/omapdrm/omap_drv.c | 69 +++++++++++++++++------- > drivers/gpu/drm/omapdrm/omap_encoder.c | 69 ++++++++++++++---------- > 8 files changed, 145 insertions(+), 61 deletions(-) >=20 > diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm= /dss/base.c > index 81ea0f55cd75..09c9f2971aa2 100644 > --- a/drivers/gpu/drm/omapdrm/dss/base.c > +++ b/drivers/gpu/drm/omapdrm/dss/base.c > @@ -19,6 +19,7 @@ > #include > #include > #include > +#include > =20 > #include "dss.h" > #include "omapdss.h" > @@ -156,7 +157,7 @@ struct omap_dss_device *omapdss_device_next_output(st= ruct omap_dss_device *from) > goto done; > } > =20 > - if (dssdev->id && dssdev->next) > + if (dssdev->id && (dssdev->next || dssdev->bridge)) > goto done; > } > =20 > @@ -184,7 +185,18 @@ int omapdss_device_connect(struct dss_device *dss, > { > int ret; > =20 > - dev_dbg(dst->dev, "connect\n"); > + dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n", > + src ? dev_name(src->dev) : "NULL", > + dst ? dev_name(dst->dev) : "NULL"); > + > + if (!dst) { > + /* > + * The destination is NULL when the source is connected to a > + * bridge instead of a DSS device. Stop here, we will attach the > + * bridge later when we will have a DRM encoder. > + */ > + return src && src->bridge ? 0 : -EINVAL; > + } > =20 > if (omapdss_device_is_connected(dst)) > return -EBUSY; > @@ -204,7 +216,16 @@ EXPORT_SYMBOL_GPL(omapdss_device_connect); > void omapdss_device_disconnect(struct omap_dss_device *src, > struct omap_dss_device *dst) > { > - dev_dbg(dst->dev, "disconnect\n"); > + struct dss_device *dss =3D src ? src->dss : dst->dss; > + > + dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n", > + src ? dev_name(src->dev) : "NULL", > + dst ? dev_name(dst->dev) : "NULL"); > + > + if (!dst) { > + WARN_ON(!src->bridge); > + return; > + } > =20 > if (!dst->id && !omapdss_device_is_connected(dst)) { > WARN_ON(!dst->display); > diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omap= drm/dss/omapdss.h > index ab5467a1e92c..f47e9b94288f 100644 > --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h > +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h > @@ -410,6 +410,7 @@ struct omap_dss_device { > =20 > struct dss_device *dss; > struct omap_dss_device *next; > + struct drm_bridge *bridge; > =20 > struct list_head list; > =20 > diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapd= rm/dss/output.c > index f25ecfd26534..2a53025c2fde 100644 > --- a/drivers/gpu/drm/omapdrm/dss/output.c > +++ b/drivers/gpu/drm/omapdrm/dss/output.c > @@ -20,25 +20,34 @@ > #include > #include > #include > +#include > =20 > #include "dss.h" > #include "omapdss.h" > =20 > int omapdss_device_init_output(struct omap_dss_device *out) > { > - out->next =3D omapdss_of_find_connected_device(out->dev->of_node, 0); > - if (IS_ERR(out->next)) { > - if (PTR_ERR(out->next) !=3D -EPROBE_DEFER) > - dev_err(out->dev, "failed to find video sink\n"); > - return PTR_ERR(out->next); > + struct device_node *remote_node; > + > + remote_node =3D of_graph_get_remote_node(out->dev->of_node, 0, 0); > + if (!remote_node) { > + dev_dbg(out->dev, "failed to find video sink\n"); > + return 0; > } > =20 > + out->next =3D omapdss_find_device_by_node(remote_node); > + out->bridge =3D of_drm_find_bridge(remote_node); > + > + of_node_put(remote_node); > + > if (out->next && out->type !=3D out->next->type) { > dev_err(out->dev, "output type and display type don't match\n"); > + omapdss_device_put(out->next); > + out->next =3D NULL; > return -EINVAL; > } > =20 > - return 0; > + return out->next || out->bridge ? 0 : -EPROBE_DEFER; > } > EXPORT_SYMBOL(omapdss_device_init_output); > =20 > diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/o= mapdrm/omap_connector.c > index 487603c56b08..f528baa80114 100644 > --- a/drivers/gpu/drm/omapdrm/omap_connector.c > +++ b/drivers/gpu/drm/omapdrm/omap_connector.c > @@ -304,9 +304,16 @@ static const struct drm_connector_helper_funcs omap_= connector_helper_funcs =3D { > .mode_valid =3D omap_connector_mode_valid, > }; > =20 > -static int omap_connector_get_type(struct omap_dss_device *display) > +static int omap_connector_get_type(struct omap_dss_device *output) > { > - switch (display->type) { > + struct omap_dss_device *display; > + enum omap_display_type type; > + > + display =3D omapdss_display_get(output); > + type =3D display->type; > + omapdss_device_put(display); > + > + switch (type) { > case OMAP_DISPLAY_TYPE_HDMI: > return DRM_MODE_CONNECTOR_HDMIA; > case OMAP_DISPLAY_TYPE_DVI: > @@ -329,14 +336,13 @@ static int omap_connector_get_type(struct omap_dss_= device *display) > /* initialize connector */ > struct drm_connector *omap_connector_init(struct drm_device *dev, > struct omap_dss_device *output, > - struct omap_dss_device *display, > struct drm_encoder *encoder) > { > struct drm_connector *connector =3D NULL; > struct omap_connector *omap_connector; > struct omap_dss_device *dssdev; > =20 > - DBG("%s", display->name); > + DBG("%s", output->name); > =20 > omap_connector =3D kzalloc(sizeof(*omap_connector), GFP_KERNEL); > if (!omap_connector) > @@ -349,7 +355,7 @@ struct drm_connector *omap_connector_init(struct drm_= device *dev, > connector->doublescan_allowed =3D 0; > =20 > drm_connector_init(dev, connector, &omap_connector_funcs, > - omap_connector_get_type(display)); > + omap_connector_get_type(output)); > drm_connector_helper_add(connector, &omap_connector_helper_funcs); > =20 > /* > diff --git a/drivers/gpu/drm/omapdrm/omap_connector.h b/drivers/gpu/drm/o= mapdrm/omap_connector.h > index 6b7d4d95e32b..608085219336 100644 > --- a/drivers/gpu/drm/omapdrm/omap_connector.h > +++ b/drivers/gpu/drm/omapdrm/omap_connector.h > @@ -31,7 +31,6 @@ struct omap_dss_device; > =20 > struct drm_connector *omap_connector_init(struct drm_device *dev, > struct omap_dss_device *output, > - struct omap_dss_device *display, > struct drm_encoder *encoder); > bool omap_connector_get_hdmi_mode(struct drm_connector *connector); > void omap_connector_enable_hpd(struct drm_connector *connector); > diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdr= m/omap_crtc.c > index 0cea3824d3a6..4486152fd6cc 100644 > --- a/drivers/gpu/drm/omapdrm/omap_crtc.c > +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c > @@ -671,7 +671,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *de= v, > &omap_crtc_funcs, NULL); > if (ret < 0) { > dev_err(dev->dev, "%s(): could not init crtc for: %s\n", > - __func__, pipe->display->name); > + __func__, pipe->output->name); > kfree(omap_crtc); > return ERR_PTR(ret); > } > diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm= /omap_drv.c > index 6b91f44e5a60..35c4669dc69d 100644 > --- a/drivers/gpu/drm/omapdrm/omap_drv.c > +++ b/drivers/gpu/drm/omapdrm/omap_drv.c > @@ -140,9 +140,7 @@ static void omap_disconnect_pipelines(struct drm_devi= ce *ddev) > omapdss_device_disconnect(NULL, pipe->output); > =20 > omapdss_device_put(pipe->output); > - omapdss_device_put(pipe->display); > pipe->output =3D NULL; > - pipe->display =3D NULL; > } > =20 > memset(&priv->channels, 0, sizeof(priv->channels)); > @@ -169,7 +167,6 @@ static int omap_connect_pipelines(struct drm_device *= ddev) > =20 > pipe =3D &priv->pipes[priv->num_pipes++]; > pipe->output =3D omapdss_device_get(output); > - pipe->display =3D omapdss_display_get(output); > =20 > if (priv->num_pipes =3D=3D ARRAY_SIZE(priv->pipes)) { > /* To balance the 'for_each_dss_output' loop */ > @@ -207,6 +204,28 @@ static int omap_modeset_init_properties(struct drm_d= evice *dev) > return 0; > } > =20 > +static int omap_display_id(struct omap_dss_device *output) > +{ > + struct device_node *node =3D NULL; > + > + if (output->next) { > + struct omap_dss_device *display; > + > + display =3D omapdss_display_get(output); > + node =3D display->dev->of_node; > + omapdss_device_put(display); > + } else { > + struct drm_bridge *bridge =3D output->bridge; > + > + while (bridge->next) > + bridge =3D bridge->next; > + > + node =3D bridge->of_node; > + } > + > + return node ? of_alias_get_id(node, "display") : -ENODEV; > +} > + > static int omap_modeset_init(struct drm_device *dev) > { > struct omap_drm_private *priv =3D dev->dev_private; > @@ -262,7 +281,10 @@ static int omap_modeset_init(struct drm_device *dev) > priv->planes[priv->num_planes++] =3D plane; > } > =20 > - /* Create the encoders and get the pipelines aliases. */ > + /* > + * Create the encoders, attach the bridges and get the pipeline alias > + * IDs. > + */ > for (i =3D 0; i < priv->num_pipes; i++) { > struct omap_drm_pipeline *pipe =3D &priv->pipes[i]; > int id; > @@ -271,7 +293,14 @@ static int omap_modeset_init(struct drm_device *dev) > if (!pipe->encoder) > return -ENOMEM; > =20 > - id =3D of_alias_get_id(pipe->display->dev->of_node, "display"); > + if (pipe->output->bridge) { > + ret =3D drm_bridge_attach(pipe->encoder, > + pipe->output->bridge, NULL); > + if (ret < 0) > + return ret; > + } > + > + id =3D omap_display_id(pipe->output); > pipe->alias_id =3D id >=3D 0 ? id : i; > } > =20 > @@ -297,16 +326,16 @@ static int omap_modeset_init(struct drm_device *dev) > for (i =3D 0; i < priv->num_pipes; i++) { > struct omap_drm_pipeline *pipe =3D &priv->pipes[i]; > struct drm_encoder *encoder =3D pipe->encoder; > - struct drm_connector *connector; > struct drm_crtc *crtc; > =20 > - connector =3D omap_connector_init(dev, pipe->output, > - pipe->display, encoder); > - if (!connector) > - return -ENOMEM; > + if (!pipe->output->bridge) { > + pipe->connector =3D omap_connector_init(dev, pipe->output, > + encoder); > + if (!pipe->connector) > + return -ENOMEM; > =20 > - drm_connector_attach_encoder(connector, encoder); > - pipe->connector =3D connector; > + drm_connector_attach_encoder(pipe->connector, encoder); > + } > =20 > crtc =3D omap_crtc_init(dev, pipe, priv->planes[i]); > if (IS_ERR(crtc)) > @@ -350,10 +379,12 @@ static int omap_modeset_init(struct drm_device *dev) > static void omap_modeset_enable_external_hpd(struct drm_device *ddev) > { > struct omap_drm_private *priv =3D ddev->dev_private; > - int i; > + unsigned int i; > =20 > - for (i =3D 0; i < priv->num_pipes; i++) > - omap_connector_enable_hpd(priv->pipes[i].connector); > + for (i =3D 0; i < priv->num_pipes; i++) { > + if (priv->pipes[i].connector) > + omap_connector_enable_hpd(priv->pipes[i].connector); > + } > } > =20 > /* > @@ -362,10 +393,12 @@ static void omap_modeset_enable_external_hpd(struct= drm_device *ddev) > static void omap_modeset_disable_external_hpd(struct drm_device *ddev) > { > struct omap_drm_private *priv =3D ddev->dev_private; > - int i; > + unsigned int i; > =20 > - for (i =3D 0; i < priv->num_pipes; i++) > - omap_connector_disable_hpd(priv->pipes[i].connector); > + for (i =3D 0; i < priv->num_pipes; i++) { > + if (priv->pipes[i].connector) > + omap_connector_disable_hpd(priv->pipes[i].connector); > + } > } > =20 > /* > diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/oma= pdrm/omap_encoder.c > index e71d359a8f07..76f94cc0c0cf 100644 > --- a/drivers/gpu/drm/omapdrm/omap_encoder.c > +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c > @@ -51,6 +51,34 @@ static const struct drm_encoder_funcs omap_encoder_fun= cs =3D { > .destroy =3D omap_encoder_destroy, > }; > =20 > +static void omap_encoder_update_videomode_flags(struct videomode *vm, > + u32 bus_flags) > +{ > + if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW | > + DISPLAY_FLAGS_DE_HIGH))) { > + if (bus_flags & DRM_BUS_FLAG_DE_LOW) > + vm->flags |=3D DISPLAY_FLAGS_DE_LOW; > + else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) > + vm->flags |=3D DISPLAY_FLAGS_DE_HIGH; > + } > + > + if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | > + DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { > + if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) > + vm->flags |=3D DISPLAY_FLAGS_PIXDATA_POSEDGE; > + else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) > + vm->flags |=3D DISPLAY_FLAGS_PIXDATA_NEGEDGE; > + } > + > + if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE | > + DISPLAY_FLAGS_SYNC_NEGEDGE))) { > + if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE) > + vm->flags |=3D DISPLAY_FLAGS_SYNC_POSEDGE; > + else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE) > + vm->flags |=3D DISPLAY_FLAGS_SYNC_NEGEDGE; > + } > +} > + > static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder, > struct drm_display_mode *adjusted_mode) > { > @@ -87,7 +115,9 @@ static void omap_encoder_mode_set(struct drm_encoder *= encoder, > struct drm_display_mode *adjusted_mode) > { > struct omap_encoder *omap_encoder =3D to_omap_encoder(encoder); > + struct omap_dss_device *output =3D omap_encoder->output; > struct omap_dss_device *dssdev; > + struct drm_bridge *bridge; > struct videomode vm =3D { 0 }; > =20 > drm_display_mode_to_videomode(adjusted_mode, &vm); > @@ -101,44 +131,29 @@ static void omap_encoder_mode_set(struct drm_encode= r *encoder, > * > * A better solution is to use DRM's bus-flags through the whole driver. > */ > - for (dssdev =3D omap_encoder->output; dssdev; dssdev =3D dssdev->next) { > - unsigned long bus_flags =3D dssdev->bus_flags; > - > - if (!(vm.flags & (DISPLAY_FLAGS_DE_LOW | > - DISPLAY_FLAGS_DE_HIGH))) { > - if (bus_flags & DRM_BUS_FLAG_DE_LOW) > - vm.flags |=3D DISPLAY_FLAGS_DE_LOW; > - else if (bus_flags & DRM_BUS_FLAG_DE_HIGH) > - vm.flags |=3D DISPLAY_FLAGS_DE_HIGH; > - } > + for (dssdev =3D output; dssdev; dssdev =3D dssdev->next) > + omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags); > =20 > - if (!(vm.flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE | > - DISPLAY_FLAGS_PIXDATA_NEGEDGE))) { > - if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) > - vm.flags |=3D DISPLAY_FLAGS_PIXDATA_POSEDGE; > - else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) > - vm.flags |=3D DISPLAY_FLAGS_PIXDATA_NEGEDGE; > - } > + for (bridge =3D output->bridge; bridge; bridge =3D bridge->next) { > + u32 bus_flags; > =20 > - if (!(vm.flags & (DISPLAY_FLAGS_SYNC_POSEDGE | > - DISPLAY_FLAGS_SYNC_NEGEDGE))) { > - if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE) > - vm.flags |=3D DISPLAY_FLAGS_SYNC_POSEDGE; > - else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE) > - vm.flags |=3D DISPLAY_FLAGS_SYNC_NEGEDGE; > - } > + if (!bridge->timings) > + continue; > + > + bus_flags =3D bridge->timings->input_bus_flags; > + omap_encoder_update_videomode_flags(&vm, bus_flags); > } > =20 > /* Set timings for all devices in the display pipeline. */ > - dss_mgr_set_timings(omap_encoder->output, &vm); > + dss_mgr_set_timings(output, &vm); > =20 > - for (dssdev =3D omap_encoder->output; dssdev; dssdev =3D dssdev->next) { > + for (dssdev =3D output; dssdev; dssdev =3D dssdev->next) { > if (dssdev->ops->set_timings) > dssdev->ops->set_timings(dssdev, adjusted_mode); > } > =20 > /* Set the HDMI mode and HDMI infoframe if applicable. */ > - if (omap_encoder->output->type =3D=3D OMAP_DISPLAY_TYPE_HDMI) > + if (output->type =3D=3D OMAP_DISPLAY_TYPE_HDMI) > omap_encoder_hdmi_mode_set(encoder, adjusted_mode); > } > =20 > --=20 > Regards, >=20 > Laurent Pinchart >=20 > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel --j55q2tppkg7tv7zk Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAlxeSFEACgkQ2O7X88g7 +pryEA/9F2kII74mYC7WAMdToIg8Ghl1g2uRt2qr1DKQYHJdnMXHCbThp88zKtFS vWAyYSfE+x5FU7GsRzwYBKS45kVrw4DS97G7/ETuIgE2TrFkQ26aradyuWZcLMxB jrN7JRXdwl/BZx57jKehcYth0HQTEBi5bihQcw2rIbcYm2QOTDJg2oDzeg1zcMBu j69ZpnWMcraJT3D6PpjcBdwpbBfWbZuDOytUwFzMCa0xgkmVOHtvIwEO80dwGORU /vZ9LyOdXz8JcNTYh+zLv8OmIaSnIL0YIP5ZJNKOkjkz2z3IkyYPDTgX+fRXP9Dj 6ek5bQ60jK+t1URqhhRAyFPMgj5CmPelt7LlrmH6cIB+X+t/zUzRIH3qRrVwGvhl jwx3RCcsKYRmlS5wLbBkwcp/lB7qJfxhBnvruMH/aqHtQG1EnsWfxA7nJo12LNDT HIwW0W8ghy4IHNjtKBAGLatQdU0dcfLir2vC7rjLnBacIZTPpW5kmYwb+gUGDMUK TYCLOaYVW+/bVHW+d9iC0eLXEMOIzFDfUqQBwafBeyc2Jx344rrv6KRfUV+/JeWE 1wVtHJXmQ08fTZBdv62Q04TvQ5Qlsb0Jr0IaOHdLjx3rFb/HemGz1GGLdDitbII3 2usDDB1vUpfys+fiwnjdQsyMO5yJ6re64Pv9pxc8TjZzL2g35sA= =nfgb -----END PGP SIGNATURE----- --j55q2tppkg7tv7zk-- --===============0620708457== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KZHJpLWRldmVs IG1haWxpbmcgbGlzdApkcmktZGV2ZWxAbGlzdHMuZnJlZWRlc2t0b3Aub3JnCmh0dHBzOi8vbGlz dHMuZnJlZWRlc2t0b3Aub3JnL21haWxtYW4vbGlzdGluZm8vZHJpLWRldmVsCg== --===============0620708457==--