Received: by 10.192.165.156 with SMTP id m28csp935217imm; Wed, 18 Apr 2018 00:38:42 -0700 (PDT) X-Google-Smtp-Source: AIpwx49eFwZ9zWrcT6ILgynswuWEQQk+GOkL1yT6VE0OcaAZMJW8Atoqreu3pCdH/kJE14gyq6Dd X-Received: by 10.98.222.198 with SMTP id h189mr995050pfg.143.1524037122902; Wed, 18 Apr 2018 00:38:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1524037122; cv=none; d=google.com; s=arc-20160816; b=NbGILx7aIWQphJ4oc0B4KDnkYASgKw+PI9u/66gRm1Xk5FtWPOxNGNTRKhau3nA3Td ETioQ6J+Oc0KT67LVT81GRfDfcowo71Hk8EVoDOdCGfs0ItvYWuGbngUvCs07OvtQhTQ FLoeTD0V+GfkPZPkQvenjEW4n0HZLWPb9Lk9Xhe2KRwWg9H9hZPh2Vt3k5Zy7tN5Bea9 e2LVoSl37GteK9EJbd9DekU37ooWKlQ8FKVE5R/xjgL/EkN6H6BVOQaOMPU7xjdKXPoy /Q+kkjSZ+ZOWOPW7BXr1fN/JX0RslK7uZmgkEQZLbMzoGIkFMkekCpcNX/qu8m26XNlD q3xg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:subject:cc:to:from:date :arc-authentication-results; bh=2U7me6QyYCadfkSedy9lgkYXBgrZn0B2TWl4VsxDaJA=; b=VoM5ohAXvtKKpHzVOgicFqreQlx2Pa2AdjKA652Krc2fV3lqUrsaxSYhVjsjAPf1Pf RjcFIrDEC5xiySgCwg5999/kgSNd/Ab+vcp2SAOPlPf2LcNEc5od+Q87UX+Q3IQXon9h y+YEZ3HbQ3XXY/6CMI20r9nISIcrGOA1NacBK8EnrIScDu88eZF9ZC/9FRE60oQRNAsr RBbNu6n/ZboeG1y0AIOawdXdYH3NJqL1RXihHPDRrS6x6kE5dyqFsEKCSWh8KQ0d1xZo Rm7bc2TI0xOeVYQ4yFNPvcfjssKaATeuDv6TY5H7Qp1R5GqmdEMV13kvB+70XhgHZIt3 Je4g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id e13si575977pgr.117.2018.04.18.00.38.28; Wed, 18 Apr 2018 00:38:42 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752749AbeDRHhE (ORCPT + 99 others); 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 Received: by mail.bootlin.com (Postfix, from userid 110) id 520432085F; Wed, 18 Apr 2018 09:37:00 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.bootlin.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from bbrezillon (AAmiens-652-1-190-101.w83-192.abo.wanadoo.fr [83.192.189.101]) by mail.bootlin.com (Postfix) with ESMTPSA id 6AA642071B; Wed, 18 Apr 2018 09:36:49 +0200 (CEST) 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 Precedence: bulk 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; > +}