[PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support

Rob Clark robdclark at gmail.com
Tue Aug 13 17:52:15 PDT 2013


On Fri, Aug 9, 2013 at 1:14 PM, Laurent Pinchart
<laurent.pinchart+renesas at ideasonboard.com> wrote:
> MIPI DBI is a configurable-width parallel display bus that transmits
> commands and data.
>
> Add a new DBI Linux bus type that implements the usual bus
> infrastructure (including devices and drivers (un)registration and
> matching, and bus configuration and access functions).
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  drivers/video/display/Kconfig        |   8 ++
>  drivers/video/display/Makefile       |   1 +
>  drivers/video/display/mipi-dbi-bus.c | 234 +++++++++++++++++++++++++++++++++++
>  include/video/display.h              |   4 +
>  include/video/mipi-dbi-bus.h         | 125 +++++++++++++++++++
>  5 files changed, 372 insertions(+)
>  create mode 100644 drivers/video/display/mipi-dbi-bus.c
>  create mode 100644 include/video/mipi-dbi-bus.h
>
> diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
> index 1d533e7..f7532c1 100644
> --- a/drivers/video/display/Kconfig
> +++ b/drivers/video/display/Kconfig
> @@ -2,3 +2,11 @@ menuconfig DISPLAY_CORE
>         tristate "Display Core"
>         ---help---
>           Support common display framework for graphics devices.
> +
> +if DISPLAY_CORE
> +
> +config DISPLAY_MIPI_DBI
> +       tristate
> +       default n
> +
> +endif # DISPLAY_CORE
> diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
> index b907aad..59022d2 100644
> --- a/drivers/video/display/Makefile
> +++ b/drivers/video/display/Makefile
> @@ -1,3 +1,4 @@
>  display-y                                      := display-core.o \
>                                                    display-notifier.o
>  obj-$(CONFIG_DISPLAY_CORE)                     += display.o
> +obj-$(CONFIG_DISPLAY_MIPI_DBI)                 += mipi-dbi-bus.o
> diff --git a/drivers/video/display/mipi-dbi-bus.c b/drivers/video/display/mipi-dbi-bus.c
> new file mode 100644
> index 0000000..791fb4d
> --- /dev/null
> +++ b/drivers/video/display/mipi-dbi-bus.c
> @@ -0,0 +1,234 @@
> +/*
> + * MIPI DBI Bus
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + *
> + * Contacts: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/export.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <video/mipi-dbi-bus.h>
> +
> +/* -----------------------------------------------------------------------------
> + * Bus operations
> + */
> +
> +int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width)
> +{
> +       if (width != 8 && width != 16)
> +               return -EINVAL;
> +
> +       dev->data_width = width;
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_set_data_width);
> +
> +int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd)
> +{
> +       return dev->bus->ops->write_command(dev->bus, dev, cmd);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_write_command);
> +
> +int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
> +                       size_t len)
> +{
> +       return dev->bus->ops->write_data(dev->bus, dev, data, len);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_write_data);
> +
> +int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len)
> +{
> +       return dev->bus->ops->read_data(dev->bus, dev, data, len);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_read_data);
> +
> +/* -----------------------------------------------------------------------------
> + * Bus type
> + */
> +
> +static const struct mipi_dbi_device_id *
> +mipi_dbi_match_id(const struct mipi_dbi_device_id *id,
> +                 struct mipi_dbi_device *dev)
> +{
> +       while (id->name[0]) {
> +               if (strcmp(dev->name, id->name) == 0) {
> +                       dev->id_entry = id;
> +                       return id;
> +               }
> +               id++;
> +       }
> +       return NULL;
> +}
> +
> +static int mipi_dbi_match(struct device *_dev, struct device_driver *_drv)
> +{
> +       struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
> +       struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_drv);
> +
> +       if (drv->id_table)
> +               return mipi_dbi_match_id(drv->id_table, dev) != NULL;
> +
> +       return (strcmp(dev->name, _drv->name) == 0);
> +}
> +
> +static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
> +                            char *buf)
> +{
> +       struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
> +       int len = snprintf(buf, PAGE_SIZE, MIPI_DBI_MODULE_PREFIX "%s\n",
> +                          dev->name);
> +
> +       return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
> +}
> +
> +static struct device_attribute mipi_dbi_dev_attrs[] = {
> +       __ATTR_RO(modalias),
> +       __ATTR_NULL,
> +};
> +
> +static int mipi_dbi_uevent(struct device *_dev, struct kobj_uevent_env *env)
> +{
> +       struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
> +
> +       add_uevent_var(env, "MODALIAS=%s%s", MIPI_DBI_MODULE_PREFIX,
> +                      dev->name);
> +       return 0;
> +}
> +
> +static const struct dev_pm_ops mipi_dbi_dev_pm_ops = {
> +       .runtime_suspend = pm_generic_runtime_suspend,
> +       .runtime_resume = pm_generic_runtime_resume,
> +       .suspend = pm_generic_suspend,
> +       .resume = pm_generic_resume,
> +       .freeze = pm_generic_freeze,
> +       .thaw = pm_generic_thaw,
> +       .poweroff = pm_generic_poweroff,
> +       .restore = pm_generic_restore,
> +};
> +
> +static struct bus_type mipi_dbi_bus_type = {
> +       .name           = "mipi-dbi",
> +       .dev_attrs      = mipi_dbi_dev_attrs,
> +       .match          = mipi_dbi_match,
> +       .uevent         = mipi_dbi_uevent,
> +       .pm             = &mipi_dbi_dev_pm_ops,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Device and driver (un)registration
> + */
> +
> +/**
> + * mipi_dbi_device_register - register a DBI device
> + * @dev: DBI device we're registering
> + */
> +int mipi_dbi_device_register(struct mipi_dbi_device *dev,
> +                             struct mipi_dbi_bus *bus)
> +{
> +       device_initialize(&dev->dev);
> +
> +       dev->bus = bus;
> +       dev->dev.bus = &mipi_dbi_bus_type;
> +       dev->dev.parent = bus->dev;
> +
> +       if (dev->id != -1)
> +               dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
> +       else
> +               dev_set_name(&dev->dev, "%s", dev->name);
> +
> +       return device_add(&dev->dev);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_device_register);
> +
> +/**
> + * mipi_dbi_device_unregister - unregister a DBI device
> + * @dev: DBI device we're unregistering
> + */
> +void mipi_dbi_device_unregister(struct mipi_dbi_device *dev)
> +{
> +       device_del(&dev->dev);
> +       put_device(&dev->dev);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_device_unregister);
> +
> +static int mipi_dbi_drv_probe(struct device *_dev)
> +{
> +       struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
> +       struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
> +
> +       return drv->probe(dev);
> +}
> +
> +static int mipi_dbi_drv_remove(struct device *_dev)
> +{
> +       struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
> +       struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
> +       int ret;
> +
> +       ret = drv->remove(dev);
> +       if (ret < 0)
> +               return ret;
> +
> +       mipi_dbi_set_drvdata(dev, NULL);
> +
> +       return 0;
> +}
> +
> +/**
> + * mipi_dbi_driver_register - register a driver for DBI devices
> + * @drv: DBI driver structure
> + */
> +int mipi_dbi_driver_register(struct mipi_dbi_driver *drv)
> +{
> +       drv->driver.bus = &mipi_dbi_bus_type;
> +       if (drv->probe)
> +               drv->driver.probe = mipi_dbi_drv_probe;
> +       if (drv->remove)
> +               drv->driver.remove = mipi_dbi_drv_remove;
> +
> +       return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_driver_register);
> +
> +/**
> + * mipi_dbi_driver_unregister - unregister a driver for DBI devices
> + * @drv: DBI driver structure
> + */
> +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv)
> +{
> +       driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);
> +
> +/* -----------------------------------------------------------------------------
> + * Init/exit
> + */
> +
> +static int __init mipi_dbi_init(void)
> +{
> +       return bus_register(&mipi_dbi_bus_type);
> +}
> +
> +static void __exit mipi_dbi_exit(void)
> +{
> +       bus_unregister(&mipi_dbi_bus_type);
> +}
> +
> +module_init(mipi_dbi_init);
> +module_exit(mipi_dbi_exit)
> +
> +MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart at ideasonboard.com>");
> +MODULE_DESCRIPTION("MIPI DBI Bus");
> +MODULE_LICENSE("GPL");
> diff --git a/include/video/display.h b/include/video/display.h
> index ba319d6..3138401 100644
> --- a/include/video/display.h
> +++ b/include/video/display.h
> @@ -17,6 +17,7 @@
>  #include <linux/list.h>
>  #include <linux/module.h>
>  #include <media/media-entity.h>
> +#include <video/mipi-dbi-bus.h>
>
>  #define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
>         (((option) << 17) | ((type) << 13) | ((variant) << 10) | \
> @@ -189,6 +190,9 @@ enum display_entity_interface_type {
>
>  struct display_entity_interface_params {
>         enum display_entity_interface_type type;
> +       union {
> +               struct mipi_dbi_interface_params dbi;
> +       } p;
>  };
>
>  struct display_entity_control_ops {
> diff --git a/include/video/mipi-dbi-bus.h b/include/video/mipi-dbi-bus.h
> new file mode 100644
> index 0000000..876b69d
> --- /dev/null
> +++ b/include/video/mipi-dbi-bus.h
> @@ -0,0 +1,125 @@
> +/*
> + * MIPI DBI Bus
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + *
> + * Contacts: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> + *
> + * 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.
> + */
> +
> +#ifndef __MIPI_DBI_BUS_H__
> +#define __MIPI_DBI_BUS_H__
> +
> +#include <linux/device.h>
> +
> +struct mipi_dbi_bus;
> +struct mipi_dbi_device;
> +
> +struct mipi_dbi_bus_ops {
> +       int (*write_command)(struct mipi_dbi_bus *bus,
> +                            struct mipi_dbi_device *dev, u16 cmd);
> +       int (*write_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
> +                         const u8 *data, size_t len);
> +       int (*read_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
> +                        u8 *data, size_t len);
> +};
> +
> +struct mipi_dbi_bus {
> +       struct device *dev;
> +       const struct mipi_dbi_bus_ops *ops;
> +};
> +
> +#define MIPI_DBI_MODULE_PREFIX         "mipi-dbi:"
> +#define MIPI_DBI_NAME_SIZE             32
> +
> +struct mipi_dbi_device_id {
> +       char name[MIPI_DBI_NAME_SIZE];
> +       __kernel_ulong_t driver_data    /* Data private to the driver */
> +                       __aligned(sizeof(__kernel_ulong_t));
> +};
> +
> +enum mipi_dbi_interface_type {
> +       MIPI_DBI_INTERFACE_TYPE_A,
> +       MIPI_DBI_INTERFACE_TYPE_B,
> +};
> +
> +#define MIPI_DBI_INTERFACE_TE          (1 << 0)
> +
> +struct mipi_dbi_interface_params {
> +       enum mipi_dbi_interface_type type;
> +       unsigned int flags;
> +
> +       unsigned int cs_setup;
> +       unsigned int rd_setup;
> +       unsigned int rd_latch;
> +       unsigned int rd_cycle;
> +       unsigned int rd_hold;
> +       unsigned int wr_setup;
> +       unsigned int wr_cycle;
> +       unsigned int wr_hold;
> +};
> +
> +#define MIPI_DBI_FLAG_ALIGN_LEFT       (1 << 0)
> +
> +struct mipi_dbi_device {
> +       const char *name;
> +       int id;
> +       struct device dev;

just fwiw, we need the "framework" to be agnostic of the association
between devices and entities in the framework.  Some things that are
multiple entities may actually map to a single device in hw (and
possibly visa versa?).  Otherwise we end up having to create fake
devices in some cases.  Really the entities, or objects, or whatever
you call 'em should just extend some kref'd base class.  Sort of like
how existing kms objects extend drm_mode_object.

So any 'struct device' moves down into the derived class, not in the
base class.  Configuration data is passed in separate, not grabbed out
of dev->platform_data, etc.  Probably there is room for some helpers
to pull stuff out of DT/ACPI/whatever.

BR,
-R

> +       const struct mipi_dbi_device_id *id_entry;
> +       struct mipi_dbi_bus *bus;
> +
> +       unsigned int flags;
> +       unsigned int bus_width;
> +       unsigned int data_width;
> +};
> +
> +#define to_mipi_dbi_device(d)  container_of(d, struct mipi_dbi_device, dev)
> +
> +int mipi_dbi_device_register(struct mipi_dbi_device *dev,
> +                            struct mipi_dbi_bus *bus);
> +void mipi_dbi_device_unregister(struct mipi_dbi_device *dev);
> +
> +struct mipi_dbi_driver {
> +       int(*probe)(struct mipi_dbi_device *);
> +       int(*remove)(struct mipi_dbi_device *);
> +       struct device_driver driver;
> +       const struct mipi_dbi_device_id *id_table;
> +};
> +
> +#define to_mipi_dbi_driver(d)  container_of(d, struct mipi_dbi_driver, driver)
> +
> +int mipi_dbi_driver_register(struct mipi_dbi_driver *drv);
> +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv);
> +
> +static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dev)
> +{
> +       return dev_get_drvdata(&dev->dev);
> +}
> +
> +static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dev,
> +                                       void *data)
> +{
> +       dev_set_drvdata(&dev->dev, data);
> +}
> +
> +/* module_mipi_dbi_driver() - Helper macro for drivers that don't do
> + * anything special in module init/exit.  This eliminates a lot of
> + * boilerplate.  Each module may only use this macro once, and
> + * calling it replaces module_init() and module_exit()
> + */
> +#define module_mipi_dbi_driver(__mipi_dbi_driver) \
> +       module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \
> +                       mipi_dbi_driver_unregister)
> +
> +int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width);
> +
> +int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd);
> +int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len);
> +int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
> +                       size_t len);
> +
> +#endif /* __MIPI_DBI_BUS__ */
> --
> 1.8.1.5
>


More information about the dri-devel mailing list