[PATCH v3 1/7] drm: Add DSI bus infrastructure
Thierry Reding
thierry.reding at gmail.com
Mon Nov 11 04:00:29 PST 2013
In order to support DSI peripherals, add a DSI bus type that devices and
drivers can be registered with.
Signed-off-by: Thierry Reding <treding at nvidia.com>
---
drivers/gpu/drm/Kconfig | 4 +
drivers/gpu/drm/Makefile | 2 +
drivers/gpu/drm/drm_dsi.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++
include/drm/drm_dsi.h | 206 +++++++++++++++++++++++++++++++
4 files changed, 518 insertions(+)
create mode 100644 drivers/gpu/drm/drm_dsi.c
create mode 100644 include/drm/drm_dsi.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f86427591167..7faefcdd6854 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_DSI
+ bool
+ depends on DRM
+
config DRM_USB
tristate
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index cc08b845f965..eef34abc1e45 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -19,6 +19,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
drm-$(CONFIG_PCI) += ati_pcigart.o
+drm-dsi-y := drm_dsi.o
drm-usb-y := drm_usb.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o
@@ -31,6 +32,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
CFLAGS_drm_trace_points.o := -I$(src)
obj-$(CONFIG_DRM) += drm.o
+obj-$(CONFIG_DRM_DSI) += drm_dsi.o
obj-$(CONFIG_DRM_USB) += drm_usb.o
obj-$(CONFIG_DRM_TTM) += ttm/
obj-$(CONFIG_DRM_TDFX) += tdfx/
diff --git a/drivers/gpu/drm/drm_dsi.c b/drivers/gpu/drm/drm_dsi.c
new file mode 100644
index 000000000000..bead3cc0e9e3
--- /dev/null
+++ b/drivers/gpu/drm/drm_dsi.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <drm/drm_dsi.h>
+
+static int dsi_device_match(struct device *dev, struct device_driver *drv)
+{
+ if (of_driver_match_device(dev, drv))
+ return 1;
+
+ return 0;
+}
+
+static struct bus_type dsi_bus_type = {
+ .name = "dsi",
+ .match = dsi_device_match,
+};
+
+static void dsi_device_release(struct device *dev)
+{
+ struct dsi_device *dsi = to_dsi_device(dev);
+
+ of_node_put(dsi->dev.of_node);
+ dsi_host_put(dsi->host);
+ kfree(dsi);
+}
+
+static struct dsi_device *dsi_device_alloc(struct dsi_host *host)
+{
+ struct dsi_device *dsi;
+
+ if (!dsi_host_get(host))
+ return ERR_PTR(-EINVAL);
+
+ dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
+ if (!dsi) {
+ dsi_host_put(host);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ dsi->host = dsi_host_get(host);
+
+ dsi->dev.parent = host->dev;
+ dsi->dev.bus = &dsi_bus_type;
+ dsi->dev.release = dsi_device_release;
+
+ device_initialize(&dsi->dev);
+
+ return dsi;
+}
+
+static int dsi_device_add(struct dsi_device *dsi)
+{
+ struct dsi_host *host = dsi->host;
+ int err;
+
+ dev_set_name(&dsi->dev, "%s.%u", dev_name(host->dev), dsi->channel);
+
+ err = device_add(&dsi->dev);
+ if (err < 0) {
+ dsi_device_put(dsi);
+ return err;
+ }
+
+ return 0;
+}
+
+static int of_dsi_host_register(struct dsi_host *host)
+{
+ if (!host->dev->of_node)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int of_dsi_register_devices(struct dsi_host *host)
+{
+ struct device_node *np;
+
+ if (!host->dev->of_node)
+ return -ENODEV;
+
+ for_each_available_child_of_node(host->dev->of_node, np) {
+ struct dsi_device *dsi;
+ u32 value;
+ int err;
+
+ dsi = dsi_device_alloc(host);
+ if (IS_ERR(dsi)) {
+ dev_err(host->dev,
+ "failed to allocate DSI device for %s: %ld\n",
+ np->full_name, PTR_ERR(dsi));
+ continue;
+ }
+
+ dsi->dev.of_node = of_node_get(np);
+
+ err = of_property_read_u32(np, "reg", &value);
+ if (err) {
+ dev_err(host->dev,
+ "device %s has no valid 'reg' property: %d\n",
+ np->full_name, err);
+ dsi_device_put(dsi);
+ continue;
+ }
+
+ if (value > 3) {
+ dev_err(host->dev,
+ "device %s has invalid virtual channel %u\n",
+ np->full_name, value);
+ dsi_device_put(dsi);
+ continue;
+ }
+
+ dsi->channel = value;
+
+ err = dsi_device_add(dsi);
+ if (err < 0) {
+ dev_err(host->dev,
+ "failed to add DSI device for %s: %d\n",
+ np->full_name, err);
+ dsi_device_put(dsi);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+int dsi_host_register(struct dsi_host *host)
+{
+ int err;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ err = of_dsi_host_register(host);
+ if (err < 0)
+ return err;
+ }
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ err = of_dsi_register_devices(host);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dsi_host_register);
+
+static int __dsi_device_unregister(struct device *dev, void *data)
+{
+ device_unregister(dev);
+ return 0;
+}
+
+/**
+ * dsi_host_unregister() - unregister a DSI host controller
+ * @host: a DSI host controller
+ *
+ * Returns 0 on success or a negative error-code on failure.
+ */
+int dsi_host_unregister(struct dsi_host *host)
+{
+ device_for_each_child(host->dev, NULL, __dsi_device_unregister);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsi_host_unregister);
+
+/**
+ * dsi_host_transfer() - transfer a DSI message between host and peripheral
+ * @host: DSI host
+ * @msg: DSI message to transfer
+ *
+ * Returns 0 on success or a negative error-code on failure.
+ */
+ssize_t dsi_host_transfer(struct dsi_host *host, struct dsi_msg *msg)
+{
+ if (host->ops && host->ops->transfer)
+ return host->ops->transfer(host, msg);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL(dsi_host_transfer);
+
+/**
+ * dsi_device_attach() - attach a DSI peripheral to its DSI host
+ * @device: DSI peripheral
+ *
+ * Returns 0 on success or a negative error-code on failure.
+ */
+int dsi_device_attach(struct dsi_device *device)
+{
+ if (device->host->ops && device->host->ops->attach)
+ return device->host->ops->attach(device->host, device);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL(dsi_device_attach);
+
+/**
+ * dsi_device_detach() - detach a DSI peripheral from its DSI host
+ * @device: DSI peripheral
+ *
+ * Returns 0 on success or a negative error-code on failure.
+ */
+int dsi_device_detach(struct dsi_device *device)
+{
+ if (device->host->ops && device->host->ops->detach)
+ return device->host->ops->detach(device->host, device);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL(dsi_device_detach);
+
+static int dsi_driver_probe(struct device *dev)
+{
+ const struct dsi_driver *drv = to_dsi_driver(dev->driver);
+ struct dsi_device *dsi = to_dsi_device(dev);
+
+ return drv->probe(dsi);
+}
+
+static int dsi_driver_remove(struct device *dev)
+{
+ const struct dsi_driver *drv = to_dsi_driver(dev->driver);
+ struct dsi_device *dsi = to_dsi_device(dev);
+
+ return drv->remove(dsi);
+}
+
+static void dsi_driver_shutdown(struct device *dev)
+{
+ const struct dsi_driver *drv = to_dsi_driver(dev->driver);
+ struct dsi_device *dsi = to_dsi_device(dev);
+
+ drv->shutdown(dsi);
+}
+
+/**
+ * dsi_register_driver() - register a DSI driver
+ * @drv: DSI driver
+ *
+ * Returns 0 on success or a negative error-code on failure.
+ */
+int dsi_register_driver(struct dsi_driver *drv)
+{
+ drv->driver.bus = &dsi_bus_type;
+
+ if (drv->probe)
+ drv->driver.probe = dsi_driver_probe;
+
+ if (drv->remove)
+ drv->driver.remove = dsi_driver_remove;
+
+ if (drv->shutdown)
+ drv->driver.shutdown = dsi_driver_shutdown;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(dsi_register_driver);
+
+/**
+ * dsi_unregister_driver() - unregister a DSI driver
+ * @drv: DSI driver
+ */
+void dsi_unregister_driver(struct dsi_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(dsi_unregister_driver);
+
+static int __init dsi_init(void)
+{
+ return bus_register(&dsi_bus_type);
+}
+postcore_initcall(dsi_init);
+
+MODULE_AUTHOR("Thierry Reding <treding at nvidia.com>");
+MODULE_DESCRIPTION("DRM DSI infrastructure");
+MODULE_LICENSE("GPL and additional rights");
diff --git a/include/drm/drm_dsi.h b/include/drm/drm_dsi.h
new file mode 100644
index 000000000000..0886160b9aa2
--- /dev/null
+++ b/include/drm/drm_dsi.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _DRM_DSI_H_
+#define _DRM_DSI_H_
+
+#include <linux/types.h>
+
+struct dsi_device;
+struct dsi_host;
+
+/*
+ * DSI packet data types
+ */
+
+/* processor-sourced packets */
+#define DSI_CMD_VSYNC_START 0x01
+#define DSI_CMD_VSYNC_END 0x11
+#define DSI_CMD_HSYNC_START 0x21
+#define DSI_CMD_HSYNC_END 0x31
+#define DSI_CMD_EOT 0x08
+#define DSI_CMD_COLOR_MODE_OFF 0x02
+#define DSI_CMD_COLOR_MODE_ON 0x12
+#define DSI_CMD_SHUT_DOWN 0x22
+#define DSI_CMD_TURN_ON 0x32
+#define DSI_CMD_GEN_SHORT_WRITE_0 0x03
+#define DSI_CMD_GEN_SHORT_WRITE_1 0x13
+#define DSI_CMD_GEN_SHORT_WRITE_2 0x23
+#define DSI_CMD_GEN_SHORT_READ_0 0x04
+#define DSI_CMD_GEN_SHORT_READ_1 0x14
+#define DSI_CMD_GEN_SHORT_READ_2 0x24
+#define DSI_CMD_DCS_SHORT_WRITE_0 0x05
+#define DSI_CMD_DCS_SHORT_WRITE_1 0x15
+#define DSI_CMD_DCS_SHORT_READ 0x06
+#define DSI_CMD_SET_MAX_RETURN_PACKET_SIZE 0x37
+#define DSI_CMD_NULL 0x09
+#define DSI_CMD_BLANK 0x19
+#define DSI_CMD_GEN_LONG_WRITE 0x29
+#define DSI_CMD_DCS_LONG_WRITE 0x39
+#define DSI_CMD_YCbCr422_20 0x0c
+#define DSI_CMD_YCbCr422_24 0x1c
+#define DSI_CMD_YCbCr422_16 0x2c
+#define DSI_CMD_RGB30 0x0d
+#define DSI_CMD_RGB36 0x1d
+#define DSI_CMD_YCbCr420 0x3d
+#define DSI_CMD_RGB16 0x0e
+#define DSI_CMD_RGB18 0x1e
+#define DSI_CMD_RGB18NP 0x2e
+#define DSI_CMD_RGB24 0x3e
+
+/* peripheral-sourced */
+#define DSI_RSP_ACK_ERR 0x02
+#define DSI_RSP_EOT 0x08
+#define DSI_RSP_GEN_SHORT_READ_1 0x11
+#define DSI_RSP_GEN_SHORT_READ_2 0x12
+#define DSI_RSP_GEN_LONG_READ 0x1a
+#define DSI_RSP_DCS_LONG_READ 0x1c
+#define DSI_RSP_DCS_SHORT_READ_1 0x21
+#define DSI_RSP_DCS_SHORT_READ_2 0x22
+
+#define DSI_ACK 0x84
+#define DSI_ESC 0x87
+
+/**
+ * struct dsi_msg - DSI command message
+ * @channel: virtual channel to send the message to
+ * @type: data ID of the message
+ * @tx_len: length of transmission buffer
+ * @tx: transmission buffer
+ * @rx_len: length of reception buffer
+ * @rx: reception buffer
+ */
+struct dsi_msg {
+ u8 channel;
+ u8 type;
+
+ size_t tx_len;
+ void *tx;
+
+ size_t rx_len;
+ void *rx;
+};
+
+/**
+ * struct dsi_host_ops - DSI host operations
+ * @attach: called when a peripheral is attached to the host
+ * @detach: called when a peripheral is detached from the host
+ * @transfer: transfer a DSI command message to a peripheral
+ */
+struct dsi_host_ops {
+ int (*attach)(struct dsi_host *host, struct dsi_device *device);
+ int (*detach)(struct dsi_host *host, struct dsi_device *device);
+ ssize_t (*transfer)(struct dsi_host *host, struct dsi_msg *msg);
+};
+
+/**
+ * struct dsi_host - DSI host
+ * @dev: device providing the DSI host functionality
+ * @ops: pointer to DSI host operations
+ */
+struct dsi_host {
+ struct device *dev;
+
+ const struct dsi_host_ops *ops;
+};
+
+static inline struct dsi_host *dsi_host_get(struct dsi_host *host)
+{
+ if (!host || !get_device(host->dev))
+ return NULL;
+
+ return host;
+}
+
+static inline void dsi_host_put(struct dsi_host *host)
+{
+ if (host)
+ put_device(host->dev);
+}
+
+int dsi_host_register(struct dsi_host *host);
+int dsi_host_unregister(struct dsi_host *host);
+
+ssize_t dsi_host_transfer(struct dsi_host *host, struct dsi_msg *msg);
+
+/**
+ * struct dsi_device - DSI peripheral
+ * @host: DSI host that this peripheral is attached to
+ * @dev: device to tie the peripheral into the device tree
+ * @channel: virtual channel of the peripheral
+ */
+struct dsi_device {
+ struct dsi_host *host;
+ struct device dev;
+
+ unsigned int channel;
+};
+
+static inline struct dsi_device *to_dsi_device(struct device *dev)
+{
+ return dev ? container_of(dev, struct dsi_device, dev) : NULL;
+}
+
+static inline struct dsi_device *dsi_device_get(struct dsi_device *dsi)
+{
+ if (!dsi || !get_device(&dsi->dev))
+ return NULL;
+
+ return dsi;
+}
+
+static inline void dsi_device_put(struct dsi_device *dsi)
+{
+ if (dsi)
+ put_device(&dsi->dev);
+}
+
+int dsi_device_attach(struct dsi_device *device);
+int dsi_device_detach(struct dsi_device *device);
+
+/**
+ * struct dsi_driver - DSI driver
+ * @driver: device driver model driver
+ * @probe: callback for device binding
+ * @remove: callback for device unbinding
+ * @shutdown: callback for device shutdown
+ */
+struct dsi_driver {
+ struct device_driver driver;
+ int (*probe)(struct dsi_device *dsi);
+ int (*remove)(struct dsi_device *dsi);
+ void (*shutdown)(struct dsi_device *dsi);
+};
+
+static inline struct dsi_driver *to_dsi_driver(struct device_driver *drv)
+{
+ return drv ? container_of(drv, struct dsi_driver, driver) : NULL;
+}
+
+int dsi_register_driver(struct dsi_driver *drv);
+void dsi_unregister_driver(struct dsi_driver *drv);
+
+#define module_dsi_driver(__dsi_driver) \
+ module_driver(__dsi_driver, dsi_register_driver, \
+ dsi_unregister_driver)
+
+#endif
--
1.8.4.2
More information about the dri-devel
mailing list