On Sunday, October 25, 2026 5:51 PM Svyatoslav Ryhel wrote:
> нд, 25 жовт. 2026 р. о 03:46 Mikko Perttunen <[email protected]> пише:
> >
> > On Friday, October 23, 2026 11:14 PM Svyatoslav Ryhel wrote:
> > > пт, 23 жовт. 2026 р. о 11:26 Svyatoslav Ryhel <[email protected]> пише:
> > > >
> > > > пт, 23 жовт. 2026 р. о 10:53 Mikko Perttunen <[email protected]> 
> > > > пише:
> > > > >
> > > ...
> > > > > > +MODULE_ALIAS("platform:tegra-dbi");
> > > > > > +MODULE_AUTHOR("Svyatoslav Ryhel <[email protected]>");
> > > > > > +MODULE_DESCRIPTION("Nvidia Tegra20/30 MIPI-DBI Type B driver");
> > > > > > +MODULE_LICENSE("GPL");
> > > > >
> > > > > No separate platform driver or device. If a separate struct device is
> > > > > needed for some technical purpose, we need to create a minimal 
> > > > > separate
> > > > > virtual device, but unless there is a strong reason to have that we
> > > > > should be fine with just the dc device.
> > > > >
> > > > > Thank you
> > > > > Mikko
> > > > >
> > > >
> > > > ATM, I use bridge framework to describe interface and I am attaching
> > > > it to RGB since it basically uses RGB to transfer video data. It works
> > > > pretty nice so far.
> > > >
> > > > You propose to replicate how RGB works just for DBI?
> > > >
> > >
> > > One of the reasons I used separate pdev was to be able get mipi_dbi
> > > from it using dev_get_drvdata() from patent (dbi bridge in this case).
> > > DBI framework seems to not have a way to pass it in any other way,
> > > unless you know smth I don't.
> >
> > Right, I think using a bridge is fine; passing the mipi_dbi interface is
> > properly done in other ways. The direct parent devdata link is a bit of
> > a "poor man's bus", and sometimes it breaks down, such as here.
> >
> > MIPI DBI is comparable to MIPI DSI -- both have a hardware channel that
> > sends both pixel data and commands. I think we should follow DSI's
> > example and have a DBI bus and devices while keeping it as simple as
> > possible.
> >
> > The DC driver would register a DBI host and the panel would bind as a
> > DBI device. The DC driver can then supply the mipi_dbi object in a field
> > of the mipi_dbi_device.
> >
> > This is a bit more complicated than a parent devdata pointer but much
> > cleaner, and follows the existing DSI pattern.
> >
> 
> I understand your intentions, but ATM kernel lacks any DBI type B
> framework. No hosts, ho devices, noting granular. Implementing
> everything just not worth my time to support 2 panels of device based
> on Tegra 20. And I am talking not about time to actually implement
> this in the code, but to upstream. DSI framework is huge, copying even
> part of it to wrap legacy DBI interface and ustreaming it does not
> worth hassle. And yes, I am a poor man, I use a parent devdata bus.

I sketched out the bus implementation and I really don't think it's that 
complicated -- about 200 lines in total. See below. If there turn out to 
be issues we can put an equivalent into drm/tegra/.

-- drm_mipi_dbi_bus.c

/* MIPI DBI devices with command transport through parallel interface */

#include <linux/err.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/slab.h>

#include <drm/drm_mipi_dbi.h>
#include <drm/drm_mipi_dbi_bus.h>

static int mipi_dbi_match(struct device *dev, const struct device_driver 
*driver)
{
        return of_driver_match_device(dev, driver);
}

static int mipi_dbi_uevent(const struct device *dev, struct kobj_uevent_env 
*env)
{
        return of_device_uevent_modalias(dev, env);
}

static const struct bus_type mipi_dbi_bus_type = {
        .name = "mipi-dbi",
        .match = mipi_dbi_match,
        .uevent = mipi_dbi_uevent,
};

static void mipi_dbi_device_release(struct device *dev)
{
        of_node_put(dev->of_node);
        kfree(to_mipi_dbi_device(dev));
}

static void mipi_dbi_device_unregister(void *data)
{
        struct device *dev = data;

        of_node_clear_flag(dev->of_node, OF_POPULATED);
        device_unregister(dev);
}

/* called by dbi host driver to create panel device */
struct mipi_dbi_device *
devm_mipi_dbi_device_register(struct mipi_dbi_host *host, struct device_node 
*np)
{
        struct mipi_dbi_device *dbidev;
        int ret;

        if (!host || !host->dev || !host->dbi || !host->dbi->command ||
            !host->ops || !host->ops->attach || !host->ops->detach || !np)
                return ERR_PTR(-EINVAL);

        if (!of_device_is_available(np))
                return ERR_PTR(-ENODEV);

        dbidev = kzalloc_obj(*dbidev);
        if (!dbidev)
                return ERR_PTR(-ENOMEM);

        if (of_node_test_and_set_flag(np, OF_POPULATED)) {
                kfree(dbidev);
                return ERR_PTR(-EBUSY);
        }

        dbidev->host = host;
        dbidev->dbi = host->dbi;
        device_initialize(&dbidev->dev);
        dbidev->dev.parent = host->dev;
        dbidev->dev.bus = &mipi_dbi_bus_type;
        dbidev->dev.release = mipi_dbi_device_release;
        device_set_node(&dbidev->dev, of_fwnode_handle(of_node_get(np)));

        ret = dev_set_name(&dbidev->dev, "%s:%s", dev_name(host->dev), 
np->full_name);
        if (ret)
                goto put_device;

        ret = device_add(&dbidev->dev);
        if (ret)
                goto put_device;

        ret = devm_add_action_or_reset(host->dev, mipi_dbi_device_unregister,
                                       &dbidev->dev);
        if (ret)
                return ERR_PTR(ret);

        return dbidev;

put_device:
        of_node_clear_flag(np, OF_POPULATED);
        put_device(&dbidev->dev);
        return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(devm_mipi_dbi_device_register);

static void mipi_dbi_detach(void *data)
{
        struct mipi_dbi_device *dev = data;

        dev->host->ops->detach(dev->host, dev);
        dev->attached = false;
}

/* called by panel on probe completion to attach to dbi host */
int devm_mipi_dbi_attach(struct mipi_dbi_device *dev)
{
        int ret;

        if (dev->attached)
                return -EBUSY;

        ret = dev->host->ops->attach(dev->host, dev);
        if (ret)
                return ret;

        dev->attached = true;
        return devm_add_action_or_reset(&dev->dev, mipi_dbi_detach, dev);
}
EXPORT_SYMBOL_GPL(devm_mipi_dbi_attach);

int __mipi_dbi_driver_register(struct device_driver *driver, struct module 
*owner)
{
        driver->owner = owner;
        driver->bus = &mipi_dbi_bus_type;

        return driver_register(driver);
}
EXPORT_SYMBOL_GPL(__mipi_dbi_driver_register);

void mipi_dbi_driver_unregister(struct device_driver *driver)
{
        driver_unregister(driver);
}
EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);

static int __init mipi_dbi_bus_init(void)
{
        return bus_register(&mipi_dbi_bus_type);
}
subsys_initcall(mipi_dbi_bus_init);

static void __exit mipi_dbi_bus_exit(void)
{
        bus_unregister(&mipi_dbi_bus_type);
}
module_exit(mipi_dbi_bus_exit);

-- drm_mipi_dbi_bus.h

#ifndef __DRM_MIPI_DBI_BUS_H__
#define __DRM_MIPI_DBI_BUS_H__

#include <linux/device.h>
#include <linux/module.h>

struct device_node;
struct mipi_dbi;
struct mipi_dbi_device;
struct mipi_dbi_host;

struct mipi_dbi_host_ops {
        int (*attach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev);
        void (*detach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev);
};

struct mipi_dbi_host {
        struct device *dev;
        struct mipi_dbi *dbi;
        const struct mipi_dbi_host_ops *ops;
};

struct mipi_dbi_device {
        struct device dev;
        struct mipi_dbi *dbi;
        struct mipi_dbi_host *host;
        bool attached;
};

static inline struct mipi_dbi_device *to_mipi_dbi_device(struct device *dev)
{
        return container_of(dev, struct mipi_dbi_device, dev);
}

struct mipi_dbi_device *
devm_mipi_dbi_device_register(struct mipi_dbi_host *host, struct device_node 
*np);
int devm_mipi_dbi_attach(struct mipi_dbi_device *dev);

int __mipi_dbi_driver_register(struct device_driver *driver, struct module 
*owner);
void mipi_dbi_driver_unregister(struct device_driver *driver);

#define mipi_dbi_driver_register(driver) \
        __mipi_dbi_driver_register(driver, THIS_MODULE)

#define module_mipi_dbi_driver(driver) \
        module_driver(driver, mipi_dbi_driver_register, 
mipi_dbi_driver_unregister)

#endif




Reply via email to