From patchwork Mon Nov 11 12:00:29 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 3167191 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 2262AC045B for ; Mon, 11 Nov 2013 12:01:07 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4D88120377 for ; Mon, 11 Nov 2013 12:01:05 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 0DCA7201F7 for ; Mon, 11 Nov 2013 12:01:00 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 888D7FABE5; Mon, 11 Nov 2013 04:00:54 -0800 (PST) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-bk0-f54.google.com (mail-bk0-f54.google.com [209.85.214.54]) by gabe.freedesktop.org (Postfix) with ESMTP id 8AB5AFABDF for ; Mon, 11 Nov 2013 04:00:48 -0800 (PST) Received: by mail-bk0-f54.google.com with SMTP id 6so1682445bkj.27 for ; Mon, 11 Nov 2013 04:00:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=bozucMrO+Rr4uC452crZOS57GkXkpyD5OFMLyMZa1ck=; b=swxKr+95aLAvwfbz7A/NdNhWpdGRx7hvHrnsAa15sDw0oqc7AX9DjQv8MqANcsw6Ix rs3D2qvANUVDIE6yJfxs9ywgwBtmjXp26sXgFogXY9/7ro/09aNAtPfx5K8KOfDvmwqI 4l/z2wET2b8n4ZEb+vnfZ2IQ8KARH2YlUmNtwOuQP++me4AGwFRxMce8QC1wEdM9uV1c x2J/A0JmX4GH/Yi+r35IvTak67bi9sT7RyZC8LQOqKrLhgzJjBasla7Fc0yBS4Uc5Mfn PYmfv4Ymq/qSek81+FmeVAN8iuvV+dsv0agBG5xziilKXRAYRNmVqGPzr+L/G1k/Hh22 ZxFw== X-Received: by 10.204.243.199 with SMTP id ln7mr2493bkb.93.1384171247672; Mon, 11 Nov 2013 04:00:47 -0800 (PST) Received: from localhost (port-13884.pppoe.wtnet.de. [84.46.54.114]) by mx.google.com with ESMTPSA id zl3sm14152656bkb.4.2013.11.11.04.00.46 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 11 Nov 2013 04:00:47 -0800 (PST) From: Thierry Reding To: dri-devel@lists.freedesktop.org Subject: [PATCH v3 1/7] drm: Add DSI bus infrastructure Date: Mon, 11 Nov 2013 13:00:29 +0100 Message-Id: <1384171235-2498-2-git-send-email-treding@nvidia.com> X-Mailer: git-send-email 1.8.4.2 In-Reply-To: <1384171235-2498-1-git-send-email-treding@nvidia.com> References: <1384171235-2498-1-git-send-email-treding@nvidia.com> X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces@lists.freedesktop.org Errors-To: dri-devel-bounces@lists.freedesktop.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In order to support DSI peripherals, add a DSI bus type that devices and drivers can be registered with. Signed-off-by: Thierry Reding --- 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 +#include +#include +#include +#include + +#include + +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 "); +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 + +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