From mboxrd@z Thu Jan 1 00:00:00 1970 From: boris.brezillon@bootlin.com (Boris Brezillon) Date: Wed, 18 Apr 2018 09:36:49 +0200 Subject: [PATCH v2 5/6] drm/atmel-hlcdc: add support for connecting to tda998x HDMI encoder In-Reply-To: <20180417131052.16336-6-peda@axentia.se> References: <20180417131052.16336-1-peda@axentia.se> <20180417131052.16336-6-peda@axentia.se> Message-ID: <20180418093649.2c304f29@bbrezillon> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org On Tue, 17 Apr 2018 15:10:51 +0200 Peter Rosin wrote: > When the of-graph points to a tda998x-compatible HDMI encoder, register > as a component master and bind to the encoder/connector provided by > the tda998x driver. Can't we do the opposite: make the tda998x driver expose its devices as drm bridges. I'd rather not add another way to connect external encoders (or bridges) to display controller drivers, especially since, when I asked DRM maintainers/devs what was the good approach to represent such external encoders they pointed me to the drm_bridge interface. > > Signed-off-by: Peter Rosin > --- > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 81 ++++++++++++-- > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 15 +++ > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 130 +++++++++++++++++++++++ > 3 files changed, 220 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > index c1ea5c36b006..8523c40fac94 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > @@ -20,6 +20,7 @@ > */ > > #include > +#include > #include > #include > #include > @@ -568,10 +569,13 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) > > drm_mode_config_init(dev); > > - ret = atmel_hlcdc_create_outputs(dev); > - if (ret) { > - dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); > - return ret; > + if (!dc->is_componentized) { > + ret = atmel_hlcdc_create_outputs(dev); > + if (ret) { > + dev_err(dev->dev, > + "failed to create HLCDC outputs: %d\n", ret); > + return ret; > + } > } > > ret = atmel_hlcdc_create_planes(dev); > @@ -586,6 +590,16 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) > return ret; > } > > + if (dc->is_componentized) { > + ret = component_bind_all(dev->dev, dev); > + if (ret < 0) > + return ret; > + > + ret = atmel_hlcdc_add_component_encoder(dev); > + if (ret < 0) > + return ret; > + } > + > dev->mode_config.min_width = dc->desc->min_width; > dev->mode_config.min_height = dc->desc->min_height; > dev->mode_config.max_width = dc->desc->max_width; > @@ -617,6 +631,9 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) > if (!dc) > return -ENOMEM; > > + dc->is_componentized = > + atmel_hlcdc_get_external_components(dev->dev, NULL) > 0; > + > dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); > if (!dc->wq) > return -ENOMEM; > @@ -751,7 +768,7 @@ static struct drm_driver atmel_hlcdc_dc_driver = { > .minor = 0, > }; > > -static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > +static int atmel_hlcdc_dc_drm_init(struct platform_device *pdev) > { > struct drm_device *ddev; > int ret; > @@ -779,7 +796,7 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > return ret; > } > > -static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > +static int atmel_hlcdc_dc_drm_fini(struct platform_device *pdev) > { > struct drm_device *ddev = platform_get_drvdata(pdev); > > @@ -790,6 +807,58 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > return 0; > } > > +static int atmel_hlcdc_bind(struct device *dev) > +{ > + return atmel_hlcdc_dc_drm_init(to_platform_device(dev)); > +} > + > +static void atmel_hlcdc_unbind(struct device *dev) > +{ > + struct drm_device *ddev = dev_get_drvdata(dev); > + > + /* Check if a subcomponent has already triggered the unloading. */ > + if (!ddev->dev_private) > + return; > + > + atmel_hlcdc_dc_drm_fini(to_platform_device(dev)); > +} > + > +static const struct component_master_ops atmel_hlcdc_comp_ops = { > + .bind = atmel_hlcdc_bind, > + .unbind = atmel_hlcdc_unbind, > +}; > + > +static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > +{ > + struct component_match *match = NULL; > + int ret; > + > + ret = atmel_hlcdc_get_external_components(&pdev->dev, &match); > + if (ret < 0) > + return ret; > + else if (ret) > + return component_master_add_with_match(&pdev->dev, > + &atmel_hlcdc_comp_ops, > + match); > + else > + return atmel_hlcdc_dc_drm_init(pdev); > +} > + > +static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > +{ > + int ret; > + > + ret = atmel_hlcdc_get_external_components(&pdev->dev, NULL); > + if (ret < 0) > + return ret; > + else if (ret) > + component_master_del(&pdev->dev, &atmel_hlcdc_comp_ops); > + else > + atmel_hlcdc_dc_drm_fini(pdev); > + > + return 0; > +} > + > #ifdef CONFIG_PM_SLEEP > static int atmel_hlcdc_dc_drm_suspend(struct device *dev) > { > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > index ab32d5b268d2..cae77c245661 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > @@ -370,6 +370,10 @@ struct atmel_hlcdc_plane_properties { > * @wq: display controller workqueue > * @suspend: used to store the HLCDC state when entering suspend > * @commit: used for async commit handling > + * @external_encoder: used encoder when componentized > + * @external_connector: used connector when componentized > + * @connector_funcs: original helper funcs of the external connector > + * @is_componentized: operating mode > */ > struct atmel_hlcdc_dc { > const struct atmel_hlcdc_dc_desc *desc; > @@ -386,6 +390,12 @@ struct atmel_hlcdc_dc { > wait_queue_head_t wait; > bool pending; > } commit; > + > + struct drm_encoder *external_encoder; > + struct drm_connector *external_connector; > + const struct drm_connector_helper_funcs *connector_funcs; > + > + bool is_componentized; > }; > > extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; > @@ -455,4 +465,9 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev); > > int atmel_hlcdc_create_outputs(struct drm_device *dev); > > +struct component_match; > +int atmel_hlcdc_add_component_encoder(struct drm_device *dev); > +int atmel_hlcdc_get_external_components(struct device *dev, > + struct component_match **match); > + > #endif /* DRM_ATMEL_HLCDC_H */ > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > index 8db51fb131db..3f86527e0473 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > @@ -6,6 +6,11 @@ > * Author: Jean-Jacques Hiblot > * Author: Boris BREZILLON > * > + * Handling of external components adapted from the tilcdc driver > + * by Peter Rosin . That original code had: > + * Copyright (C) 2015 Texas Instruments > + * Author: Jyri Sarha > + * > * This program is free software; you can redistribute it and/or modify it > * under the terms of the GNU General Public License version 2 as published by > * the Free Software Foundation. > @@ -19,6 +24,7 @@ > * this program. If not, see . > */ > > +#include > #include > > #include > @@ -88,3 +94,127 @@ int atmel_hlcdc_create_outputs(struct drm_device *dev) > > return ret; > } > + > +static int atmel_hlcdc_external_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct atmel_hlcdc_dc *dc = connector->dev->dev_private; > + int ret; > + > + ret = atmel_hlcdc_dc_mode_valid(dc, mode); > + if (ret != MODE_OK) > + return ret; > + > + if (WARN_ON(dc->external_connector != connector)) > + return MODE_ERROR; > + if (WARN_ON(!dc->connector_funcs)) > + return MODE_ERROR; > + > + if (IS_ERR(dc->connector_funcs) || !dc->connector_funcs->mode_valid) > + return MODE_OK; > + > + /* The connector has its own mode_valid, call it. */ > + return dc->connector_funcs->mode_valid(connector, mode); > +} > + > +static int atmel_hlcdc_add_external_connector(struct drm_device *dev, > + struct drm_connector *connector) > +{ > + struct atmel_hlcdc_dc *dc = dev->dev_private; > + struct drm_connector_helper_funcs *connector_funcs; > + > + /* There should never be more than one connector. */ > + if (WARN_ON(dc->external_connector)) > + return -EINVAL; > + > + dc->external_connector = connector; > + connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), > + GFP_KERNEL); > + if (!connector_funcs) > + return -ENOMEM; > + > + /* > + * connector->helper_private always contains the struct > + * connector_helper_funcs pointer. For the atmel-hlcdc crtc > + * to have a say if a specific mode is Ok, we install our > + * own helper functions. In our helper functions we copy > + * everything else but use our own mode_valid() (above). > + */ > + if (connector->helper_private) { > + dc->connector_funcs = connector->helper_private; > + *connector_funcs = *dc->connector_funcs; > + } else { > + dc->connector_funcs = ERR_PTR(-ENOENT); > + } > + connector_funcs->mode_valid = atmel_hlcdc_external_mode_valid; > + drm_connector_helper_add(connector, connector_funcs); > + > + dev_dbg(dev->dev, "External connector '%s' connected\n", > + connector->name); > + > + return 0; > +} > + > +static struct drm_connector * > +atmel_hlcdc_encoder_find_connector(struct drm_device *dev, > + struct drm_encoder *encoder) > +{ > + struct drm_connector *connector; > + int i; > + > + list_for_each_entry(connector, &dev->mode_config.connector_list, head) > + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) > + if (connector->encoder_ids[i] == encoder->base.id) > + return connector; > + > + dev_err(dev->dev, "No connector found for %s encoder (id %d)\n", > + encoder->name, encoder->base.id); > + > + return NULL; > +} > + > +int atmel_hlcdc_add_component_encoder(struct drm_device *dev) > +{ > + struct atmel_hlcdc_dc *dc = dev->dev_private; > + struct drm_connector *connector; > + struct drm_encoder *encoder; > + > + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { > + if (encoder->possible_crtcs & (1 << dc->crtc->index)) > + break; > + } > + > + if (!encoder) { > + dev_err(dev->dev, "%s: No suitable encoder found\n", __func__); > + return -ENODEV; > + } > + > + connector = atmel_hlcdc_encoder_find_connector(dev, encoder); > + if (!connector) > + return -ENODEV; > + > + return atmel_hlcdc_add_external_connector(dev, connector); > +} > + > +static int dev_match_of(struct device *dev, void *data) > +{ > + return dev->of_node == data; > +} > + > +int atmel_hlcdc_get_external_components(struct device *dev, > + struct component_match **match) > +{ > + struct device_node *node; > + > + node = of_graph_get_remote_node(dev->of_node, 0, 0); > + > + if (!of_device_is_compatible(node, "nxp,tda998x")) { > + of_node_put(node); > + return 0; > + } > + > + if (match) > + drm_of_component_match_add(dev, match, dev_match_of, node); > + of_node_put(node); > + return 1; > +} From mboxrd@z Thu Jan 1 00:00:00 1970 From: Boris Brezillon Subject: Re: [PATCH v2 5/6] drm/atmel-hlcdc: add support for connecting to tda998x HDMI encoder Date: Wed, 18 Apr 2018 09:36:49 +0200 Message-ID: <20180418093649.2c304f29@bbrezillon> References: <20180417131052.16336-1-peda@axentia.se> <20180417131052.16336-6-peda@axentia.se> Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Return-path: In-Reply-To: <20180417131052.16336-6-peda@axentia.se> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Peter Rosin Cc: Mark Rutland , Boris Brezillon , Alexandre Belloni , devicetree@vger.kernel.org, David Airlie , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Nicolas Ferre , Rob Herring , Laurent Pinchart , Daniel Vetter , Russell King - ARM Linux , linux-arm-kernel@lists.infradead.org List-Id: devicetree@vger.kernel.org T24gVHVlLCAxNyBBcHIgMjAxOCAxNToxMDo1MSArMDIwMApQZXRlciBSb3NpbiA8cGVkYUBheGVu dGlhLnNlPiB3cm90ZToKCj4gV2hlbiB0aGUgb2YtZ3JhcGggcG9pbnRzIHRvIGEgdGRhOTk4eC1j b21wYXRpYmxlIEhETUkgZW5jb2RlciwgcmVnaXN0ZXIKPiBhcyBhIGNvbXBvbmVudCBtYXN0ZXIg YW5kIGJpbmQgdG8gdGhlIGVuY29kZXIvY29ubmVjdG9yIHByb3ZpZGVkIGJ5Cj4gdGhlIHRkYTk5 OHggZHJpdmVyLgoKQ2FuJ3Qgd2UgZG8gdGhlIG9wcG9zaXRlOiBtYWtlIHRoZSB0ZGE5OTh4IGRy aXZlciBleHBvc2UgaXRzIGRldmljZXMgYXMKZHJtIGJyaWRnZXMuIEknZCByYXRoZXIgbm90IGFk ZCBhbm90aGVyIHdheSB0byBjb25uZWN0IGV4dGVybmFsCmVuY29kZXJzIChvciBicmlkZ2VzKSB0 byBkaXNwbGF5IGNvbnRyb2xsZXIgZHJpdmVycywgZXNwZWNpYWxseSBzaW5jZSwKd2hlbiBJIGFz a2VkIERSTSBtYWludGFpbmVycy9kZXZzIHdoYXQgd2FzIHRoZSBnb29kIGFwcHJvYWNoIHRvCnJl cHJlc2VudCBzdWNoIGV4dGVybmFsIGVuY29kZXJzIHRoZXkgcG9pbnRlZCBtZSB0byB0aGUgZHJt X2JyaWRnZQppbnRlcmZhY2UuCgo+IAo+IFNpZ25lZC1vZmYtYnk6IFBldGVyIFJvc2luIDxwZWRh QGF4ZW50aWEuc2U+Cj4gLS0tCj4gIGRyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9o bGNkY19kYy5jICAgICB8ICA4MSArKysrKysrKysrKystLQo+ICBkcml2ZXJzL2dwdS9kcm0vYXRt ZWwtaGxjZGMvYXRtZWxfaGxjZGNfZGMuaCAgICAgfCAgMTUgKysrCj4gIGRyaXZlcnMvZ3B1L2Ry bS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19vdXRwdXQuYyB8IDEzMCArKysrKysrKysrKysrKysr KysrKysrKwo+ICAzIGZpbGVzIGNoYW5nZWQsIDIyMCBpbnNlcnRpb25zKCspLCA2IGRlbGV0aW9u cygtKQo+IAo+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2dwdS9kcm0vYXRtZWwtaGxjZGMvYXRtZWxf aGxjZGNfZGMuYyBiL2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19kYy5j Cj4gaW5kZXggYzFlYTVjMzZiMDA2Li44NTIzYzQwZmFjOTQgMTAwNjQ0Cj4gLS0tIGEvZHJpdmVy cy9ncHUvZHJtL2F0bWVsLWhsY2RjL2F0bWVsX2hsY2RjX2RjLmMKPiArKysgYi9kcml2ZXJzL2dw dS9kcm0vYXRtZWwtaGxjZGMvYXRtZWxfaGxjZGNfZGMuYwo+IEBAIC0yMCw2ICsyMCw3IEBACj4g ICAqLwo+ICAKPiAgI2luY2x1ZGUgPGxpbnV4L2Nsay5oPgo+ICsjaW5jbHVkZSA8bGludXgvY29t cG9uZW50Lmg+Cj4gICNpbmNsdWRlIDxsaW51eC9pcnEuaD4KPiAgI2luY2x1ZGUgPGxpbnV4L2ly cWNoaXAuaD4KPiAgI2luY2x1ZGUgPGxpbnV4L21vZHVsZS5oPgo+IEBAIC01NjgsMTAgKzU2OSwx MyBAQCBzdGF0aWMgaW50IGF0bWVsX2hsY2RjX2RjX21vZGVzZXRfaW5pdChzdHJ1Y3QgZHJtX2Rl dmljZSAqZGV2KQo+ICAKPiAgCWRybV9tb2RlX2NvbmZpZ19pbml0KGRldik7Cj4gIAo+IC0JcmV0 ID0gYXRtZWxfaGxjZGNfY3JlYXRlX291dHB1dHMoZGV2KTsKPiAtCWlmIChyZXQpIHsKPiAtCQlk ZXZfZXJyKGRldi0+ZGV2LCAiZmFpbGVkIHRvIGNyZWF0ZSBITENEQyBvdXRwdXRzOiAlZFxuIiwg cmV0KTsKPiAtCQlyZXR1cm4gcmV0Owo+ICsJaWYgKCFkYy0+aXNfY29tcG9uZW50aXplZCkgewo+ ICsJCXJldCA9IGF0bWVsX2hsY2RjX2NyZWF0ZV9vdXRwdXRzKGRldik7Cj4gKwkJaWYgKHJldCkg ewo+ICsJCQlkZXZfZXJyKGRldi0+ZGV2LAo+ICsJCQkJImZhaWxlZCB0byBjcmVhdGUgSExDREMg b3V0cHV0czogJWRcbiIsIHJldCk7Cj4gKwkJCXJldHVybiByZXQ7Cj4gKwkJfQo+ICAJfQo+ICAK PiAgCXJldCA9IGF0bWVsX2hsY2RjX2NyZWF0ZV9wbGFuZXMoZGV2KTsKPiBAQCAtNTg2LDYgKzU5 MCwxNiBAQCBzdGF0aWMgaW50IGF0bWVsX2hsY2RjX2RjX21vZGVzZXRfaW5pdChzdHJ1Y3QgZHJt X2RldmljZSAqZGV2KQo+ICAJCXJldHVybiByZXQ7Cj4gIAl9Cj4gIAo+ICsJaWYgKGRjLT5pc19j b21wb25lbnRpemVkKSB7Cj4gKwkJcmV0ID0gY29tcG9uZW50X2JpbmRfYWxsKGRldi0+ZGV2LCBk ZXYpOwo+ICsJCWlmIChyZXQgPCAwKQo+ICsJCQlyZXR1cm4gcmV0Owo+ICsKPiArCQlyZXQgPSBh dG1lbF9obGNkY19hZGRfY29tcG9uZW50X2VuY29kZXIoZGV2KTsKPiArCQlpZiAocmV0IDwgMCkK PiArCQkJcmV0dXJuIHJldDsKPiArCX0KPiArCj4gIAlkZXYtPm1vZGVfY29uZmlnLm1pbl93aWR0 aCA9IGRjLT5kZXNjLT5taW5fd2lkdGg7Cj4gIAlkZXYtPm1vZGVfY29uZmlnLm1pbl9oZWlnaHQg PSBkYy0+ZGVzYy0+bWluX2hlaWdodDsKPiAgCWRldi0+bW9kZV9jb25maWcubWF4X3dpZHRoID0g ZGMtPmRlc2MtPm1heF93aWR0aDsKPiBAQCAtNjE3LDYgKzYzMSw5IEBAIHN0YXRpYyBpbnQgYXRt ZWxfaGxjZGNfZGNfbG9hZChzdHJ1Y3QgZHJtX2RldmljZSAqZGV2KQo+ICAJaWYgKCFkYykKPiAg CQlyZXR1cm4gLUVOT01FTTsKPiAgCj4gKwlkYy0+aXNfY29tcG9uZW50aXplZCA9Cj4gKwkJYXRt ZWxfaGxjZGNfZ2V0X2V4dGVybmFsX2NvbXBvbmVudHMoZGV2LT5kZXYsIE5VTEwpID4gMDsKPiAr Cj4gIAlkYy0+d3EgPSBhbGxvY19vcmRlcmVkX3dvcmtxdWV1ZSgiYXRtZWwtaGxjZGMtZGMiLCAw KTsKPiAgCWlmICghZGMtPndxKQo+ICAJCXJldHVybiAtRU5PTUVNOwo+IEBAIC03NTEsNyArNzY4 LDcgQEAgc3RhdGljIHN0cnVjdCBkcm1fZHJpdmVyIGF0bWVsX2hsY2RjX2RjX2RyaXZlciA9IHsK PiAgCS5taW5vciA9IDAsCj4gIH07Cj4gIAo+IC1zdGF0aWMgaW50IGF0bWVsX2hsY2RjX2RjX2Ry bV9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQo+ICtzdGF0aWMgaW50IGF0bWVs X2hsY2RjX2RjX2RybV9pbml0KHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpCj4gIHsKPiAg CXN0cnVjdCBkcm1fZGV2aWNlICpkZGV2Owo+ICAJaW50IHJldDsKPiBAQCAtNzc5LDcgKzc5Niw3 IEBAIHN0YXRpYyBpbnQgYXRtZWxfaGxjZGNfZGNfZHJtX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9k ZXZpY2UgKnBkZXYpCj4gIAlyZXR1cm4gcmV0Owo+ICB9Cj4gIAo+IC1zdGF0aWMgaW50IGF0bWVs X2hsY2RjX2RjX2RybV9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKPiArc3Rh dGljIGludCBhdG1lbF9obGNkY19kY19kcm1fZmluaShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpw ZGV2KQo+ICB7Cj4gIAlzdHJ1Y3QgZHJtX2RldmljZSAqZGRldiA9IHBsYXRmb3JtX2dldF9kcnZk YXRhKHBkZXYpOwo+ICAKPiBAQCAtNzkwLDYgKzgwNyw1OCBAQCBzdGF0aWMgaW50IGF0bWVsX2hs Y2RjX2RjX2RybV9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikKPiAgCXJldHVy biAwOwo+ICB9Cj4gIAo+ICtzdGF0aWMgaW50IGF0bWVsX2hsY2RjX2JpbmQoc3RydWN0IGRldmlj ZSAqZGV2KQo+ICt7Cj4gKwlyZXR1cm4gYXRtZWxfaGxjZGNfZGNfZHJtX2luaXQodG9fcGxhdGZv cm1fZGV2aWNlKGRldikpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgdm9pZCBhdG1lbF9obGNkY191bmJp bmQoc3RydWN0IGRldmljZSAqZGV2KQo+ICt7Cj4gKwlzdHJ1Y3QgZHJtX2RldmljZSAqZGRldiA9 IGRldl9nZXRfZHJ2ZGF0YShkZXYpOwo+ICsKPiArCS8qIENoZWNrIGlmIGEgc3ViY29tcG9uZW50 IGhhcyBhbHJlYWR5IHRyaWdnZXJlZCB0aGUgdW5sb2FkaW5nLiAqLwo+ICsJaWYgKCFkZGV2LT5k ZXZfcHJpdmF0ZSkKPiArCQlyZXR1cm47Cj4gKwo+ICsJYXRtZWxfaGxjZGNfZGNfZHJtX2Zpbmko dG9fcGxhdGZvcm1fZGV2aWNlKGRldikpOwo+ICt9Cj4gKwo+ICtzdGF0aWMgY29uc3Qgc3RydWN0 IGNvbXBvbmVudF9tYXN0ZXJfb3BzIGF0bWVsX2hsY2RjX2NvbXBfb3BzID0gewo+ICsJLmJpbmQg PSBhdG1lbF9obGNkY19iaW5kLAo+ICsJLnVuYmluZCA9IGF0bWVsX2hsY2RjX3VuYmluZCwKPiAr fTsKPiArCj4gK3N0YXRpYyBpbnQgYXRtZWxfaGxjZGNfZGNfZHJtX3Byb2JlKHN0cnVjdCBwbGF0 Zm9ybV9kZXZpY2UgKnBkZXYpCj4gK3sKPiArCXN0cnVjdCBjb21wb25lbnRfbWF0Y2ggKm1hdGNo ID0gTlVMTDsKPiArCWludCByZXQ7Cj4gKwo+ICsJcmV0ID0gYXRtZWxfaGxjZGNfZ2V0X2V4dGVy bmFsX2NvbXBvbmVudHMoJnBkZXYtPmRldiwgJm1hdGNoKTsKPiArCWlmIChyZXQgPCAwKQo+ICsJ CXJldHVybiByZXQ7Cj4gKwllbHNlIGlmIChyZXQpCj4gKwkJcmV0dXJuIGNvbXBvbmVudF9tYXN0 ZXJfYWRkX3dpdGhfbWF0Y2goJnBkZXYtPmRldiwKPiArCQkJCQkJICAgICAgICZhdG1lbF9obGNk Y19jb21wX29wcywKPiArCQkJCQkJICAgICAgIG1hdGNoKTsKPiArCWVsc2UKPiArCQlyZXR1cm4g YXRtZWxfaGxjZGNfZGNfZHJtX2luaXQocGRldik7Cj4gK30KPiArCj4gK3N0YXRpYyBpbnQgYXRt ZWxfaGxjZGNfZGNfZHJtX3JlbW92ZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlICpwZGV2KQo+ICt7 Cj4gKwlpbnQgcmV0Owo+ICsKPiArCXJldCA9IGF0bWVsX2hsY2RjX2dldF9leHRlcm5hbF9jb21w b25lbnRzKCZwZGV2LT5kZXYsIE5VTEwpOwo+ICsJaWYgKHJldCA8IDApCj4gKwkJcmV0dXJuIHJl dDsKPiArCWVsc2UgaWYgKHJldCkKPiArCQljb21wb25lbnRfbWFzdGVyX2RlbCgmcGRldi0+ZGV2 LCAmYXRtZWxfaGxjZGNfY29tcF9vcHMpOwo+ICsJZWxzZQo+ICsJCWF0bWVsX2hsY2RjX2RjX2Ry bV9maW5pKHBkZXYpOwo+ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICAjaWZkZWYgQ09ORklH X1BNX1NMRUVQCj4gIHN0YXRpYyBpbnQgYXRtZWxfaGxjZGNfZGNfZHJtX3N1c3BlbmQoc3RydWN0 IGRldmljZSAqZGV2KQo+ICB7Cj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1o bGNkYy9hdG1lbF9obGNkY19kYy5oIGIvZHJpdmVycy9ncHUvZHJtL2F0bWVsLWhsY2RjL2F0bWVs X2hsY2RjX2RjLmgKPiBpbmRleCBhYjMyZDViMjY4ZDIuLmNhZTc3YzI0NTY2MSAxMDA2NDQKPiAt LS0gYS9kcml2ZXJzL2dwdS9kcm0vYXRtZWwtaGxjZGMvYXRtZWxfaGxjZGNfZGMuaAo+ICsrKyBi L2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19kYy5oCj4gQEAgLTM3MCw2 ICszNzAsMTAgQEAgc3RydWN0IGF0bWVsX2hsY2RjX3BsYW5lX3Byb3BlcnRpZXMgewo+ICAgKiBA d3E6IGRpc3BsYXkgY29udHJvbGxlciB3b3JrcXVldWUKPiAgICogQHN1c3BlbmQ6IHVzZWQgdG8g c3RvcmUgdGhlIEhMQ0RDIHN0YXRlIHdoZW4gZW50ZXJpbmcgc3VzcGVuZAo+ICAgKiBAY29tbWl0 OiB1c2VkIGZvciBhc3luYyBjb21taXQgaGFuZGxpbmcKPiArICogQGV4dGVybmFsX2VuY29kZXI6 IHVzZWQgZW5jb2RlciB3aGVuIGNvbXBvbmVudGl6ZWQKPiArICogQGV4dGVybmFsX2Nvbm5lY3Rv cjogdXNlZCBjb25uZWN0b3Igd2hlbiBjb21wb25lbnRpemVkCj4gKyAqIEBjb25uZWN0b3JfZnVu Y3M6IG9yaWdpbmFsIGhlbHBlciBmdW5jcyBvZiB0aGUgZXh0ZXJuYWwgY29ubmVjdG9yCj4gKyAq IEBpc19jb21wb25lbnRpemVkOiBvcGVyYXRpbmcgbW9kZQo+ICAgKi8KPiAgc3RydWN0IGF0bWVs X2hsY2RjX2RjIHsKPiAgCWNvbnN0IHN0cnVjdCBhdG1lbF9obGNkY19kY19kZXNjICpkZXNjOwo+ IEBAIC0zODYsNiArMzkwLDEyIEBAIHN0cnVjdCBhdG1lbF9obGNkY19kYyB7Cj4gIAkJd2FpdF9x dWV1ZV9oZWFkX3Qgd2FpdDsKPiAgCQlib29sIHBlbmRpbmc7Cj4gIAl9IGNvbW1pdDsKPiArCj4g KwlzdHJ1Y3QgZHJtX2VuY29kZXIgKmV4dGVybmFsX2VuY29kZXI7Cj4gKwlzdHJ1Y3QgZHJtX2Nv bm5lY3RvciAqZXh0ZXJuYWxfY29ubmVjdG9yOwo+ICsJY29uc3Qgc3RydWN0IGRybV9jb25uZWN0 b3JfaGVscGVyX2Z1bmNzICpjb25uZWN0b3JfZnVuY3M7Cj4gKwo+ICsJYm9vbCBpc19jb21wb25l bnRpemVkOwo+ICB9Owo+ICAKPiAgZXh0ZXJuIHN0cnVjdCBhdG1lbF9obGNkY19mb3JtYXRzIGF0 bWVsX2hsY2RjX3BsYW5lX3JnYl9mb3JtYXRzOwo+IEBAIC00NTUsNCArNDY1LDkgQEAgaW50IGF0 bWVsX2hsY2RjX2NydGNfY3JlYXRlKHN0cnVjdCBkcm1fZGV2aWNlICpkZXYpOwo+ICAKPiAgaW50 IGF0bWVsX2hsY2RjX2NyZWF0ZV9vdXRwdXRzKHN0cnVjdCBkcm1fZGV2aWNlICpkZXYpOwo+ICAK PiArc3RydWN0IGNvbXBvbmVudF9tYXRjaDsKPiAraW50IGF0bWVsX2hsY2RjX2FkZF9jb21wb25l bnRfZW5jb2RlcihzdHJ1Y3QgZHJtX2RldmljZSAqZGV2KTsKPiAraW50IGF0bWVsX2hsY2RjX2dl dF9leHRlcm5hbF9jb21wb25lbnRzKHN0cnVjdCBkZXZpY2UgKmRldiwKPiArCQkJCQlzdHJ1Y3Qg Y29tcG9uZW50X21hdGNoICoqbWF0Y2gpOwo+ICsKPiAgI2VuZGlmIC8qIERSTV9BVE1FTF9ITENE Q19IICovCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9o bGNkY19vdXRwdXQuYyBiL2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19v dXRwdXQuYwo+IGluZGV4IDhkYjUxZmIxMzFkYi4uM2Y4NjUyN2UwNDczIDEwMDY0NAo+IC0tLSBh L2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19vdXRwdXQuYwo+ICsrKyBi L2RyaXZlcnMvZ3B1L2RybS9hdG1lbC1obGNkYy9hdG1lbF9obGNkY19vdXRwdXQuYwo+IEBAIC02 LDYgKzYsMTEgQEAKPiAgICogQXV0aG9yOiBKZWFuLUphY3F1ZXMgSGlibG90IDxqamhpYmxvdEB0 cmFwaGFuZGxlci5jb20+Cj4gICAqIEF1dGhvcjogQm9yaXMgQlJFWklMTE9OIDxib3Jpcy5icmV6 aWxsb25AZnJlZS1lbGVjdHJvbnMuY29tPgo+ICAgKgo+ICsgKiBIYW5kbGluZyBvZiBleHRlcm5h bCBjb21wb25lbnRzIGFkYXB0ZWQgZnJvbSB0aGUgdGlsY2RjIGRyaXZlcgo+ICsgKiBieSBQZXRl ciBSb3NpbiA8cGVkYUBheGVudGlhLnNlPi4gVGhhdCBvcmlnaW5hbCBjb2RlIGhhZDoKPiArICog ICBDb3B5cmlnaHQgKEMpIDIwMTUgVGV4YXMgSW5zdHJ1bWVudHMKPiArICogICBBdXRob3I6IEp5 cmkgU2FyaGEgPGpzYXJoYUB0aS5jb20+Cj4gKyAqCj4gICAqIFRoaXMgcHJvZ3JhbSBpcyBmcmVl IHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5IGl0Cj4gICAq IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgdmVyc2lv biAyIGFzIHB1Ymxpc2hlZCBieQo+ICAgKiB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLgo+ IEBAIC0xOSw2ICsyNCw3IEBACj4gICAqIHRoaXMgcHJvZ3JhbS4gIElmIG5vdCwgc2VlIDxodHRw Oi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi4KPiAgICovCj4gIAo+ICsjaW5jbHVkZSA8bGludXgv Y29tcG9uZW50Lmg+Cj4gICNpbmNsdWRlIDxsaW51eC9vZl9ncmFwaC5oPgo+ICAKPiAgI2luY2x1 ZGUgPGRybS9kcm1QLmg+Cj4gQEAgLTg4LDMgKzk0LDEyNyBAQCBpbnQgYXRtZWxfaGxjZGNfY3Jl YXRlX291dHB1dHMoc3RydWN0IGRybV9kZXZpY2UgKmRldikKPiAgCj4gIAlyZXR1cm4gcmV0Owo+ ICB9Cj4gKwo+ICtzdGF0aWMgaW50IGF0bWVsX2hsY2RjX2V4dGVybmFsX21vZGVfdmFsaWQoc3Ry dWN0IGRybV9jb25uZWN0b3IgKmNvbm5lY3RvciwKPiArCQkJCQkgICBzdHJ1Y3QgZHJtX2Rpc3Bs YXlfbW9kZSAqbW9kZSkKPiArewo+ICsJc3RydWN0IGF0bWVsX2hsY2RjX2RjICpkYyA9IGNvbm5l Y3Rvci0+ZGV2LT5kZXZfcHJpdmF0ZTsKPiArCWludCByZXQ7Cj4gKwo+ICsJcmV0ID0gYXRtZWxf aGxjZGNfZGNfbW9kZV92YWxpZChkYywgbW9kZSk7Cj4gKwlpZiAocmV0ICE9IE1PREVfT0spCj4g KwkJcmV0dXJuIHJldDsKPiArCj4gKwlpZiAoV0FSTl9PTihkYy0+ZXh0ZXJuYWxfY29ubmVjdG9y ICE9IGNvbm5lY3RvcikpCj4gKwkJcmV0dXJuIE1PREVfRVJST1I7Cj4gKwlpZiAoV0FSTl9PTigh ZGMtPmNvbm5lY3Rvcl9mdW5jcykpCj4gKwkJcmV0dXJuIE1PREVfRVJST1I7Cj4gKwo+ICsJaWYg KElTX0VSUihkYy0+Y29ubmVjdG9yX2Z1bmNzKSB8fCAhZGMtPmNvbm5lY3Rvcl9mdW5jcy0+bW9k ZV92YWxpZCkKPiArCQlyZXR1cm4gTU9ERV9PSzsKPiArCj4gKwkvKiBUaGUgY29ubmVjdG9yIGhh cyBpdHMgb3duIG1vZGVfdmFsaWQsIGNhbGwgaXQuICovCj4gKwlyZXR1cm4gZGMtPmNvbm5lY3Rv cl9mdW5jcy0+bW9kZV92YWxpZChjb25uZWN0b3IsIG1vZGUpOwo+ICt9Cj4gKwo+ICtzdGF0aWMg aW50IGF0bWVsX2hsY2RjX2FkZF9leHRlcm5hbF9jb25uZWN0b3Ioc3RydWN0IGRybV9kZXZpY2Ug KmRldiwKPiArCQkJCQkgICAgICBzdHJ1Y3QgZHJtX2Nvbm5lY3RvciAqY29ubmVjdG9yKQo+ICt7 Cj4gKwlzdHJ1Y3QgYXRtZWxfaGxjZGNfZGMgKmRjID0gZGV2LT5kZXZfcHJpdmF0ZTsKPiArCXN0 cnVjdCBkcm1fY29ubmVjdG9yX2hlbHBlcl9mdW5jcyAqY29ubmVjdG9yX2Z1bmNzOwo+ICsKPiAr CS8qIFRoZXJlIHNob3VsZCBuZXZlciBiZSBtb3JlIHRoYW4gb25lIGNvbm5lY3Rvci4gKi8KPiAr CWlmIChXQVJOX09OKGRjLT5leHRlcm5hbF9jb25uZWN0b3IpKQo+ICsJCXJldHVybiAtRUlOVkFM Owo+ICsKPiArCWRjLT5leHRlcm5hbF9jb25uZWN0b3IgPSBjb25uZWN0b3I7Cj4gKwljb25uZWN0 b3JfZnVuY3MgPSBkZXZtX2t6YWxsb2MoZGV2LT5kZXYsIHNpemVvZigqY29ubmVjdG9yX2Z1bmNz KSwKPiArCQkJCSAgICAgICBHRlBfS0VSTkVMKTsKPiArCWlmICghY29ubmVjdG9yX2Z1bmNzKQo+ ICsJCXJldHVybiAtRU5PTUVNOwo+ICsKPiArCS8qCj4gKwkgKiBjb25uZWN0b3ItPmhlbHBlcl9w cml2YXRlIGFsd2F5cyBjb250YWlucyB0aGUgc3RydWN0Cj4gKwkgKiBjb25uZWN0b3JfaGVscGVy X2Z1bmNzIHBvaW50ZXIuIEZvciB0aGUgYXRtZWwtaGxjZGMgY3J0Ywo+ICsJICogdG8gaGF2ZSBh IHNheSBpZiBhIHNwZWNpZmljIG1vZGUgaXMgT2ssIHdlIGluc3RhbGwgb3VyCj4gKwkgKiBvd24g aGVscGVyIGZ1bmN0aW9ucy4gSW4gb3VyIGhlbHBlciBmdW5jdGlvbnMgd2UgY29weQo+ICsJICog ZXZlcnl0aGluZyBlbHNlIGJ1dCB1c2Ugb3VyIG93biBtb2RlX3ZhbGlkKCkgKGFib3ZlKS4KPiAr CSAqLwo+ICsJaWYgKGNvbm5lY3Rvci0+aGVscGVyX3ByaXZhdGUpIHsKPiArCQlkYy0+Y29ubmVj dG9yX2Z1bmNzID0gY29ubmVjdG9yLT5oZWxwZXJfcHJpdmF0ZTsKPiArCQkqY29ubmVjdG9yX2Z1 bmNzID0gKmRjLT5jb25uZWN0b3JfZnVuY3M7Cj4gKwl9IGVsc2Ugewo+ICsJCWRjLT5jb25uZWN0 b3JfZnVuY3MgPSBFUlJfUFRSKC1FTk9FTlQpOwo+ICsJfQo+ICsJY29ubmVjdG9yX2Z1bmNzLT5t b2RlX3ZhbGlkID0gYXRtZWxfaGxjZGNfZXh0ZXJuYWxfbW9kZV92YWxpZDsKPiArCWRybV9jb25u ZWN0b3JfaGVscGVyX2FkZChjb25uZWN0b3IsIGNvbm5lY3Rvcl9mdW5jcyk7Cj4gKwo+ICsJZGV2 X2RiZyhkZXYtPmRldiwgIkV4dGVybmFsIGNvbm5lY3RvciAnJXMnIGNvbm5lY3RlZFxuIiwKPiAr CQljb25uZWN0b3ItPm5hbWUpOwo+ICsKPiArCXJldHVybiAwOwo+ICt9Cj4gKwo+ICtzdGF0aWMg c3RydWN0IGRybV9jb25uZWN0b3IgKgo+ICthdG1lbF9obGNkY19lbmNvZGVyX2ZpbmRfY29ubmVj dG9yKHN0cnVjdCBkcm1fZGV2aWNlICpkZXYsCj4gKwkJCQkgICBzdHJ1Y3QgZHJtX2VuY29kZXIg KmVuY29kZXIpCj4gK3sKPiArCXN0cnVjdCBkcm1fY29ubmVjdG9yICpjb25uZWN0b3I7Cj4gKwlp bnQgaTsKPiArCj4gKwlsaXN0X2Zvcl9lYWNoX2VudHJ5KGNvbm5lY3RvciwgJmRldi0+bW9kZV9j b25maWcuY29ubmVjdG9yX2xpc3QsIGhlYWQpCj4gKwkJZm9yIChpID0gMDsgaSA8IERSTV9DT05O RUNUT1JfTUFYX0VOQ09ERVI7IGkrKykKPiArCQkJaWYgKGNvbm5lY3Rvci0+ZW5jb2Rlcl9pZHNb aV0gPT0gZW5jb2Rlci0+YmFzZS5pZCkKPiArCQkJCXJldHVybiBjb25uZWN0b3I7Cj4gKwo+ICsJ ZGV2X2VycihkZXYtPmRldiwgIk5vIGNvbm5lY3RvciBmb3VuZCBmb3IgJXMgZW5jb2RlciAoaWQg JWQpXG4iLAo+ICsJCWVuY29kZXItPm5hbWUsIGVuY29kZXItPmJhc2UuaWQpOwo+ICsKPiArCXJl dHVybiBOVUxMOwo+ICt9Cj4gKwo+ICtpbnQgYXRtZWxfaGxjZGNfYWRkX2NvbXBvbmVudF9lbmNv ZGVyKHN0cnVjdCBkcm1fZGV2aWNlICpkZXYpCj4gK3sKPiArCXN0cnVjdCBhdG1lbF9obGNkY19k YyAqZGMgPSBkZXYtPmRldl9wcml2YXRlOwo+ICsJc3RydWN0IGRybV9jb25uZWN0b3IgKmNvbm5l Y3RvcjsKPiArCXN0cnVjdCBkcm1fZW5jb2RlciAqZW5jb2RlcjsKPiArCj4gKwlsaXN0X2Zvcl9l YWNoX2VudHJ5KGVuY29kZXIsICZkZXYtPm1vZGVfY29uZmlnLmVuY29kZXJfbGlzdCwgaGVhZCkg ewo+ICsJCWlmIChlbmNvZGVyLT5wb3NzaWJsZV9jcnRjcyAmICgxIDw8IGRjLT5jcnRjLT5pbmRl eCkpCj4gKwkJCWJyZWFrOwo+ICsJfQo+ICsKPiArCWlmICghZW5jb2Rlcikgewo+ICsJCWRldl9l cnIoZGV2LT5kZXYsICIlczogTm8gc3VpdGFibGUgZW5jb2RlciBmb3VuZFxuIiwgX19mdW5jX18p Owo+ICsJCXJldHVybiAtRU5PREVWOwo+ICsJfQo+ICsKPiArCWNvbm5lY3RvciA9IGF0bWVsX2hs Y2RjX2VuY29kZXJfZmluZF9jb25uZWN0b3IoZGV2LCBlbmNvZGVyKTsKPiArCWlmICghY29ubmVj dG9yKQo+ICsJCXJldHVybiAtRU5PREVWOwo+ICsKPiArCXJldHVybiBhdG1lbF9obGNkY19hZGRf ZXh0ZXJuYWxfY29ubmVjdG9yKGRldiwgY29ubmVjdG9yKTsKPiArfQo+ICsKPiArc3RhdGljIGlu dCBkZXZfbWF0Y2hfb2Yoc3RydWN0IGRldmljZSAqZGV2LCB2b2lkICpkYXRhKQo+ICt7Cj4gKwly ZXR1cm4gZGV2LT5vZl9ub2RlID09IGRhdGE7Cj4gK30KPiArCj4gK2ludCBhdG1lbF9obGNkY19n ZXRfZXh0ZXJuYWxfY29tcG9uZW50cyhzdHJ1Y3QgZGV2aWNlICpkZXYsCj4gKwkJCQkJc3RydWN0 IGNvbXBvbmVudF9tYXRjaCAqKm1hdGNoKQo+ICt7Cj4gKwlzdHJ1Y3QgZGV2aWNlX25vZGUgKm5v ZGU7Cj4gKwo+ICsJbm9kZSA9IG9mX2dyYXBoX2dldF9yZW1vdGVfbm9kZShkZXYtPm9mX25vZGUs IDAsIDApOwo+ICsKPiArCWlmICghb2ZfZGV2aWNlX2lzX2NvbXBhdGlibGUobm9kZSwgIm54cCx0 ZGE5OTh4IikpIHsKPiArCQlvZl9ub2RlX3B1dChub2RlKTsKPiArCQlyZXR1cm4gMDsKPiArCX0K PiArCj4gKwlpZiAobWF0Y2gpCj4gKwkJZHJtX29mX2NvbXBvbmVudF9tYXRjaF9hZGQoZGV2LCBt YXRjaCwgZGV2X21hdGNoX29mLCBub2RlKTsKPiArCW9mX25vZGVfcHV0KG5vZGUpOwo+ICsJcmV0 dXJuIDE7Cj4gK30KCl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fCmRyaS1kZXZlbCBtYWlsaW5nIGxpc3QKZHJpLWRldmVsQGxpc3RzLmZyZWVkZXNrdG9wLm9y ZwpodHRwczovL2xpc3RzLmZyZWVkZXNrdG9wLm9yZy9tYWlsbWFuL2xpc3RpbmZvL2RyaS1kZXZl bAo= From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752784AbeDRHhE (ORCPT ); Wed, 18 Apr 2018 03:37:04 -0400 Received: from mail.bootlin.com ([62.4.15.54]:53772 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751879AbeDRHhC (ORCPT ); Wed, 18 Apr 2018 03:37:02 -0400 Date: Wed, 18 Apr 2018 09:36:49 +0200 From: Boris Brezillon To: Peter Rosin Cc: linux-kernel@vger.kernel.org, David Airlie , Rob Herring , Mark Rutland , Nicolas Ferre , Alexandre Belloni , Boris Brezillon , Daniel Vetter , Gustavo Padovan , Sean Paul , Laurent Pinchart , Russell King - ARM Linux , dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH v2 5/6] drm/atmel-hlcdc: add support for connecting to tda998x HDMI encoder Message-ID: <20180418093649.2c304f29@bbrezillon> In-Reply-To: <20180417131052.16336-6-peda@axentia.se> References: <20180417131052.16336-1-peda@axentia.se> <20180417131052.16336-6-peda@axentia.se> X-Mailer: Claws Mail 3.15.0-dirty (GTK+ 2.24.31; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, 17 Apr 2018 15:10:51 +0200 Peter Rosin wrote: > When the of-graph points to a tda998x-compatible HDMI encoder, register > as a component master and bind to the encoder/connector provided by > the tda998x driver. Can't we do the opposite: make the tda998x driver expose its devices as drm bridges. I'd rather not add another way to connect external encoders (or bridges) to display controller drivers, especially since, when I asked DRM maintainers/devs what was the good approach to represent such external encoders they pointed me to the drm_bridge interface. > > Signed-off-by: Peter Rosin > --- > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 81 ++++++++++++-- > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 15 +++ > drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 130 +++++++++++++++++++++++ > 3 files changed, 220 insertions(+), 6 deletions(-) > > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > index c1ea5c36b006..8523c40fac94 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c > @@ -20,6 +20,7 @@ > */ > > #include > +#include > #include > #include > #include > @@ -568,10 +569,13 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) > > drm_mode_config_init(dev); > > - ret = atmel_hlcdc_create_outputs(dev); > - if (ret) { > - dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); > - return ret; > + if (!dc->is_componentized) { > + ret = atmel_hlcdc_create_outputs(dev); > + if (ret) { > + dev_err(dev->dev, > + "failed to create HLCDC outputs: %d\n", ret); > + return ret; > + } > } > > ret = atmel_hlcdc_create_planes(dev); > @@ -586,6 +590,16 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) > return ret; > } > > + if (dc->is_componentized) { > + ret = component_bind_all(dev->dev, dev); > + if (ret < 0) > + return ret; > + > + ret = atmel_hlcdc_add_component_encoder(dev); > + if (ret < 0) > + return ret; > + } > + > dev->mode_config.min_width = dc->desc->min_width; > dev->mode_config.min_height = dc->desc->min_height; > dev->mode_config.max_width = dc->desc->max_width; > @@ -617,6 +631,9 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) > if (!dc) > return -ENOMEM; > > + dc->is_componentized = > + atmel_hlcdc_get_external_components(dev->dev, NULL) > 0; > + > dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); > if (!dc->wq) > return -ENOMEM; > @@ -751,7 +768,7 @@ static struct drm_driver atmel_hlcdc_dc_driver = { > .minor = 0, > }; > > -static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > +static int atmel_hlcdc_dc_drm_init(struct platform_device *pdev) > { > struct drm_device *ddev; > int ret; > @@ -779,7 +796,7 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > return ret; > } > > -static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > +static int atmel_hlcdc_dc_drm_fini(struct platform_device *pdev) > { > struct drm_device *ddev = platform_get_drvdata(pdev); > > @@ -790,6 +807,58 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > return 0; > } > > +static int atmel_hlcdc_bind(struct device *dev) > +{ > + return atmel_hlcdc_dc_drm_init(to_platform_device(dev)); > +} > + > +static void atmel_hlcdc_unbind(struct device *dev) > +{ > + struct drm_device *ddev = dev_get_drvdata(dev); > + > + /* Check if a subcomponent has already triggered the unloading. */ > + if (!ddev->dev_private) > + return; > + > + atmel_hlcdc_dc_drm_fini(to_platform_device(dev)); > +} > + > +static const struct component_master_ops atmel_hlcdc_comp_ops = { > + .bind = atmel_hlcdc_bind, > + .unbind = atmel_hlcdc_unbind, > +}; > + > +static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) > +{ > + struct component_match *match = NULL; > + int ret; > + > + ret = atmel_hlcdc_get_external_components(&pdev->dev, &match); > + if (ret < 0) > + return ret; > + else if (ret) > + return component_master_add_with_match(&pdev->dev, > + &atmel_hlcdc_comp_ops, > + match); > + else > + return atmel_hlcdc_dc_drm_init(pdev); > +} > + > +static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) > +{ > + int ret; > + > + ret = atmel_hlcdc_get_external_components(&pdev->dev, NULL); > + if (ret < 0) > + return ret; > + else if (ret) > + component_master_del(&pdev->dev, &atmel_hlcdc_comp_ops); > + else > + atmel_hlcdc_dc_drm_fini(pdev); > + > + return 0; > +} > + > #ifdef CONFIG_PM_SLEEP > static int atmel_hlcdc_dc_drm_suspend(struct device *dev) > { > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > index ab32d5b268d2..cae77c245661 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h > @@ -370,6 +370,10 @@ struct atmel_hlcdc_plane_properties { > * @wq: display controller workqueue > * @suspend: used to store the HLCDC state when entering suspend > * @commit: used for async commit handling > + * @external_encoder: used encoder when componentized > + * @external_connector: used connector when componentized > + * @connector_funcs: original helper funcs of the external connector > + * @is_componentized: operating mode > */ > struct atmel_hlcdc_dc { > const struct atmel_hlcdc_dc_desc *desc; > @@ -386,6 +390,12 @@ struct atmel_hlcdc_dc { > wait_queue_head_t wait; > bool pending; > } commit; > + > + struct drm_encoder *external_encoder; > + struct drm_connector *external_connector; > + const struct drm_connector_helper_funcs *connector_funcs; > + > + bool is_componentized; > }; > > extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; > @@ -455,4 +465,9 @@ int atmel_hlcdc_crtc_create(struct drm_device *dev); > > int atmel_hlcdc_create_outputs(struct drm_device *dev); > > +struct component_match; > +int atmel_hlcdc_add_component_encoder(struct drm_device *dev); > +int atmel_hlcdc_get_external_components(struct device *dev, > + struct component_match **match); > + > #endif /* DRM_ATMEL_HLCDC_H */ > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > index 8db51fb131db..3f86527e0473 100644 > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c > @@ -6,6 +6,11 @@ > * Author: Jean-Jacques Hiblot > * Author: Boris BREZILLON > * > + * Handling of external components adapted from the tilcdc driver > + * by Peter Rosin . That original code had: > + * Copyright (C) 2015 Texas Instruments > + * Author: Jyri Sarha > + * > * This program is free software; you can redistribute it and/or modify it > * under the terms of the GNU General Public License version 2 as published by > * the Free Software Foundation. > @@ -19,6 +24,7 @@ > * this program. If not, see . > */ > > +#include > #include > > #include > @@ -88,3 +94,127 @@ int atmel_hlcdc_create_outputs(struct drm_device *dev) > > return ret; > } > + > +static int atmel_hlcdc_external_mode_valid(struct drm_connector *connector, > + struct drm_display_mode *mode) > +{ > + struct atmel_hlcdc_dc *dc = connector->dev->dev_private; > + int ret; > + > + ret = atmel_hlcdc_dc_mode_valid(dc, mode); > + if (ret != MODE_OK) > + return ret; > + > + if (WARN_ON(dc->external_connector != connector)) > + return MODE_ERROR; > + if (WARN_ON(!dc->connector_funcs)) > + return MODE_ERROR; > + > + if (IS_ERR(dc->connector_funcs) || !dc->connector_funcs->mode_valid) > + return MODE_OK; > + > + /* The connector has its own mode_valid, call it. */ > + return dc->connector_funcs->mode_valid(connector, mode); > +} > + > +static int atmel_hlcdc_add_external_connector(struct drm_device *dev, > + struct drm_connector *connector) > +{ > + struct atmel_hlcdc_dc *dc = dev->dev_private; > + struct drm_connector_helper_funcs *connector_funcs; > + > + /* There should never be more than one connector. */ > + if (WARN_ON(dc->external_connector)) > + return -EINVAL; > + > + dc->external_connector = connector; > + connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), > + GFP_KERNEL); > + if (!connector_funcs) > + return -ENOMEM; > + > + /* > + * connector->helper_private always contains the struct > + * connector_helper_funcs pointer. For the atmel-hlcdc crtc > + * to have a say if a specific mode is Ok, we install our > + * own helper functions. In our helper functions we copy > + * everything else but use our own mode_valid() (above). > + */ > + if (connector->helper_private) { > + dc->connector_funcs = connector->helper_private; > + *connector_funcs = *dc->connector_funcs; > + } else { > + dc->connector_funcs = ERR_PTR(-ENOENT); > + } > + connector_funcs->mode_valid = atmel_hlcdc_external_mode_valid; > + drm_connector_helper_add(connector, connector_funcs); > + > + dev_dbg(dev->dev, "External connector '%s' connected\n", > + connector->name); > + > + return 0; > +} > + > +static struct drm_connector * > +atmel_hlcdc_encoder_find_connector(struct drm_device *dev, > + struct drm_encoder *encoder) > +{ > + struct drm_connector *connector; > + int i; > + > + list_for_each_entry(connector, &dev->mode_config.connector_list, head) > + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) > + if (connector->encoder_ids[i] == encoder->base.id) > + return connector; > + > + dev_err(dev->dev, "No connector found for %s encoder (id %d)\n", > + encoder->name, encoder->base.id); > + > + return NULL; > +} > + > +int atmel_hlcdc_add_component_encoder(struct drm_device *dev) > +{ > + struct atmel_hlcdc_dc *dc = dev->dev_private; > + struct drm_connector *connector; > + struct drm_encoder *encoder; > + > + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { > + if (encoder->possible_crtcs & (1 << dc->crtc->index)) > + break; > + } > + > + if (!encoder) { > + dev_err(dev->dev, "%s: No suitable encoder found\n", __func__); > + return -ENODEV; > + } > + > + connector = atmel_hlcdc_encoder_find_connector(dev, encoder); > + if (!connector) > + return -ENODEV; > + > + return atmel_hlcdc_add_external_connector(dev, connector); > +} > + > +static int dev_match_of(struct device *dev, void *data) > +{ > + return dev->of_node == data; > +} > + > +int atmel_hlcdc_get_external_components(struct device *dev, > + struct component_match **match) > +{ > + struct device_node *node; > + > + node = of_graph_get_remote_node(dev->of_node, 0, 0); > + > + if (!of_device_is_compatible(node, "nxp,tda998x")) { > + of_node_put(node); > + return 0; > + } > + > + if (match) > + drm_of_component_match_add(dev, match, dev_match_of, node); > + of_node_put(node); > + return 1; > +}