[RFC v2 PATCH] mipi-dsi-bus: add MIPI DSI bus support
Andrzej Hajda
a.hajda at samsung.com
Mon Nov 25 07:05:41 PST 2013
Hi Thierry,
Thanks for the review.
On 11/22/2013 06:41 PM, Thierry Reding wrote:
> On Mon, Nov 18, 2013 at 05:25:23PM +0100, Andrzej Hajda wrote:
>> MIPI DSI bus allows to model DSI hosts
>> and DSI devices using Linux bus.
> This looks somewhat terse. Any chance you could be more verbose about
> what exactly this does? You could mention for instance that it allows
> DSI devices to be instantiated from device tree and manually. That a
> Linux bus type is provided and device drivers can use that to bind to
> DSI devices.
OK.
>
>> Signed-off-by: Andrzej Hajda <a.hajda at samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
>> ---
>> Hi,
>>
>> This is my implementation of mipi dsi bus.
>> The first time it was posted as a part of CDF infrastructure [1],
>> but if it can be merged independently I will be glad.
>>
>> I have not addressed yet Bert's comments, but I think it should
>> be easy, once we have agreement how to implement it.
>>
>> There are also working drivers which uses this bus:
>> - Exynos DSI master,
>> - s6e8aa0 panel.
>> I will post them later.
>>
>> [1] http://www.mail-archive.com/dri-devel@lists.freedesktop.org/msg45252.html
>>
>> Changes:
>> v2:
>> - set_power callback removed (its role is passed to RUNTIME_PM),
>> - changed transfer ops parameters to struct,
>> - changed source location,
>> - minor fixes
>>
>> Regards
>> Andrzej
>>
>> ---
>> drivers/gpu/drm/Kconfig | 4 +
>> drivers/gpu/drm/Makefile | 2 +
>> drivers/gpu/drm/drm_mipi_dsi.c | 344 +++++++++++++++++++++++++++++++++++++++++
>> include/drm/drm_mipi_dsi.h | 154 ++++++++++++++++++
>> 4 files changed, 504 insertions(+)
>> create mode 100644 drivers/gpu/drm/drm_mipi_dsi.c
>> create mode 100644 include/drm/drm_mipi_dsi.h
> This seems to be missing a device tree binding document. That could
> probably be rather small since there's not a lot there right now. I
> volunteer to draft that document if you don't have the time to do
> that.
>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index f864275..58a603d 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -20,6 +20,10 @@ menuconfig DRM
>> details. You should also select and configure AGP
>> (/dev/agpgart) support if it is available for your platform.
>>
>> +config DRM_MIPI_DSI
>> + tristate
>> + default y
> Shouldn't this remain off by default? And only be selected by drivers
> that actually need it. I think that DSI host drivers could select this
> symbol and DSI peripheral drivers can depend on it. That way you'll
> automatically be able to only select peripheral drivers if there's at
> least one DSI host driver enabled.
Yes, of course. I just forgot to set it back properly after last tests.
>
>> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> [...]
>> +/*
>> + * MIPI DSI Bus
>> + *
>> + * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
> Perhaps this should now be "2012-2013"?
Yes, thanks.
>
>> + * Andrzej Hajda <a.hajda at samsung.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/of_device.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/slab.h>
>> +#include <linux/pm.h>
>> +#include <linux/pm_runtime.h>
> Can these please be ordered alphabetically?
OK
>
>> +/* -----------------------------------------------------------------------------
>> + * Bus operations
>> + */
> Nitpick: I'm not a huge fan of these separators. They have a tendency to
> get in the way when people add new functions and then all of a sudden
> they no longer fit into that category...
>
>> +int mipi_dsi_init(struct mipi_dsi_device *dev)
>> +{
> [...]
>> +}
>> +EXPORT_SYMBOL_GPL(mipi_dsi_init);
>> +
>> +int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on)
>> +{
> [...]
>> +}
>> +EXPORT_SYMBOL_GPL(mipi_dsi_set_stream);
> These are missing documentation. It's not clear at all what they are
> supposed to do.
>
>> +int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
> unsigned int for channel, please. And const void * for data so that the
> same type is used consistently.
>
>> +static struct device_attribute mipi_dsi_dev_attrs[] = {
> static const?
>
> I also believe these now need to be done using attribute groups. Look at
> commit d06262e58546 (driver-core: platform: convert bus code to use
> dev_groups) for an example of how to do that.
>> +static const struct dev_pm_ops mipi_dsi_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_dsi_bus_type = {
>> + .name = "mipi-dsi",
>> + .dev_attrs = mipi_dsi_dev_attrs,
>> + .match = mipi_dsi_match,
>> + .uevent = mipi_dsi_uevent,
>> + .pm = &mipi_dsi_dev_pm_ops,
>> +};
> Can we please stick to one style for these structures? I personally
> prefer the one you used for mipi_dsi_dev_pm_ops because the other one
> falls apart when you need to initialize a new field that exceeds the
> size of the indentation that you've chosen. It also wastes screen space
> and doesn't really make the code more readable in my opinion.
OK, for all above.
>
>> +void mipi_dsi_dev_release(struct device *_dev)
>> +{
>> + struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
>> +
>> + kfree(dev);
>> +}
>> +
>> +static struct device_type mipi_dsi_dev_type = {
>> + .release = mipi_dsi_dev_release,
>> +};
> Also, is there any reason why you've switched to the mipi_dsi_dev prefix
> instead of mipi_dsi_device all of a sudden?
Ups, will be fixed.
>
>> +/**
>> + * mipi_dsi_device_register - register a DSI device
>> + * @dev: DSI device we're registering
>> + */
>> +int mipi_dsi_device_register(struct mipi_dsi_device *dev,
>> + struct mipi_dsi_bus *bus)
>> +{
>> + device_initialize(&dev->dev);
>> +
>> + dev->bus = bus;
>> + dev->dev.bus = &mipi_dsi_bus_type;
>> + dev->dev.parent = bus->dev;
>> + dev->dev.type = &mipi_dsi_dev_type;
>> +
>> + if (dev->id != -1)
> Does that case actually make sense in the context of DSI? There's a
> defined hierarchy in DSI, so shouldn't we construct the names in a more
> hierarchical way? I'm not sure if the ID is useful at all, perhaps it
> should really be the virtual channel?
>
> The patch that I proposed used the following to make up the device name:
>
> dev_set_name(&dsi->dev, "%s.%u", dev_name(host->dev), dsi->channel);
>
> That has the advantage of reflecting the bus topology.
>
> It seems like your code was copied mostly from platform_device, for
> which there's no well-defined topology and therefore having an ID makes
> sense to differentiate between multiple instances of the same device.
> But for DSI any host/bus can only have a single device with a given
> channel, so that makes for a pretty good for unique name.
Code was based on mipi_dbi_bus.c from CDF.
I am not sure about hardwiring devices to virtual channels.
There could be devices which uses more than one virtual channel,
in fact exynos-drm docs suggests that such hardware exists.
I can also imagine scenarios when dynamic virtual channel (re-)association
can be useful and DSI specs are not against it AFAIK.
Anyway the whole 'multiple devs on one DSI master' thing seems to me
quite unspecified.
>
>> +/**
>> + * mipi_dsi_device_unregister - unregister a DSI device
>> + * @dev: DSI device we're unregistering
>> + */
>> +void mipi_dsi_device_unregister(struct mipi_dsi_device *dev)
>> +{
>> + device_del(&dev->dev);
>> + put_device(&dev->dev);
> That's actually device_unregister().
>
>> +}
>> +EXPORT_SYMBOL_GPL(mipi_dsi_device_unregister);
> In general DRM doesn't use GPL-only symbols in order to make it easier
> to port the code to other operating systems.
>
>> +struct mipi_dsi_device *of_mipi_dsi_register_device(struct mipi_dsi_bus *bus,
>> + struct device_node *node)
>> +{
>> + struct mipi_dsi_device *d = NULL;
> Perhaps "dev" instead of "d", as the former is used everywhere else.
>
>> + int ret;
>> +
>> + d = kzalloc(sizeof(*d), GFP_KERNEL);
>> + if (!d)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + ret = of_modalias_node(node, d->name, sizeof(d->name));
>> + if (ret) {
>> + dev_err(bus->dev, "modalias failure on %s\n", node->full_name);
>> + goto err_free;
>> + }
>> +
>> + d->dev.of_node = of_node_get(node);
>> + if (!d->dev.of_node)
>> + goto err_free;
>> +
>> + ret = mipi_dsi_device_register(d, bus);
>> +
>> + if (!ret)
>> + return d;
>> +
>> + of_node_put(node);
>> +err_free:
>> + kfree(d);
>> +
>> + return ERR_PTR(ret);
>> +}
> The cleanup looks somewhat weird here. I think the more canonical form
> would be:
>
> ret = mipi_dsi_device_register(dev, bus);
> if (ret)
> goto err_put;
>
> return dev;
>
> err_put:
> of_node_put(node);
> err_free:
> kfree(dev);
>
> return ERR_PTR(ret);
OK for all above
>
> Also I think we'll need to have a reg property and parse that to allow a
> device tree node to be matched to a virtual channel.
reg property means device is at fixed virtual channel.
DSI specs says nothing about it IMHO.
>> +static int __init mipi_dsi_bus_init(void)
>> +{
>> + return bus_register(&mipi_dsi_bus_type);
>> +}
>> +
>> +static void __exit mipi_dsi_bus_exit(void)
>> +{
>> + bus_unregister(&mipi_dsi_bus_type);
>> +}
>> +
>> +module_init(mipi_dsi_bus_init);
>> +module_exit(mipi_dsi_bus_exit)
> I don't think module_init() will work here. The reason is that the probe
> order for built-in drivers is determined primarily by link-order, so if
> any DSI device drivers end up linked in earlier than the DSI bus
> infrastructure, then the bus won't have been initialized yet when that
> driver is probed and registering a device or bus with it will fail.
>
>> +MODULE_LICENSE("GPL v2");
> I think this needs to be "GPL and additional rights" to be used within
> DRM.
>
>> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> [...]
>> +#ifndef __DRM_MIPI_DSI_H__
>> +#define __DRM_MIPI_DSI_H__
>> +
>> +#include <linux/device.h>
>> +#include <video/videomode.h>
>> +
>> +struct mipi_dsi_bus;
>> +struct mipi_dsi_device;
>> +
>> +struct mipi_dsi_msg {
>> + u8 channel;
>> + u8 type;
>> +
>> + size_t tx_len;
>> + const void *tx_buf;
>> +
>> + size_t rx_len;
>> + void *rx_buf;
>> +};
>> +
>> +struct mipi_dsi_bus_ops {
>> + int (*init)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev);
>> + int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
>> + bool on);
> Again, these should really be documented. I can somewhat guess what
> .init() is supposed to do. But I have no idea what .set_stream() is,
> though I think I've seen that in CDF patches. Perhaps that's a relic
> from when this was based on CDF?
OK, beside documenting I will add drivers for dsi and panel in the next
iteration
to show it in use.
>
>> + ssize_t (*transfer)(struct mipi_dsi_bus *bus,
>> + struct mipi_dsi_device *dev,
>> + struct mipi_dsi_msg *msg);
> Why do we need the dev parameter? There's already a channel field in the
> mipi_dsi_msg structure. Isn't that all that the transfer function needs
> to know about the device?
For simple HSI transfers, yes. In case transfer would depend on dev state
it would be useful, but I have no use case yet, so it could be removed.
>
>> +#define DSI_MODE_VIDEO (1 << 0)
>> +#define DSI_MODE_VIDEO_BURST (1 << 1)
>> +#define DSI_MODE_VIDEO_SYNC_PULSE (1 << 2)
>> +#define DSI_MODE_VIDEO_AUTO_VERT (1 << 3)
>> +#define DSI_MODE_VIDEO_HSE (1 << 4)
>> +#define DSI_MODE_VIDEO_HFP (1 << 5)
>> +#define DSI_MODE_VIDEO_HBP (1 << 6)
>> +#define DSI_MODE_VIDEO_HSA (1 << 7)
>> +#define DSI_MODE_VSYNC_FLUSH (1 << 8)
>> +#define DSI_MODE_EOT_PACKET (1 << 9)
> It should be documented how these are used.
OK
>
>> +enum mipi_dsi_pixel_format {
>> + DSI_FMT_RGB888,
>> + DSI_FMT_RGB666,
>> + DSI_FMT_RGB666_PACKED,
>> + DSI_FMT_RGB565,
>> +};
>> +
>> +struct mipi_dsi_interface_params {
>> + enum mipi_dsi_pixel_format format;
>> + unsigned long mode;
> I assume this is a bitmask of DSI_MODE_*. In that case perhaps "modes"
> would be a more accurate name?
Yes
>
>> + unsigned long hs_clk_freq;
>> + unsigned long esc_clk_freq;
> How are these interface parameters? The frequencies can certainly vary
> depending on the display mode, so why would a device specify that as a
> parameter?
>
>> + unsigned char data_lanes;
>> + unsigned char cmd_allow;
> What's cmd_allow?
For sure it requires documentation:)
In this particular case it is a number of lines where DSI-master allows
to send commands in video mode.
>
>> +};
> Any reason these parameters cannot be moved to the mipi_dsi_device
> structure?
I agree, it is again heritage of CDF.
>
>> +struct mipi_dsi_bus {
> I'd prefer this to be called mipi_dsi_host, because that's the name used
> everywhere in the specification.
>
>> + struct device *dev;
>> + const struct mipi_dsi_bus_ops *ops;
>> +};
>> +
>> +#define MIPI_DSI_MODULE_PREFIX "mipi-dsi:"
>> +#define MIPI_DSI_NAME_SIZE 32
> Do we really need this? Can't we allocate the name dynamically...
>
>> +struct mipi_dsi_device_id {
>> + char name[MIPI_DSI_NAME_SIZE];
> ... or use const char * here?
>
>> + __kernel_ulong_t driver_data /* Data private to the driver */
>> + __aligned(sizeof(__kernel_ulong_t));
> I don't think the explicit aligned attribute is necessary here.
>
>> +};
>> +
>> +struct mipi_dsi_device {
>> + char name[MIPI_DSI_NAME_SIZE];
>> + int id;
>> + struct device dev;
>> +
>> + const struct mipi_dsi_device_id *id_entry;
>> + struct mipi_dsi_bus *bus;
>> + struct videomode vm;
> Why is the videomode part of this structure? What if a device supports
> more than a single mode?
This is the current video mode. If we want to change mode,
we call:
ops->set_stream(false);
dev->vm =...new mode
ops->set_stream(true);
>
>> +int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
> Perhaps there ought to be a dummy implementation for this in case OF
> isn't selected?
>
> In fact, I think we should be hiding all those details from users. They
> shouldn't bother about manually registering with OF. Instead this should
> be mipi_dsi_register_bus/host()...
OK
>
>> +void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
> ... and this mipi_dsi_unregister_bus/host(). That way registration with
> OF (and instantiation of devices from DT) can be done automatically as
> needed.
>
>> +
>> +/* module_mipi_dsi_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()
>> + */
> The correct style for block comments for kernel-doc is:
>
> /**
> * module_mipi_dsi_driver() - ...
> * ...
> */
>
>> +#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
>> +({\
>> + const u8 d[] = { seq };\
>> + BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for stack");\
>> + mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
>> +
>> +#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
>> +({\
>> + static const u8 d[] = { seq };\
>> + mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
> Can we drop these please? I'd rather have drivers construct buffers
> explicitly than use these.
I like those two. It removes lots of boiler-plate code. Please compare
implementations
of s6e8aa panel drivers without it [1] and with it [2], look for
calls to dsi_dcs_write.
[1] http://lists.freedesktop.org/archives/dri-devel/2013-January/034212.html
[2] https://linuxtv.org/patch/20215/
Andrzej
>
> Thierry
More information about the dri-devel
mailing list