From 158322e3f55b614c20e186111fdf23a47e375931 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Sat, 8 Feb 2020 21:51:02 +0100 Subject: [PATCH 2/2] drm: add MIPI DBI bus infrastructure Add infrastructure to register MIPI DBI display drivers. The code is more or less a straight copy from drm_mipi_dsi with adaptions like host => display etc. Signed-off-by: Sam Ravnborg --- drivers/gpu/drm/drm_mipi_dbi.c | 247 +++++++++++++++++++++++++++++++++ include/drm/drm_mipi_dbi.h | 135 ++++++++++++++++++ 2 files changed, 382 insertions(+) diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 558baf989f5a..8341499e303f 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -1327,4 +1329,249 @@ EXPORT_SYMBOL(mipi_dbi_debugfs_init); #endif +static int mipi_dbi_device_match(struct device *dev, struct device_driver *drv) +{ + struct mipi_dbi_dev *dbi = to_mipi_dbi_dev(dev); + + /* attempt OF style match */ + if (of_driver_match_device(dev, drv)) + return 1; + + /* compare DBI device and driver names */ + if (!strcmp(dbi->name, drv->name)) + return 1; + + return 0; +} + +static int mipi_dbi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mipi_dbi_dev *dbi = to_mipi_dbi_dev(dev); + int err; + + err = of_device_uevent_modalias(dev, env); + if (err != -ENODEV) + return err; + + add_uevent_var(env, "MODALIAS=%s%s", MIPI_DBI_MODULE_PREFIX, + dbi->name); + + return 0; +} + +static const struct dev_pm_ops mipi_dbi_device_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", + .match = mipi_dbi_device_match, + .uevent = mipi_dbi_uevent, + .pm = &mipi_dbi_device_pm_ops, +}; + +static void mipi_dbi_dev_release(struct device *dev) +{ + struct mipi_dbi_dev *dbi = to_mipi_dbi_dev(dev); + + of_node_put(dev->of_node); + kfree(dbi); +} + +static const struct device_type mipi_dbi_device_type = { + .release = mipi_dbi_dev_release, +}; + +static struct mipi_dbi_dev *mipi_dbi_dev_alloc(struct mipi_dbi_display *display) +{ + struct mipi_dbi_dev *dbi; + + dbi = kzalloc(sizeof(*dbi), GFP_KERNEL); + if (!dbi) + return ERR_PTR(-ENOMEM); + + dbi->display = display; + dbi->dev.bus = &mipi_dbi_bus_type; + dbi->dev.parent = display->dev; + dbi->dev.type = &mipi_dbi_device_type; + + device_initialize(&dbi->dev); + + return dbi; +} + +static int mipi_dbi_dev_add(struct mipi_dbi_dev *dbi) +{ + struct mipi_dbi_display *display = dbi->display; + + dev_set_name(&dbi->dev, "%s.%d", dev_name(display->dev), dbi->index); + + return device_add(&dbi->dev); +} + +#if IS_ENABLED(CONFIG_OF) +static struct mipi_dbi_dev * +of_mipi_dbi_dev_add(struct mipi_dbi_display *display, struct device_node *node) +{ + struct device *dev = display->dev; + struct mipi_dbi_dev_info info = { }; + int ret; + u32 reg; + + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { + dev_err(dev, "modalias failure on %pOF\n", node); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32(node, "reg", ®); + if (ret) { + dev_err(dev, "device node %pOF has no valid reg property: %d\n", + node, ret); + return ERR_PTR(-EINVAL); + } + + info.index = reg; + info.node = of_node_get(node); + + return mipi_dbi_dev_register_full(display, &info); +} +#else +static struct mipi_dbi_device * +of_mipi_dbi_dev_add(struct mipi_dbi_display *display, struct device_node *node) +{ + return ERR_PTR(-ENODEV); +} +#endif + +/** + * mipi_dbi_device_register_full - create a MIPI DBI device + * @host: DBI display to which this device is connected + * @info: pointer to template containing DBI device information + * + * Create a MIPI DBI device by using the device information provided by + * mipi_dbi_dev_info template + * + * Returns: + * A pointer to the newly created MIPI DBI device, or a pointer encoded + * with an error + */ +struct mipi_dbi_dev * +mipi_dbi_dev_register_full(struct mipi_dbi_display *display, + const struct mipi_dbi_dev_info *info) +{ + struct mipi_dbi_dev *dbi; + struct device *dev = display->dev; + int ret; + + if (!info) { + dev_err(dev, "invalid mipi_dbi_device_info pointer\n"); + return ERR_PTR(-EINVAL); + } + + if (info->index > 7) { + dev_err(dev, "invalid index: %u\n", info->index); + return ERR_PTR(-EINVAL); + } + + dbi = mipi_dbi_dev_alloc(display); + if (IS_ERR(dbi)) { + dev_err(dev, "failed to allocate DBI device %ld\n", + PTR_ERR(dbi)); + return dbi; + } + + dbi->dev.of_node = info->node; + dbi->index = info->index; + strlcpy(dbi->name, info->type, sizeof(dbi->name)); + + ret = mipi_dbi_dev_add(dbi); + if (ret) { + dev_err(dev, "failed to add DBI device %d\n", ret); + kfree(dbi); + return ERR_PTR(ret); + } + + return dbi; +} +EXPORT_SYMBOL(mipi_dbi_dev_register_full); + +static DEFINE_MUTEX(display_lock); +static LIST_HEAD(display_list); + +/** + * of_find_mipi_dbi_display_by_node() - find the MIPI DBI display + * matching a device tree node + * @node: device tree node + * + * Returns: + * A pointer to the MIPI DBI display corresponding to @node or NULL if no + * such device exists (or has not been registered yet). + */ +struct mipi_dbi_display * +of_find_mipi_dbi_display_by_node(struct device_node *node) +{ + struct mipi_dbi_display *display; + + mutex_lock(&display_lock); + + list_for_each_entry(display, &display_list, list) { + if (display->dev->of_node == node) { + mutex_unlock(&display_lock); + return display; + } + } + + mutex_unlock(&display_lock); + + return NULL; +} +EXPORT_SYMBOL(of_find_mipi_dbi_display_by_node); + + +int mipi_dbi_displays_register(struct mipi_dbi_display *display) +{ + struct device_node *node; + + for_each_available_child_of_node(display->dev->of_node, node) { + /* skip nodes without reg property */ + if (!of_find_property(node, "reg", NULL)) + continue; + of_mipi_dbi_dev_add(display, node); + } + + mutex_lock(&display_lock); + list_add_tail(&display->list, &display_list); + mutex_unlock(&display_lock); + + return 0; +} +EXPORT_SYMBOL(mipi_dbi_displays_register); + +/** + * mipi_dbi_driver_unregister() - unregister a driver for DBI devices + * @drv: DBI driver structure + * + * Return: 0 on success or a negative error code on failure. + */ +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(mipi_dbi_driver_unregister); + +static int __init mipi_dbi_bus_init(void) +{ + return bus_register(&mipi_dbi_bus_type); +} +postcore_initcall(mipi_dbi_bus_init); + +MODULE_AUTHOR("Sam Ravnborg "); +MODULE_DESCRIPTION("MIPI DPI Bus"); MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 33f325f5af2b..752c3ef273ab 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -17,6 +17,9 @@ struct spi_device; struct gpio_desc; struct regulator; +#define DBI_DEV_NAME_SIZE 20 +#define MIPI_DBI_MODULE_PREFIX "mipi-dbi:" + /** * struct mipi_dbi - MIPI DBI interface */ @@ -70,6 +73,27 @@ struct mipi_dbi { size_t tx_buf9_len; }; +/** + * struct mipi_dbi_dev_info - template for creating a mipi_dbi_dev + * + * This is populated and passed to mipi_dbi_dev_alloc to create a new + * DBI device + */ +struct mipi_dbi_dev_info { + /** + * @type: DBI type + */ + char type[DBI_DEV_NAME_SIZE]; + /** + * @index: The display number on the bus + */ + u32 index; + /** + * @node: pointer to OF device node + */ + struct device_node *node; +}; + /** * struct mipi_dbi_dev - MIPI DBI device */ @@ -79,6 +103,26 @@ struct mipi_dbi_dev { */ struct drm_device drm; + /** + * @dev: device + */ + struct device dev; + + /** + * @name: The name of the DBI device + */ + char name[DBI_DEV_NAME_SIZE]; + + /** + * display: DBI display + */ + struct mipi_dbi_display *display; + + /** + * index: index of display + */ + u32 index; + /** * @pipe: Display pipe structure */ @@ -137,6 +181,11 @@ struct mipi_dbi_dev { struct mipi_dbi dbi; }; +static inline struct mipi_dbi_dev *to_mipi_dbi_dev(struct device *dev) +{ + return container_of(dev, struct mipi_dbi_dev, dev); +} + static inline struct mipi_dbi_dev *drm_to_mipi_dbi_dev(struct drm_device *drm) { return container_of(drm, struct mipi_dbi_dev, drm); @@ -173,6 +222,92 @@ int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, struct drm_rect *clip, bool swap); + +/** + * struct mipi_dbi_msg - read/write DBI buffer + * @index: index of display + * @type: payload data type + * @flags: flags controlling this message transmission + * @tx_len: length of @tx_buf + * @tx_buf: data to be written + * @rx_len: length of @rx_buf + * @rx_buf: data to be read, or NULL + */ +struct mipi_dbi_msg { + u8 index; + u8 type; + u16 flags; + + size_t tx_len; + const void *tx_buf; + + size_t rx_len; + void *rx_buf; +}; + +struct mipi_dbi_display; + +/** + * struct mipi_dbi_display_ops - DBI bus operations + * @attach: attach DBI device to DBI display + * @detach: detach DBI device from DBI display + * @transfer: transmit a DBI packet + * + * DBI packets transmitted by .transfer() are passed in as mipi_dbi_msg + * structures. This structure contains information about the type of packet + * being transmitted as well as the transmit and receive buffers. When an + * error is encountered during transmission, this function will return a + * negative error code. On success it shall return the number of bytes + * transmitted for write packets or the number of bytes received for read + * packets. + * + * Note that typically DBI packet transmission is atomic, so the .transfer() + * function will seldomly return anything other than the number of bytes + * contained in the transmit buffer on success. + */ +struct mipi_dbi_display_ops { + int (*attach)(struct mipi_dbi_display *display, + struct mipi_dbi_dev *dbi); + int (*detach)(struct mipi_dbi_display *display, + struct mipi_dbi_dev *dbi); + ssize_t (*transfer)(struct mipi_dbi_display *display, + const struct mipi_dbi_msg *msg); +}; + +/** + * struct mipi_dbi_display - DBI display device + * @dev: driver model device node for this DBI display + * @ops: DBI host operations + * @list: list management + */ +struct mipi_dbi_display { + struct device *dev; + const struct mipi_dbi_display_ops *ops; + struct list_head list; +}; + +/** + * struct mipi_dbi_driver - DBI driver + * @driver: device driver model driver + * @probe: callback for device binding + * @remove: callback for device unbinding + * @shutdown: called at shutdown time to quiesce the device + */ +struct mipi_dbi_driver { + struct device_driver driver; + int(*probe)(struct mipi_dbi_dev *dbi); + int(*remove)(struct mipi_dbi_dev *dbi); + void (*shutdown)(struct mipi_dbi_dev *dbi); +}; + +struct mipi_dbi_dev * +mipi_dbi_dev_register_full(struct mipi_dbi_display *display, + const struct mipi_dbi_dev_info *info); +struct mipi_dbi_display * +of_find_mipi_dbi_display_by_node(struct device_node *node); +int mipi_dbi_displays_register(struct mipi_dbi_display *display); +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv); + /** * mipi_dbi_command - MIPI DCS command with optional parameter(s) * @dbi: MIPI DBI structure -- 2.25.1