From: Thierry Reding <thierry.reding@gmail.com>
To: Daniel Vetter <daniel@ffwll.ch>
Cc: linux-tegra@vger.kernel.org, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH] drm/tegra: Do not use ->load() and ->unload() callbacks
Date: Thu, 24 Oct 2019 19:12:34 +0200 [thread overview]
Message-ID: <20191024171234.GA174225@ulmo> (raw)
In-Reply-To: <20191024160754.GK11828@phenom.ffwll.local>
[-- Attachment #1.1: Type: text/plain, Size: 16350 bytes --]
On Thu, Oct 24, 2019 at 06:07:54PM +0200, Daniel Vetter wrote:
> On Thu, Oct 24, 2019 at 05:10:30PM +0200, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> >
> > The ->load() and ->unload() drivers are midlayers and should be avoided
> > in modern drivers. Fix this by moving the code into the driver ->probe()
> > and ->remove() implementations, respectively.
> >
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> > drivers/gpu/drm/tegra/drm.c | 386 +++++++++++++++++-------------------
> > 1 file changed, 186 insertions(+), 200 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
> > index 3012f13bab97..bd7a00272965 100644
> > --- a/drivers/gpu/drm/tegra/drm.c
> > +++ b/drivers/gpu/drm/tegra/drm.c
> > @@ -82,202 +82,6 @@ tegra_drm_mode_config_helpers = {
> > .atomic_commit_tail = tegra_atomic_commit_tail,
> > };
> >
> > -static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
> > -{
> > - struct host1x_device *device = to_host1x_device(drm->dev);
> > - struct iommu_domain *domain;
> > - struct tegra_drm *tegra;
> > - int err;
> > -
> > - tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
> > - if (!tegra)
> > - return -ENOMEM;
> > -
> > - /*
> > - * If the Tegra DRM clients are backed by an IOMMU, push buffers are
> > - * likely to be allocated beyond the 32-bit boundary if sufficient
> > - * system memory is available. This is problematic on earlier Tegra
> > - * generations where host1x supports a maximum of 32 address bits in
> > - * the GATHER opcode. In this case, unless host1x is behind an IOMMU
> > - * as well it won't be able to process buffers allocated beyond the
> > - * 32-bit boundary.
> > - *
> > - * The DMA API will use bounce buffers in this case, so that could
> > - * perhaps still be made to work, even if less efficient, but there
> > - * is another catch: in order to perform cache maintenance on pages
> > - * allocated for discontiguous buffers we need to map and unmap the
> > - * SG table representing these buffers. This is fine for something
> > - * small like a push buffer, but it exhausts the bounce buffer pool
> > - * (typically on the order of a few MiB) for framebuffers (many MiB
> > - * for any modern resolution).
> > - *
> > - * Work around this by making sure that Tegra DRM clients only use
> > - * an IOMMU if the parent host1x also uses an IOMMU.
> > - *
> > - * Note that there's still a small gap here that we don't cover: if
> > - * the DMA API is backed by an IOMMU there's no way to control which
> > - * device is attached to an IOMMU and which isn't, except via wiring
> > - * up the device tree appropriately. This is considered an problem
> > - * of integration, so care must be taken for the DT to be consistent.
> > - */
> > - domain = iommu_get_domain_for_dev(drm->dev->parent);
> > -
> > - if (domain && iommu_present(&platform_bus_type)) {
> > - tegra->domain = iommu_domain_alloc(&platform_bus_type);
> > - if (!tegra->domain) {
> > - err = -ENOMEM;
> > - goto free;
> > - }
> > -
> > - err = iova_cache_get();
> > - if (err < 0)
> > - goto domain;
> > - }
> > -
> > - mutex_init(&tegra->clients_lock);
> > - INIT_LIST_HEAD(&tegra->clients);
> > -
> > - drm->dev_private = tegra;
> > - tegra->drm = drm;
> > -
> > - drm_mode_config_init(drm);
> > -
> > - drm->mode_config.min_width = 0;
> > - drm->mode_config.min_height = 0;
> > -
> > - drm->mode_config.max_width = 4096;
> > - drm->mode_config.max_height = 4096;
> > -
> > - drm->mode_config.allow_fb_modifiers = true;
> > -
> > - drm->mode_config.normalize_zpos = true;
> > -
> > - drm->mode_config.funcs = &tegra_drm_mode_config_funcs;
> > - drm->mode_config.helper_private = &tegra_drm_mode_config_helpers;
> > -
> > - err = tegra_drm_fb_prepare(drm);
> > - if (err < 0)
> > - goto config;
> > -
> > - drm_kms_helper_poll_init(drm);
> > -
> > - err = host1x_device_init(device);
> > - if (err < 0)
> > - goto fbdev;
> > -
> > - if (tegra->group) {
> > - u64 carveout_start, carveout_end, gem_start, gem_end;
> > - u64 dma_mask = dma_get_mask(&device->dev);
> > - dma_addr_t start, end;
> > - unsigned long order;
> > -
> > - start = tegra->domain->geometry.aperture_start & dma_mask;
> > - end = tegra->domain->geometry.aperture_end & dma_mask;
> > -
> > - gem_start = start;
> > - gem_end = end - CARVEOUT_SZ;
> > - carveout_start = gem_end + 1;
> > - carveout_end = end;
> > -
> > - order = __ffs(tegra->domain->pgsize_bitmap);
> > - init_iova_domain(&tegra->carveout.domain, 1UL << order,
> > - carveout_start >> order);
> > -
> > - tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
> > - tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
> > -
> > - drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
> > - mutex_init(&tegra->mm_lock);
> > -
> > - DRM_DEBUG_DRIVER("IOMMU apertures:\n");
> > - DRM_DEBUG_DRIVER(" GEM: %#llx-%#llx\n", gem_start, gem_end);
> > - DRM_DEBUG_DRIVER(" Carveout: %#llx-%#llx\n", carveout_start,
> > - carveout_end);
> > - } else if (tegra->domain) {
> > - iommu_domain_free(tegra->domain);
> > - tegra->domain = NULL;
> > - iova_cache_put();
> > - }
> > -
> > - if (tegra->hub) {
> > - err = tegra_display_hub_prepare(tegra->hub);
> > - if (err < 0)
> > - goto device;
> > - }
> > -
> > - /*
> > - * We don't use the drm_irq_install() helpers provided by the DRM
> > - * core, so we need to set this manually in order to allow the
> > - * DRM_IOCTL_WAIT_VBLANK to operate correctly.
> > - */
> > - drm->irq_enabled = true;
> > -
> > - /* syncpoints are used for full 32-bit hardware VBLANK counters */
> > - drm->max_vblank_count = 0xffffffff;
> > -
> > - err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> > - if (err < 0)
> > - goto hub;
> > -
> > - drm_mode_config_reset(drm);
> > -
> > - err = tegra_drm_fb_init(drm);
> > - if (err < 0)
> > - goto hub;
> > -
> > - return 0;
> > -
> > -hub:
> > - if (tegra->hub)
> > - tegra_display_hub_cleanup(tegra->hub);
> > -device:
> > - if (tegra->domain) {
> > - mutex_destroy(&tegra->mm_lock);
> > - drm_mm_takedown(&tegra->mm);
> > - put_iova_domain(&tegra->carveout.domain);
> > - iova_cache_put();
> > - }
> > -
> > - host1x_device_exit(device);
> > -fbdev:
> > - drm_kms_helper_poll_fini(drm);
> > - tegra_drm_fb_free(drm);
> > -config:
> > - drm_mode_config_cleanup(drm);
> > -domain:
> > - if (tegra->domain)
> > - iommu_domain_free(tegra->domain);
> > -free:
> > - kfree(tegra);
> > - return err;
> > -}
> > -
> > -static void tegra_drm_unload(struct drm_device *drm)
> > -{
> > - struct host1x_device *device = to_host1x_device(drm->dev);
> > - struct tegra_drm *tegra = drm->dev_private;
> > - int err;
> > -
> > - drm_kms_helper_poll_fini(drm);
> > - tegra_drm_fb_exit(drm);
> > - drm_atomic_helper_shutdown(drm);
> > - drm_mode_config_cleanup(drm);
> > -
> > - err = host1x_device_exit(device);
> > - if (err < 0)
> > - return;
> > -
> > - if (tegra->domain) {
> > - mutex_destroy(&tegra->mm_lock);
> > - drm_mm_takedown(&tegra->mm);
> > - put_iova_domain(&tegra->carveout.domain);
> > - iova_cache_put();
> > - iommu_domain_free(tegra->domain);
> > - }
> > -
> > - kfree(tegra);
> > -}
> > -
> > static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
> > {
> > struct tegra_drm_file *fpriv;
> > @@ -1046,8 +850,6 @@ static int tegra_debugfs_init(struct drm_minor *minor)
> > static struct drm_driver tegra_drm_driver = {
> > .driver_features = DRIVER_MODESET | DRIVER_GEM |
> > DRIVER_ATOMIC | DRIVER_RENDER,
> > - .load = tegra_drm_load,
> > - .unload = tegra_drm_unload,
> > .open = tegra_drm_open,
> > .postclose = tegra_drm_postclose,
> > .lastclose = drm_fb_helper_lastclose,
> > @@ -1231,6 +1033,8 @@ void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt,
> > static int host1x_drm_probe(struct host1x_device *dev)
> > {
> > struct drm_driver *driver = &tegra_drm_driver;
> > + struct iommu_domain *domain;
> > + struct tegra_drm *tegra;
> > struct drm_device *drm;
> > int err;
> >
> > @@ -1238,18 +1042,179 @@ static int host1x_drm_probe(struct host1x_device *dev)
> > if (IS_ERR(drm))
> > return PTR_ERR(drm);
> >
> > + tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
> > + if (!tegra) {
> > + err = -ENOMEM;
> > + goto put;
> > + }
> > +
> > + /*
> > + * If the Tegra DRM clients are backed by an IOMMU, push buffers are
> > + * likely to be allocated beyond the 32-bit boundary if sufficient
> > + * system memory is available. This is problematic on earlier Tegra
> > + * generations where host1x supports a maximum of 32 address bits in
> > + * the GATHER opcode. In this case, unless host1x is behind an IOMMU
> > + * as well it won't be able to process buffers allocated beyond the
> > + * 32-bit boundary.
> > + *
> > + * The DMA API will use bounce buffers in this case, so that could
> > + * perhaps still be made to work, even if less efficient, but there
> > + * is another catch: in order to perform cache maintenance on pages
> > + * allocated for discontiguous buffers we need to map and unmap the
> > + * SG table representing these buffers. This is fine for something
> > + * small like a push buffer, but it exhausts the bounce buffer pool
> > + * (typically on the order of a few MiB) for framebuffers (many MiB
> > + * for any modern resolution).
> > + *
> > + * Work around this by making sure that Tegra DRM clients only use
> > + * an IOMMU if the parent host1x also uses an IOMMU.
> > + *
> > + * Note that there's still a small gap here that we don't cover: if
> > + * the DMA API is backed by an IOMMU there's no way to control which
> > + * device is attached to an IOMMU and which isn't, except via wiring
> > + * up the device tree appropriately. This is considered an problem
> > + * of integration, so care must be taken for the DT to be consistent.
> > + */
> > + domain = iommu_get_domain_for_dev(drm->dev->parent);
> > +
> > + if (domain && iommu_present(&platform_bus_type)) {
> > + tegra->domain = iommu_domain_alloc(&platform_bus_type);
> > + if (!tegra->domain) {
> > + err = -ENOMEM;
> > + goto free;
> > + }
> > +
> > + err = iova_cache_get();
> > + if (err < 0)
> > + goto domain;
> > + }
> > +
> > + mutex_init(&tegra->clients_lock);
> > + INIT_LIST_HEAD(&tegra->clients);
> > +
> > dev_set_drvdata(&dev->dev, drm);
> > + drm->dev_private = tegra;
> > + tegra->drm = drm;
> > +
> > + drm_mode_config_init(drm);
> > +
> > + drm->mode_config.min_width = 0;
> > + drm->mode_config.min_height = 0;
> > +
> > + drm->mode_config.max_width = 4096;
> > + drm->mode_config.max_height = 4096;
> > +
> > + drm->mode_config.allow_fb_modifiers = true;
> > +
> > + drm->mode_config.normalize_zpos = true;
> > +
> > + drm->mode_config.funcs = &tegra_drm_mode_config_funcs;
> > + drm->mode_config.helper_private = &tegra_drm_mode_config_helpers;
> > +
> > + err = tegra_drm_fb_prepare(drm);
> > + if (err < 0)
> > + goto config;
> > +
> > + drm_kms_helper_poll_init(drm);
> > +
> > + err = host1x_device_init(dev);
> > + if (err < 0)
> > + goto fbdev;
> > +
> > + if (tegra->group) {
> > + u64 carveout_start, carveout_end, gem_start, gem_end;
> > + u64 dma_mask = dma_get_mask(&dev->dev);
> > + dma_addr_t start, end;
> > + unsigned long order;
> > +
> > + start = tegra->domain->geometry.aperture_start & dma_mask;
> > + end = tegra->domain->geometry.aperture_end & dma_mask;
> > +
> > + gem_start = start;
> > + gem_end = end - CARVEOUT_SZ;
> > + carveout_start = gem_end + 1;
> > + carveout_end = end;
> > +
> > + order = __ffs(tegra->domain->pgsize_bitmap);
> > + init_iova_domain(&tegra->carveout.domain, 1UL << order,
> > + carveout_start >> order);
> > +
> > + tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
> > + tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
> > +
> > + drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
> > + mutex_init(&tegra->mm_lock);
> > +
> > + DRM_DEBUG_DRIVER("IOMMU apertures:\n");
> > + DRM_DEBUG_DRIVER(" GEM: %#llx-%#llx\n", gem_start, gem_end);
> > + DRM_DEBUG_DRIVER(" Carveout: %#llx-%#llx\n", carveout_start,
> > + carveout_end);
> > + } else if (tegra->domain) {
> > + iommu_domain_free(tegra->domain);
> > + tegra->domain = NULL;
> > + iova_cache_put();
> > + }
> > +
> > + if (tegra->hub) {
> > + err = tegra_display_hub_prepare(tegra->hub);
> > + if (err < 0)
> > + goto device;
> > + }
> > +
> > + /*
> > + * We don't use the drm_irq_install() helpers provided by the DRM
> > + * core, so we need to set this manually in order to allow the
> > + * DRM_IOCTL_WAIT_VBLANK to operate correctly.
> > + */
> > + drm->irq_enabled = true;
> > +
> > + /* syncpoints are used for full 32-bit hardware VBLANK counters */
> > + drm->max_vblank_count = 0xffffffff;
> > +
> > + err = drm_vblank_init(drm, drm->mode_config.num_crtc);
> > + if (err < 0)
> > + goto hub;
> > +
> > + drm_mode_config_reset(drm);
> > +
> > + err = tegra_drm_fb_init(drm);
> > + if (err < 0)
> > + goto hub;
> >
> > err = drm_fb_helper_remove_conflicting_framebuffers(NULL, "tegradrmfb", false);
>
> I think you want to do this before you set up the drmfb. Well probably you
> want to do this as the one of the very first things in your probe code,
> before you start touching any of the hw. At least that's what the old
> order did.
Hm... you're right. I had actually wondered about this and then
concluded that it might be best to only remove the conflicting
framebuffers when it was relatively certain that the driver could
correctly create a new DRM framebuffer.
But yeah, it definitely needs to kick out the conflicting framebuffer
before the call to tegra_drm_fb_init().
I'll Cc Michał on the next version, maybe he's got a way to actually
test this. I don't have access to any hardware where the firmware
installs a framebuffer.
Thierry
>
> > if (err < 0)
> > - goto put;
> > + goto fb;
> >
> > err = drm_dev_register(drm, 0);
> > if (err < 0)
> > - goto put;
> > + goto fb;
> >
> > return 0;
> >
> > +fb:
> > + tegra_drm_fb_exit(drm);
> > +hub:
> > + if (tegra->hub)
> > + tegra_display_hub_cleanup(tegra->hub);
> > +device:
> > + if (tegra->domain) {
> > + mutex_destroy(&tegra->mm_lock);
> > + drm_mm_takedown(&tegra->mm);
> > + put_iova_domain(&tegra->carveout.domain);
> > + iova_cache_put();
> > + }
> > +
> > + host1x_device_exit(dev);
> > +fbdev:
> > + drm_kms_helper_poll_fini(drm);
> > + tegra_drm_fb_free(drm);
> > +config:
> > + drm_mode_config_cleanup(drm);
> > +domain:
> > + if (tegra->domain)
> > + iommu_domain_free(tegra->domain);
> > +free:
> > + kfree(tegra);
> > put:
> > drm_dev_put(drm);
> > return err;
> > @@ -1258,8 +1223,29 @@ static int host1x_drm_probe(struct host1x_device *dev)
> > static int host1x_drm_remove(struct host1x_device *dev)
> > {
> > struct drm_device *drm = dev_get_drvdata(&dev->dev);
> > + struct tegra_drm *tegra = drm->dev_private;
> > + int err;
> >
> > drm_dev_unregister(drm);
> > +
> > + drm_kms_helper_poll_fini(drm);
> > + tegra_drm_fb_exit(drm);
> > + drm_atomic_helper_shutdown(drm);
> > + drm_mode_config_cleanup(drm);
> > +
> > + err = host1x_device_exit(dev);
> > + if (err < 0)
> > + dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err);
> > +
> > + if (tegra->domain) {
> > + mutex_destroy(&tegra->mm_lock);
> > + drm_mm_takedown(&tegra->mm);
> > + put_iova_domain(&tegra->carveout.domain);
> > + iova_cache_put();
> > + iommu_domain_free(tegra->domain);
> > + }
> > +
> > + kfree(tegra);
> > drm_dev_put(drm);
> >
> > return 0;
>
> Aside from the one question:
>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
> > --
> > 2.23.0
> >
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
prev parent reply other threads:[~2019-10-24 17:12 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-10-24 15:10 [PATCH] drm/tegra: Do not use ->load() and ->unload() callbacks Thierry Reding
2019-10-24 16:07 ` Daniel Vetter
2019-10-24 17:12 ` Thierry Reding [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20191024171234.GA174225@ulmo \
--to=thierry.reding@gmail.com \
--cc=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-tegra@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.