Received: by 2002:a05:6a10:6d10:0:0:0:0 with SMTP id gq16csp851967pxb; Fri, 15 Apr 2022 13:12:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzXjHNyYPOGSBw+8WIcwaMaZ686VQNXxjwRbQnWaVTQ5g4lEJEoFHZ9MsfF59XIByWF2FC3 X-Received: by 2002:a17:906:dc89:b0:6db:a789:7563 with SMTP id cs9-20020a170906dc8900b006dba7897563mr495846ejc.471.1650053568925; Fri, 15 Apr 2022 13:12:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1650053568; cv=none; d=google.com; s=arc-20160816; b=eerlkl6M6dUVh5tha9qMl8Q81bih1JuMShAf1wxjKWLrPEZPb5i92RDH54S4Lp5oX/ UIGH+z9eLD1d97WNiIN1T6EW0Gsjsewsc8QyrI9F8sCXqN6JDPOV7EHZeKRclewofXFt rKHq2vN2zWtWtUePJ5NV9C6zuXrDGTa8281x2gBA0Z39VTqq7RRo/BiMHpdCc+M0Hz5W 0yGOOTzNaa2ARVjtq0wxgvlyqBHOmRn8cdRNFbRwwneRd/DjNcq+QuBk3/M16T5ioAlN bMPC5K5CM7U9bv/c9uyBo2wxi4t+Qc4htRQ0oWRdD1QpMj/6ZQzCm0eU5t1yglYeinyK mZQw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:in-reply-to:from :references:cc:to:content-language:subject:user-agent:mime-version :date:message-id:dkim-signature; bh=csxGDJowjwfPfYbPWapMKvmutL0jsjVI2/38LVM08ks=; b=sGGEbOJU4pedp2csIzSxx/yXnZuW8EqGedqpBQet45+Gs/C/J5lDv3lhmHvRZWecur YdQO4Z1w91uoQDH7Yh+Bb7q2P+GtiXCL9JmzaHgHR8PlqgXsvakhvUaxDTC7dalgr7ux NDP/w7Jvqe56szXGOi0xI1RYHb9Hu5GgYy5xyOF9dq3JikAFLH/rewB2tPMJuICWYBNo W8nEHRJTcKTxyD5XpI/+gjuP24YRCBwzdzdNYnYo9ABEciT2jI0aeZkKXpw+1Pswjviu 6DbqfKy3SXbGOnZEuI/bZzV7J7AjZWq+sCTeZZlNGx70dL6D/PXJFvYEF3/9kzaZA0oB /3KA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YEZiGpve; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id b1-20020a50ccc1000000b0041d89a8523dsi1548905edj.491.2022.04.15.13.12.18; Fri, 15 Apr 2022 13:12:48 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=YEZiGpve; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237315AbiDOAt3 (ORCPT + 99 others); Thu, 14 Apr 2022 20:49:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40410 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234201AbiDOAt2 (ORCPT ); Thu, 14 Apr 2022 20:49:28 -0400 Received: from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com [IPv6:2a00:1450:4864:20::22a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C33D9B822D for ; Thu, 14 Apr 2022 17:47:00 -0700 (PDT) Received: by mail-lj1-x22a.google.com with SMTP id m8so7977026ljc.7 for ; Thu, 14 Apr 2022 17:47:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=message-id:date:mime-version:user-agent:subject:content-language:to :cc:references:from:in-reply-to:content-transfer-encoding; bh=csxGDJowjwfPfYbPWapMKvmutL0jsjVI2/38LVM08ks=; b=YEZiGpver9sFJAEaj/Wlc4KH7/QjuuTQm2INVtXsCnOkKyaMHHV1B/AAm8WGVpIXk7 0mzrr4pHLliyFt4W/TtUbcGNt5ksYaZFWIoHKEfsC5c2np5ZrRJHb0DGd3WWVWu7NXeA mBEU9/Pyv+9Ihhrwh7Tv3nwTrZBtqFFWoR+D2eHXew++ORh8dY5Lbvct+tUEJMHt8Qhq /+0modCBYvkaxfVGNr2OcPuBF/yIfAY48MqAdz2eOuUOLF/7Z/ZMdrswEWrT5jpgF78B cF+tCvv/CY4HHtzt8UdZn94RJm+pNsvWyAt9Ok16y1RwGnd1wcboD1VmP+P6SsvaQyIZ 3EMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:cc:references:from:in-reply-to :content-transfer-encoding; bh=csxGDJowjwfPfYbPWapMKvmutL0jsjVI2/38LVM08ks=; b=7FU6MFR+/vtTWA8VQALf2gL2KbEUyJjT00twFtdrYCwrblua2OXYsoQh5Rt0AUUnr3 MlQc1PQjV5fOLELXng1h63Oj0S8/XlkhNGZ2Z/3QCL84ThYFu1QZ5uhjBJjld8QYV8cA LCxwiY1s6aKkxYb1LsNMCDor4qOLYwDkxEzqkgCD/BL9i/MM1lvPsm0NIf5NgmUiqroE Ez9CaW/WHQErETRqd/SIzVn7F0F+MDrZq1gz6S9dbiwggoQjd8+fiXuLKxFeGehsAl65 Ns8Xdq+rrH+ZO1F6LqhepSvTX3I121meEYhK6anmISwGD/M2lbgR+rQ4xEV7oasko5Uk 38Yw== X-Gm-Message-State: AOAM533VM9uOt7kI+5qrV+fzJgOR3xvb1Vc6JdvP1v8X3FCHFmYU3Nea MlLTQm2HTCkissEBPtDENntldA== X-Received: by 2002:a2e:3a1a:0:b0:24b:59d4:7006 with SMTP id h26-20020a2e3a1a000000b0024b59d47006mr3024682lja.392.1649983618953; Thu, 14 Apr 2022 17:46:58 -0700 (PDT) Received: from [192.168.1.211] ([37.153.55.125]) by smtp.gmail.com with ESMTPSA id z24-20020a0565120c1800b0044add225193sm156525lfu.228.2022.04.14.17.46.58 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 14 Apr 2022 17:46:58 -0700 (PDT) Message-ID: Date: Fri, 15 Apr 2022 03:46:58 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.8.0 Subject: Re: [RFC PATCH 1/6] drm/dp: Helpers to make it easier for drivers to use DP AUX bus properly Content-Language: en-GB To: Douglas Anderson , dri-devel@lists.freedesktop.org Cc: Robert Foss , Hsin-Yi Wang , Abhinav Kumar , Sankeerth Billakanti , Philip Chen , Stephen Boyd , Daniel Vetter , David Airlie , Linus Walleij , Lyude Paul , Thomas Zimmermann , linux-kernel@vger.kernel.org References: <20220409023628.2104952-1-dianders@chromium.org> <20220408193536.RFC.1.I4182ae27e00792842cb86f1433990a0ef9c0a073@changeid> From: Dmitry Baryshkov In-Reply-To: <20220408193536.RFC.1.I4182ae27e00792842cb86f1433990a0ef9c0a073@changeid> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NICE_REPLY_A,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 09/04/2022 05:36, Douglas Anderson wrote: > As talked about in the kerneldoc for "struct dp_aux_ep_client" in this > patch and also in the past in commit a1e3667a9835 ("drm/bridge: > ti-sn65dsi86: Promote the AUX channel to its own sub-dev"), to use the > DP AUX bus properly we really need two "struct device"s. One "struct > device" is in charge of providing the DP AUX bus and the other is > where we'll try to get a reference to the newly probed endpoint > devices. > > In ti-sn65dsi86 this wasn't too difficult to accomplish. That driver > is already broken up into several "struct devices" anyway because it > also provides a PWM and some GPIOs. Adding one more wasn't that > difficult / ugly. > > When I tried to do the same solution in parade-ps8640, it felt like I > was copying too much boilerplate code. I made the realization that I > didn't _really_ need a separate "driver" for each person that wanted > to do the same thing. By putting all the "driver" related code in a > common place then we could save a bit of hassle. This change > effectively adds a new "ep_client" driver that can be used by > anyone. The devices instantiated by this driver will just call through > to the probe/remove/shutdown calls provided. > > At the moment, the "ep_client" driver is backed by the Linux auxiliary > bus (unfortunate naming--this has nothing to do with DP AUX). I didn't > want to expose this to clients, though, so as far as clients are > concerned they get a vanilla "struct device". I have been thinking about your approach for quite some time. I think that enforcing a use of auxilliary device is an overkill. What do we really need is the the set callbacks in the bus struct or a notifier. We have to notify the aux_bus controller side that the client has been probed successfully or that the client is going to be removed. And this approach would make driver's life easier, since e.g. the bus code can pm_get the EP device before calling callbacks/notifiers and pm_put_autosuspend it afterwards. > > Signed-off-by: Douglas Anderson > --- > > drivers/gpu/drm/dp/drm_dp_aux_bus.c | 165 +++++++++++++++++++++++++++- > include/drm/dp/drm_dp_aux_bus.h | 58 ++++++++++ > 2 files changed, 222 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/dp/drm_dp_aux_bus.c b/drivers/gpu/drm/dp/drm_dp_aux_bus.c > index 415afce3cf96..5386ceacf133 100644 > --- a/drivers/gpu/drm/dp/drm_dp_aux_bus.c > +++ b/drivers/gpu/drm/dp/drm_dp_aux_bus.c > @@ -12,6 +12,7 @@ > * to perform transactions on that bus. > */ > > +#include > #include > #include > #include > @@ -299,6 +300,163 @@ void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *drv) > } > EXPORT_SYMBOL_GPL(dp_aux_dp_driver_unregister); > > +/* ----------------------------------------------------------------------------- > + * DP AUX EP Client > + */ > + > +struct dp_aux_ep_client_data { > + struct dp_aux_ep_client *client; > + struct auxiliary_device adev; > +}; > + > +static int dp_aux_ep_client_probe(struct auxiliary_device *adev, > + const struct auxiliary_device_id *id) > +{ > + struct dp_aux_ep_client_data *data = > + container_of(adev, struct dp_aux_ep_client_data, adev); > + > + if (!data->client->probe) > + return 0; > + > + return data->client->probe(&adev->dev, data->client); > +} > + > +static void dp_aux_ep_client_remove(struct auxiliary_device *adev) > +{ > + struct dp_aux_ep_client_data *data = > + container_of(adev, struct dp_aux_ep_client_data, adev); > + > + if (data->client->remove) > + data->client->remove(&adev->dev, data->client); > +} > + > +static void dp_aux_ep_client_shutdown(struct auxiliary_device *adev) > +{ > + struct dp_aux_ep_client_data *data = > + container_of(adev, struct dp_aux_ep_client_data, adev); > + > + if (data->client->shutdown) > + data->client->shutdown(&adev->dev, data->client); > +} > + > +static void dp_aux_ep_client_dev_release(struct device *dev) > +{ > + struct auxiliary_device *adev = to_auxiliary_dev(dev); > + struct dp_aux_ep_client_data *data = > + container_of(adev, struct dp_aux_ep_client_data, adev); > + > + kfree(data); > +} > + > +/** > + * dp_aux_register_ep_client() - Register an DP AUX EP client > + * @client: The structure describing the client. It's the client's > + * responsibility to keep this memory around until > + * dp_aux_unregister_ep_client() is called, either explicitly or > + * implicitly via devm. > + * > + * See the description of "struct dp_aux_ep_client" for a full explanation > + * of when you should use this and why. > + * > + * Return: 0 if no error or negative error code. > + */ > +int dp_aux_register_ep_client(struct dp_aux_ep_client *client) > +{ > + struct dp_aux_ep_client_data *data; > + int ret; > + > + data = kzalloc(sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->client = client; > + data->adev.name = "ep_client"; > + data->adev.dev.parent = client->aux->dev; > + data->adev.dev.release = dp_aux_ep_client_dev_release; > + device_set_of_node_from_dev(&data->adev.dev, client->aux->dev); > + > + ret = auxiliary_device_init(&data->adev); > + if (ret) { > + /* > + * NOTE: if init doesn't fail then it takes ownership > + * of memory and this kfree() is magically part of > + * auxiliary_device_uninit(). > + */ > + kfree(data); > + return ret; > + } > + > + ret = auxiliary_device_add(&data->adev); > + if (ret) > + goto err_did_init; > + > + client->_opaque = data; > + > + return 0; > + > +err_did_init: > + auxiliary_device_uninit(&data->adev); > + return ret; > +} > + > +/** > + * dp_aux_unregister_ep_client() - Inverse of dp_aux_register_ep_client() > + * @client: The structure describing the client. > + * > + * If dp_aux_register_ep_client() returns no error then you should call this > + * to free resources. > + */ > +void dp_aux_unregister_ep_client(struct dp_aux_ep_client *client) > +{ > + struct dp_aux_ep_client_data *data = client->_opaque; > + > + auxiliary_device_delete(&data->adev); > + auxiliary_device_uninit(&data->adev); > +} > + > +static void dp_aux_unregister_ep_client_void(void *data) > +{ > + dp_aux_unregister_ep_client(data); > +} > + > +/** > + * devm_dp_aux_register_ep_client() - devm wrapper for dp_aux_register_ep_client() > + * @client: The structure describing the client. > + * > + * Handles freeing w/ devm on the device "client->aux->dev". > + * > + * Return: 0 if no error or negative error code. > + */ > +int devm_dp_aux_register_ep_client(struct dp_aux_ep_client *client) > +{ > + int ret; > + > + ret = dp_aux_register_ep_client(client); > + if (ret) > + return ret; > + > + return devm_add_action_or_reset(client->aux->dev, > + dp_aux_unregister_ep_client_void, > + client); > +} > + > +static const struct auxiliary_device_id dp_aux_ep_client_id_table[] = { > + { .name = "drm_dp_aux_bus.ep_client", }, > + {}, > +}; > + > +static struct auxiliary_driver dp_aux_ep_client_driver = { > + .name = "ep_client", > + .probe = dp_aux_ep_client_probe, > + .remove = dp_aux_ep_client_remove, > + .shutdown = dp_aux_ep_client_shutdown, > + .id_table = dp_aux_ep_client_id_table, > +}; > + > +/* ----------------------------------------------------------------------------- > + * Module init > + */ > + > static int __init dp_aux_bus_init(void) > { > int ret; > @@ -307,11 +465,16 @@ static int __init dp_aux_bus_init(void) > if (ret) > return ret; > > - return 0; > + ret = auxiliary_driver_register(&dp_aux_ep_client_driver); > + if (ret) > + bus_unregister(&dp_aux_bus_type); > + > + return ret; > } > > static void __exit dp_aux_bus_exit(void) > { > + auxiliary_driver_unregister(&dp_aux_ep_client_driver); > bus_unregister(&dp_aux_bus_type); > } > > diff --git a/include/drm/dp/drm_dp_aux_bus.h b/include/drm/dp/drm_dp_aux_bus.h > index 4f19b20b1dd6..ecf68b6873bd 100644 > --- a/include/drm/dp/drm_dp_aux_bus.h > +++ b/include/drm/dp/drm_dp_aux_bus.h > @@ -54,4 +54,62 @@ int __dp_aux_dp_driver_register(struct dp_aux_ep_driver *aux_ep_drv, > struct module *owner); > void dp_aux_dp_driver_unregister(struct dp_aux_ep_driver *aux_ep_drv); > > +/** > + * struct dp_aux_ep_device - Helper for clients of DP AUX EP devices > + * > + * The DP AUX bus can be a bit tricky to use properly. Usually, the way > + * things work is that: > + * - The DP controller driver provides the DP AUX bus and would like to probe > + * the endpoints on the DP AUX bus (AKA the panel) as part of its probe > + * routine. > + * - The DP controller driver would also like to acquire a reference to the > + * DP AUX endpoints (AKA the panel) as part of its probe. > + * > + * The problem is that the DP AUX endpoints aren't guaranteed to complete their > + * probe right away. They could be probing asynchronously or they simply might > + * fail to acquire some resource and return -EPROBE_DEFER. > + * > + * The best way to solve this is to break the DP controller's probe into > + * two parts. The first part will create the DP AUX bus. The second part will > + * acquire the reference to the DP AUX endpoint. The first part can complete > + * finish with no problems and be "done" even if the second part ends up > + * deferring while waiting for the DP AUX endpoint. > + * > + * The dp_aux_ep_client structure and associated functions help with managing > + * this common case. They will create/add a second "struct device" for you. > + * In the probe for this second "struct device" (known as the "clientdev" here) > + * you can acquire references to the AUX DP endpoints and can freely return > + * -EPROBE_DEFER if they're not ready yet. > + * > + * A few notes: > + * - The parent of the clientdev is guaranteed to be aux->dev > + * - The of_node of the clientdev is guaranteed to be the same as the of_node > + * of aux->dev, copied with device_set_of_node_from_dev(). > + * - If you're doing "devm" type things in the clientdev's probe you should > + * use the clientdev. This makes lifetimes be managed properly. > + * > + * NOTE: there's no requirement to use these helpers if you have solved the > + * problem described above in some other way. > + */ > +struct dp_aux_ep_client { > + /** @probe: The second half of the probe */ > + int (*probe)(struct device *clientdev, struct dp_aux_ep_client *client); > + > + /** @remove: Remove function corresponding to the probe */ > + void (*remove)(struct device *clientdev, struct dp_aux_ep_client *client); > + > + /** @shutdown: Shutdown function corresponding to the probe */ > + void (*shutdown)(struct device *clientdev, struct dp_aux_ep_client *client); > + > + /** @aux: The AUX bus */ > + struct drm_dp_aux *aux; > + > + /** @_opaque: Used by the implementation */ > + void *_opaque; > +}; > + > +int dp_aux_register_ep_client(struct dp_aux_ep_client *client); > +void dp_aux_unregister_ep_client(struct dp_aux_ep_client *client); > +int devm_dp_aux_register_ep_client(struct dp_aux_ep_client *client); > + > #endif /* _DP_AUX_BUS_H_ */ -- With best wishes Dmitry