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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Laurent,

On 9 August 2013 22:44, Laurent Pinchart
<laurent.pinchart+renesas@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxxxx>
> + *
> + * 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);
> +}



The function looks very much specific to NON-DT case where you will be
calling mipi_dbi_device_register() in the machine file.

I was actually trying to migrate to CDFv3 and adding MIPI DSI support
for exynos5250,
but in my case where exynos5250 is fully DT based, in which case we
need something like ./drivers/of/platform.c for MIPI DBI and MIPI DSI
to add the MIPI DBI/DSI device via DT way, ./drivers/of/mipi_dbi.c and
./drivers/of/mipi_dsi.c

may look like below,

int of_mipi_dbi_device_register(struct device_node *np,
                                         const char *bus_id,
                                         struct device *parent)
{

         struct mipi_dbi_device *dev;
         dev = of_device_alloc(np, bus_id, parent);
         if (!dev)
                 return NULL;
       device_initialize(dev);

       dev->bus = &mipi_dbi_bus_type;
       dev->parent = parent;

       return of_device_add(dev);
}

Correct me if I am wrong.



> +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);


Here we are assuming that  mipi_dbi_device can be obtained by using
_dev pointer, which may NOT be true in DT case, i think.

let me know if i am missing something.

if you can give me a example for DT case, that would be helpful.


> +
> +       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@xxxxxxxxxxxxxxxx>");
> +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@xxxxxxxxxxxxxxxx>
> + *
> + * 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;
> +
> +       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
>



-- 
Thanks and Regards
 Vikas Sajjan
_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/dri-devel




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux