From patchwork Thu Aug 2 19:45:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Ravnborg X-Patchwork-Id: 10554645 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7B2F713BB for ; Fri, 3 Aug 2018 07:16:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 61F092C451 for ; Fri, 3 Aug 2018 07:16:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 564062C578; Fri, 3 Aug 2018 07:16:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0A9152C451 for ; Fri, 3 Aug 2018 07:16:24 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 321416E6B5; Fri, 3 Aug 2018 07:14:18 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ed1-x543.google.com (mail-ed1-x543.google.com [IPv6:2a00:1450:4864:20::543]) by gabe.freedesktop.org (Postfix) with ESMTPS id A19CB89890 for ; Thu, 2 Aug 2018 19:45:42 +0000 (UTC) Received: by mail-ed1-x543.google.com with SMTP id s16-v6so1328200edq.12 for ; Thu, 02 Aug 2018 12:45:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=EmyceJsWlKJKj6s5+Zz3FdA8utFArWkVCik2jDUiN6E=; b=tob9T4AxZM2JrNrEMSFw7EeC8dM3OwN6HmqkIXUz4tMI1n7EEv29BWJ4EfxroUxSHE OfCsyoQk4IBsgN8gX2uYaXYQAleKR/PPoMUGp5PjNE5sfKR6SKpmjre/yKsirdf0v4LT LZynPJvCS0HFg7d81J2eLckzEErC1pe8Q+JutBCezZC04wt9ELkqXVPAKBEMO8st4gbg LbN7m8Bu7atO5nekYQSY/tsNxwE+fzksSuzomU+cLTsExoSUhiN06+TreemECKDW8Wzc j/q7VqBs38mfOOvBJ8M2fx71qJpAxdbkgEUp/Qa9WEDsbqK8bQWRJi51tOxksYy/P0F8 GQ0g== X-Gm-Message-State: AOUpUlEfwasSz0WyMDp6ofTvIMIARRsS0Mco7ewBbJuaJCe1dfVz+ysN PjlLNyTIDcOMUvNOCdXG6W1lZjxP X-Google-Smtp-Source: AAOMgpdeOrDFpcLwNdkN/bA+C9nkk4jXDsqGWw1FZG5A3IVNe8Tbf1XiF9uUCcd2HRqKkFAQRCuGIw== X-Received: by 2002:a50:c8c3:: with SMTP id k3-v6mr4532127edh.193.1533239141291; Thu, 02 Aug 2018 12:45:41 -0700 (PDT) Received: from jupiter.lan (126.158-248-196.customer.lyse.net. [158.248.196.126]) by smtp.gmail.com with ESMTPSA id g6-v6sm1219655edn.28.2018.08.02.12.45.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Aug 2018 12:45:40 -0700 (PDT) From: Sam Ravnborg To: Noralf T , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [PATCH v1 1/5] dt-bindings: add parallel data bus (pardata) Date: Thu, 2 Aug 2018 21:45:32 +0200 Message-Id: <20180802194536.10820-1-sam@ravnborg.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20180802193909.GA11443@ravnborg.org> References: <20180802193909.GA11443@ravnborg.org> X-Mailman-Approved-At: Fri, 03 Aug 2018 07:13:57 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Ravnborg MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The parallel data bus is a simple parallel bus that may be used to drive displays. It is mostly used in simple embedded systems but is also used in some desings utilising Linux. Signed-off-by: Sam Ravnborg --- .../bindings/pardata/parallel-data-bus.txt | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/pardata/parallel-data-bus.txt diff --git a/Documentation/devicetree/bindings/pardata/parallel-data-bus.txt b/Documentation/devicetree/bindings/pardata/parallel-data-bus.txt new file mode 100644 index 000000000000..fa33b5bb8302 --- /dev/null +++ b/Documentation/devicetree/bindings/pardata/parallel-data-bus.txt @@ -0,0 +1,60 @@ +DT binding for parallel data bus + +Required properties: +- compatible: "parallel-data-bus" +- data-gpios: array of 8 GPIO specifiers, referring to the GPIO pins + connected to the data signal lines DB0-DB7 + (8-bit mode) of the LCD Controller's bus interface, +- enable-gpios: GPIO specifier, referring to the GPIO pin connected to + the "E" (Enable) signal line of the controller's bus interface, +- rs-gpios: GPIO specifier, referring to the GPIO pin + connected to the "RS" (Register Select) controller's bus interface, + +Required properties for 8080 interface: +- readwrite-gpios: For chips with 8080 interface list the GPIO + pin connected to the controllers read/write pin + +Required properties for 6800 interface: +- read-gpios: For chips with 6800 interface list the GPIO + pin connected to the controllers read pin +- write-gpios: For chips with 6800 interface list the GPIO + pin connected to the controllers write pin + +Required property for child node(s): +- reg: numeric identifier for the display + +Example: + + backlight: backlight { + compatible = "gpio-backlight"; + } + + power: regulator@0 { + } + + pardatabus { + compatible = "parallel-data-bus"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pardata>; + data-gpios = <&pioe 0 GPIO_ACTIVE_HIGH + &pioe 1 GPIO_ACTIVE_HIGH + &pioe 2 GPIO_ACTIVE_HIGH + &pioe 3 GPIO_ACTIVE_HIGH + &pioe 4 GPIO_ACTIVE_HIGH + &pioe 5 GPIO_ACTIVE_HIGH + &pioe 6 GPIO_ACTIVE_HIGH + &pioe 7 GPIO_ACTIVE_HIGH>; + enable-gpios = <&pioe 8 GPIO_ACTIVE_HIGH>; + rs-gpios = <&pioe 12 GPIO_ACTIVE_HIGH>; + readdwrite-gpios = <&pioe 11 GPIO_ACTIVE_HIGH>; + + wg160160@0 { + compatible = "winstar,wg160160"; + reg = <1>; + reset-gpios = <&pioe 13 GPIO_ACTIVE_LOW>; + chipselect-gpios = <&pioe 9 GPIO_ACTIVE_LOW>; + backlight = &backlight; + power = &power; + } + } + From patchwork Thu Aug 2 19:45:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Ravnborg X-Patchwork-Id: 10554629 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 59F2F13BB for ; Fri, 3 Aug 2018 07:14:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 40B5328745 for ; Fri, 3 Aug 2018 07:14:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 34E372C36A; Fri, 3 Aug 2018 07:14:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 28E4D28745 for ; Fri, 3 Aug 2018 07:14:45 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C25326E69F; Fri, 3 Aug 2018 07:14:00 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ed1-x544.google.com (mail-ed1-x544.google.com [IPv6:2a00:1450:4864:20::544]) by gabe.freedesktop.org (Postfix) with ESMTPS id A51AD899C7 for ; Thu, 2 Aug 2018 19:45:51 +0000 (UTC) Received: by mail-ed1-x544.google.com with SMTP id k15-v6so1346696edr.3 for ; Thu, 02 Aug 2018 12:45:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=W+QhGKv63Bkgx9akWAUZspgZxiyfH0Fd3o7TmCLCC2o=; b=O7zpkTqTcZ0TehilqSTkFHf/KXTc9ZuR/dQ9nLIU8U610TFJGA0WfJ5ZdO6XWHGXJB w632/z/Pj9ZAdSVQidjDgL8lN6U9j9Fb/P36gxpphboUDpHtn04+s9BGnPVZFWHuNjGm bboSUS0N9pjOjzRDYED6p4ByiXFyLRR63ZYn1R1HGWLUBo29Eznu9Jrk61phPGXiD6r9 oVWU9kRMivist0x67GkO/+iL/+zP7F6cfRKdBQfPcEaSPKCZ1gQ9bzaPER/+FQeeNNoU dyd4wcJlXCkk87Jx1avuvgLqpMV6hIRrK0XRfGzbSfEkulz19NL79KShGFV5ElammFwD 2sqQ== X-Gm-Message-State: AOUpUlGyTX3YOwssa/AK4AbBoQXOcXBKxoAegKeqKX5I1Y3JLDRuGmuE 0+PmOHABjcWXit8qc6z1vAg= X-Google-Smtp-Source: AAOMgpdN5bfm2kAfg6E/VQZ69SwSYXR2b4sW5fty/L0z2r5yneAImxQ9yCe29PnFjEhPWmYKoyahew== X-Received: by 2002:a50:fd93:: with SMTP id o19-v6mr4496083edt.73.1533239150136; Thu, 02 Aug 2018 12:45:50 -0700 (PDT) Received: from jupiter.lan (126.158-248-196.customer.lyse.net. [158.248.196.126]) by smtp.gmail.com with ESMTPSA id g6-v6sm1219655edn.28.2018.08.02.12.45.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Aug 2018 12:45:49 -0700 (PDT) From: Sam Ravnborg To: Noralf T , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [PATCH v1 2/5] pardata: new bus for parallel data access Date: Thu, 2 Aug 2018 21:45:33 +0200 Message-Id: <20180802194536.10820-2-sam@ravnborg.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20180802193909.GA11443@ravnborg.org> References: <20180802193909.GA11443@ravnborg.org> X-Mailman-Approved-At: Fri, 03 Aug 2018 07:13:57 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Ravnborg MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The pardata supports implement a simple bus for devices that are connected using a parallel bus driven by GPIOs. The is often used in combination with simple displays that is often seen in older embedded designs. There is a demand for this support also in the linux kernel for HW designs that uses these kind of displays. The pardata bus uses a platfrom_driver that when probed creates devices for all child nodes in the DT, which are then supposed to be handled by pardata_drivers. Signed-off-by: Sam Ravnborg --- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/pardata.rst | 60 ++++++++ MAINTAINERS | 9 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/pardata/Kconfig | 17 +++ drivers/pardata/Makefile | 5 + drivers/pardata/pardata.c | 282 +++++++++++++++++++++++++++++++++++ include/linux/pardata.h | 138 +++++++++++++++++ 9 files changed, 515 insertions(+) create mode 100644 Documentation/driver-api/pardata.rst create mode 100644 drivers/pardata/Kconfig create mode 100644 drivers/pardata/Makefile create mode 100644 drivers/pardata/pardata.c create mode 100644 include/linux/pardata.h diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6d9f2f9fe20e..1808fca406ae 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -41,6 +41,7 @@ available subsections can be seen below. miscellaneous w1 rapidio + pardata s390-drivers vme 80211/index diff --git a/Documentation/driver-api/pardata.rst b/Documentation/driver-api/pardata.rst new file mode 100644 index 000000000000..a811f024a0fe --- /dev/null +++ b/Documentation/driver-api/pardata.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +Parallel Data Bus/Drivers +========================= + +Displays may be connected using a simple parallel bus. +This is often seen in embedded systems with a simple MCU, but is also +used in Linux based systems to a small extent. + +The bus looks like this: + +.. code-block:: none + + ----+ + | DB0-DB7 or DB4-DB7 +---- + ===/======================== + | E - enable | D + ---------------------------- I + C | Reset | S + P ---------------------------- P + U | Read/Write (one or two) | L + ---------------------------- A + | RS - instruction/data | Y + ---------------------------- + | +---- + ----+ + +There may be several devices connected to the bus with individual +reset and read/write signals. +Two types of interfaces are supported. 8800 with a single read/write signal, +and 6800 with individual read/write signals. + +Display supported by the parallel data bus: ``_ + +The overall code structure is + +.. code-block:: none + + platform_device [pardata bus] <--- pardatabus [driver] + | + +-> device + | + +-> pardata_bus_data + + pardata_device + | + +-> device + | + +-> + + pardatabus_bus + + +API documentation +================= +.. kernel-doc:: include/linux/pardata.h + :internal: +.. kernel-doc:: drivers/pardata/pardata.c + :internal: diff --git a/MAINTAINERS b/MAINTAINERS index 96e98e206b0d..4ba7ff7c3e46 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10727,6 +10727,15 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/panasonic-laptop.c +PARALLEL DATA SUBSYSTEM +M: Sam Ravnborg +S: Maintained +F: drivers/pardata/ +F: include/linux/pardata.h +F: drivers/gpu/drm/tinydrm/pardata-dbi.c +F: include/drm/pardata-dbi.h +F: Documentation/driver-api/pardata.rst + PARALLEL LCD/KEYPAD PANEL DRIVER M: Willy Tarreau M: Ksenija Stanojevic diff --git a/drivers/Kconfig b/drivers/Kconfig index 95b9ccc08165..b51b25aae9a5 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -217,4 +217,6 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/pardata/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 24cd47014657..cfe7945f2f6b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_CONNECTOR) += connector/ obj-$(CONFIG_FB_I810) += video/fbdev/i810/ obj-$(CONFIG_FB_INTEL) += video/fbdev/intelfb/ +obj-$(CONFIG_PARDATA) += pardata/ obj-$(CONFIG_PARPORT) += parport/ obj-$(CONFIG_NVM) += lightnvm/ obj-y += base/ block/ misc/ mfd/ nfc/ diff --git a/drivers/pardata/Kconfig b/drivers/pardata/Kconfig new file mode 100644 index 000000000000..5a4280a3f85a --- /dev/null +++ b/drivers/pardata/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Parallel Data Bus framework +# +menuconfig PARDATA + tristate "Parallel Data support" + help + The parallel data framework is used for devices communicating + with a simple parallel interface. + The interface include 8 data pins, one register select, + one enable pin, a chip select and a reset pin. + The read/write may be 8080 style (one r/w pin) or 6800 style + with separate read and write pins. + The parallel data bus is often used for displays in embedded + systems. + + If unsure, choose N. diff --git a/drivers/pardata/Makefile b/drivers/pardata/Makefile new file mode 100644 index 000000000000..0e48bd648879 --- /dev/null +++ b/drivers/pardata/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for kernel Parallel Data subsystem +# +obj-$(CONFIG_PARDATA) += pardata.o diff --git a/drivers/pardata/pardata.c b/drivers/pardata/pardata.c new file mode 100644 index 000000000000..985d692c116a --- /dev/null +++ b/drivers/pardata/pardata.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, Sam Ravnborg + * + * Author: Sam Ravnborg + */ + +/** + * DOC: Bus driver for parallel data bus + * + * The parallel data bus is often used for displays + * in embedded designs and is sometimes also used + * in designs supporting Linux. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pardata_device * +pardata_device_create(struct pardatabus_data *pdbus) +{ + struct pardata_device *pddev; + + /* We allocate the device, and initialize the default values */ + pddev = kzalloc(sizeof(*pddev), GFP_KERNEL); + if (!pddev) + return ERR_PTR(-ENOMEM); + + pddev->dev.parent = pdbus->dev; + pddev->dev.bus = &pardata_bus; + + dev_set_name(&pddev->dev, "pardata"); + + device_initialize(&pddev->dev); + + return pddev; +} + +static void pardata_device_release(struct pardata_device *ppdev) +{ + kfree(ppdev); +} + +int pardata_device_register(struct pardata_device *pddev) +{ + return device_add(&pddev->dev); +} + +static int of_pdbus_register_device(struct pardatabus_data *pdbus, + struct device_node *np) +{ + struct pardata_device *pddev; + int ret; + u32 id; + + pddev = pardata_device_create(pdbus); + if (IS_ERR(pddev)) + return PTR_ERR(pddev); + + /* The reg property is used as id */ + ret = of_property_read_u32(np, "reg", &id); + if (ret) { + dev_err(pdbus->dev, "Failed to read the 'reg' property"); + pardata_device_release(pddev); + return ret; + } + pddev->id = id; + + /* + * Associate the OF node with the device structure so it + * can be looked up later. + */ + of_node_get(np); + pddev->dev.of_node = np; + + /* All data is now stored in the pardata_device struct; register it. */ + ret = pardata_device_register(pddev); + if (ret) { + pardata_device_release(pddev); + of_node_put(np); + return ret; + } + + dev_dbg(&pddev->dev, "registered pardata device %s with id %i\n", + np->name, id); + return 0; +} + +static void pdbus_unregister(void) +{ + bus_unregister(&pardata_bus); +} + +/** + * of_pardata_register - Register pardatabus and create devices from DT + * + * @pdbus: pointer to pardatabus_data structure + * + * This function registers the pardatabus_data structure and registers + * a pardata_device for each child node of @np. + */ +int of_pardata_register(struct pardatabus_data *pdbus) +{ + struct device_node *child; + struct device_node *np; + int ret; + + np = pdbus->dev->of_node; + + /* Do not continue if the node is not available or disabled */ + if (!np || !of_device_is_available(np)) + return -ENODEV; + + /* Register the parallel data bus */ + ret = bus_register(&pardata_bus); + if (ret) + return ret; + + /* + * Loop over the child nodes and register a + * pardata_device for each node + */ + for_each_available_child_of_node(np, child) { + ret = of_pdbus_register_device(pdbus, child); + + if (ret == -ENODEV) + dev_err(pdbus->dev, "pardata device missing.\n"); + else if (ret) + goto unregister; + } + return 0; + +unregister: + pdbus_unregister(); + return ret; +} + +static int pardata_device_match(struct device *dev, struct device_driver *drv) +{ + /* OF style match */ + if (of_match_device(drv->of_match_table, dev) != NULL) + return 1; + + return 0; +} + +static int pardata_device_probe(struct device *dev) +{ + struct pardata_device *pddev; + struct pardata_driver *pddrv; + + pddev = to_pardata_device(dev); + pddrv = to_pardata_driver(dev->driver); + + return pddrv->probe(pddev); +} + +struct bus_type pardata_bus = { + .name = "pardata", + .match = pardata_device_match, + .probe = pardata_device_probe, +}; +EXPORT_SYMBOL_GPL(pardata_bus); + +/* + * __pardata_driver_register() - Client driver registration + * + * @drv:Client driver to be associated with client-device. + * @owner: owning module/driver + * + * Do not call this direct, use pardata_driver_register() + */ +int __pardata_driver_register(struct pardata_driver *drv, struct module *owner) +{ + /* probe is mandatory */ + if (!drv->probe) + return -EINVAL; + + drv->driver.bus = &pardata_bus; + drv->driver.owner = owner; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__pardata_driver_register); + +/* + * Use a simple pardatabus driver to create the pardata + * devices from child nodes in DT. + * + * The pardatabus driver parses the properties in the + * device tree that will be used by the pardata drivers. + */ +static int pardatabus_probe(struct platform_device *pdev) +{ + struct pardatabus_data *pdbus; + struct gpio_descs *pins; + struct gpio_desc *pin; + struct device *dev; + + dev = &pdev->dev; + + pdbus = devm_kzalloc(dev, sizeof(*pdbus), GFP_KERNEL); + if (!pdbus) + return -ENOMEM; + + pdbus->dev = &pdev->dev; + dev_set_drvdata(dev, pdbus); + + /* Get the mandatory bus details from DT */ + pins = devm_gpiod_get_array(dev, "data-gpios", GPIOD_OUT_LOW); + if (IS_ERR(pins)) { + dev_err(dev, "Failed to get gpio 'data-gpios'"); + return PTR_ERR(pins); + } + if (pins->ndescs != 4 && pins->ndescs != 8) { + dev_err(dev, "Invalid number of data-gpios %d - expected 4 or 8\n", + pins->ndescs); + return -EINVAL; + } + pdbus->data_pins = pins; + + pin = devm_gpiod_get(dev, "enable-gpios", GPIOD_OUT_LOW); + if (IS_ERR(pin)) { + dev_err(dev, "Failed to get gpio 'enable-gpios'\n"); + return PTR_ERR(pin); + } + pdbus->pin_enable = pin; + + pin = devm_gpiod_get(dev, "rs-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(pin)) { + dev_err(dev, "Failed to get gpio 'rs-gpios'\n"); + return PTR_ERR(pin); + } + pdbus->pin_rs = pin; + + pin = devm_gpiod_get(dev, "rw-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(pin)) { + dev_err(dev, "Failed to get gpio 'rw-gpios'\n"); + return PTR_ERR(pin); + } + pdbus->pin_readwrite = pin; + + pin = devm_gpiod_get(dev, "read-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(pin)) { + dev_err(dev, "Failed to get gpio 'read-gpios'\n"); + return PTR_ERR(pin); + } + pdbus->pin_read = pin; + + pin = devm_gpiod_get(dev, "write-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(pin)) { + dev_err(dev, "Failed to get gpio 'write-gpios'\n"); + return PTR_ERR(pin); + } + pdbus->pin_write = pin; + + return of_pardata_register(pdbus); +} + +static const struct of_device_id pardata_bus_dt_ids[] = { + { .compatible = "parallel-data-bus", }, + { /* sentinel */ } +}; + +static struct platform_driver pardatabus_driver = { + .driver = { + .name = "pardatabus", + .of_match_table = pardata_bus_dt_ids, + }, + .probe = pardatabus_probe, +}; +module_platform_driver(pardatabus_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Parallel Data Bus"); diff --git a/include/linux/pardata.h b/include/linux/pardata.h new file mode 100644 index 000000000000..888a1c88176e --- /dev/null +++ b/include/linux/pardata.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Sam Ravnborg + */ + +#ifndef _LINUX_PARDATA_H +#define _LINUX_PARDATA_H + +#include +#include + +extern struct bus_type pardata_bus; + +/** + * struct pardatabus_data - private data for the pardata bus + * + * Drivers uses these data when usung the pins that are part of + * the bus. + */ +struct pardatabus_data { + /** + * @dev: device node that represent the DT node of the bus + */ + struct device *dev; + + /** + * @data_pins: The 8 or 4 GPIOs used for data + */ + struct gpio_descs *data_pins; + + /** + * @pin_enable: The enable pin (mandatory) + */ + struct gpio_desc *pin_enable; + + /** + * @pin_rs: register select (0: instruction 1: data) + */ + struct gpio_desc *pin_rs; + + /** + * @pin_readwrite: readwrite pin for 8080 style interface + */ + struct gpio_desc *pin_readwrite; + + /** + * @pin_read: read pin for 6800 style interface + */ + struct gpio_desc *pin_read; + + /** + * @pin_write: write pin for 6800 style interface + */ + struct gpio_desc *pin_write; +}; + +static inline struct pardatabus_data *to_pardatabus_data(struct device *d) +{ + return container_of(d, struct pardatabus_data, dev); +} + +/** + * struct pardata_device - pardata device handle. + * @dev: Driver model representation of the device. + * + * This is the device handle returned when a pardata + * device is registered. + * The id comes from the DT + * + * @dev: The device + * @id: Id of the device (from DT) + */ +struct pardata_device { + struct device dev; + u32 id; +}; + +static inline struct pardata_device *to_pardata_device(struct device *d) +{ + return container_of(d, struct pardata_device, dev); +} + +static inline void *pardata_dev_get_drvdata(const struct pardata_device *pdd) +{ + return dev_get_drvdata(&pdd->dev); +} + +static inline void pardata_dev_set_drvdata(struct pardata_device *pdd, + void *data) +{ + dev_set_drvdata(&pdd->dev, data); +} + +/** + * struct pardata_driver - pardata 'generic device' + * (similar to 'spi_device' on SPI) + * + * @driver: generic device driver + * pardata drivers must initialize name, owner and of_match_table + * of this structure + * @probe: Binds this driver to a pardata bus. + * @shutdown: Standard shutdown callback used during powerdown/halt. + */ +struct pardata_driver { + struct device_driver driver; + int (*probe)(struct pardata_device *pdd); + void (*shutdown)(struct pardata_device *pdd); +}; + +static inline struct pardata_driver *to_pardata_driver(struct device_driver *d) +{ + return container_of(d, struct pardata_driver, driver); +} + +/* Use macro for driver registering to get simple access to THIS_MODULE */ +#define pardata_driver_register(drv) \ + __pardata_driver_register(drv, THIS_MODULE) +int __pardata_driver_register(struct pardata_driver *drv, struct module *owner); + +static inline void pardata_driver_unregister(struct pardata_driver *drv) +{ + return driver_unregister(&drv->driver); +} + +/** + * module_pardata_driver() - Helper macro for registering a parallel data driver + * @__pardata_driver: pardatabus_driver struct + * + * Helper macro for parallel data drivers which do not 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_pardata_driver(__pardata_driver) \ + module_driver(__pardata_driver, pardata_driver_register, \ + pardata_driver_unregister) + +#endif /* _LINUX_PARDATA_H */ From patchwork Thu Aug 2 19:45:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Ravnborg X-Patchwork-Id: 10554643 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E7E6313BB for ; Fri, 3 Aug 2018 07:16:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CE5C62C451 for ; Fri, 3 Aug 2018 07:16:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C0A772C578; Fri, 3 Aug 2018 07:16:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BAC042C451 for ; Fri, 3 Aug 2018 07:16:17 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id D881E6E6B2; Fri, 3 Aug 2018 07:14:17 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ed1-x543.google.com (mail-ed1-x543.google.com [IPv6:2a00:1450:4864:20::543]) by gabe.freedesktop.org (Postfix) with ESMTPS id 3ACCB6E63A for ; Thu, 2 Aug 2018 19:45:56 +0000 (UTC) Received: by mail-ed1-x543.google.com with SMTP id k15-v6so1346755edr.3 for ; Thu, 02 Aug 2018 12:45:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=qlp/yvroyvV5fqwp5a4gJoO/6mmWbq2pzsE8ATWcghc=; b=QwvhiSua1UG+fzTiftfgrncOcSNjp9VyI+Up4fHzrhQY7VMTp4OWEYXsPLlfSEmJas X76ReXNgpHqYH4dc6vlCeba0q4rQT+E/DcbS1biMJmAq2qYKSQButZWQHLw8WanZPirg Q610OMHbVhQb4OIEif/9UPAv6j74xhns/5dFmVUpjhgDYIpR5y5YuyvZCYFlF5/EF/bW JavcODDRPdXl9pq+JWCE1oDJ+KO6sc/zUDS23vZam1r636HH5vMcHnKKMOI6TJ4d4zbO KMDQoQQamlz2wNquYO7k6PDlsOP0hN3ga0lT5V3V5jrOSfNsxmxak8kytfkfbg1T/Cht ejhg== X-Gm-Message-State: AOUpUlHipzgpi3xKmXpY4hiXy58u/Ws7RQcNC/BM3cPt2MfT5FW/XjY+ vzYh01vRZIiZoa81KnE9tLU= X-Google-Smtp-Source: AAOMgpf+nitEWDdiNLY0lJJZo4QIdpxsEXrNNBSe+oQPuuRtPNnIha8tiqFsJbtuM051Vk98WP4R9g== X-Received: by 2002:a50:a804:: with SMTP id j4-v6mr4781364edc.61.1533239154678; Thu, 02 Aug 2018 12:45:54 -0700 (PDT) Received: from jupiter.lan (126.158-248-196.customer.lyse.net. [158.248.196.126]) by smtp.gmail.com with ESMTPSA id g6-v6sm1219655edn.28.2018.08.02.12.45.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Aug 2018 12:45:54 -0700 (PDT) From: Sam Ravnborg To: Noralf T , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [PATCH v1 3/5] tinydrm: add support for parallel data displays Date: Thu, 2 Aug 2018 21:45:34 +0200 Message-Id: <20180802194536.10820-3-sam@ravnborg.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20180802193909.GA11443@ravnborg.org> References: <20180802193909.GA11443@ravnborg.org> X-Mailman-Approved-At: Fri, 03 Aug 2018 07:13:57 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Ravnborg MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Utilising the pardata bus/driver add support for displays connected to a parallel data bus. There is no specific protocol implemented, but the display is tied to the tinydrm pipe. Only monochrome displays supported using the XRGB8888 format. Signed-off-by: Sam Ravnborg --- drivers/gpu/drm/tinydrm/Kconfig | 3 + drivers/gpu/drm/tinydrm/Makefile | 1 + drivers/gpu/drm/tinydrm/pardata-dbi.c | 417 ++++++++++++++++++++++++++++++++++ include/drm/tinydrm/pardata-dbi.h | 257 +++++++++++++++++++++ 4 files changed, 678 insertions(+) create mode 100644 drivers/gpu/drm/tinydrm/pardata-dbi.c create mode 100644 include/drm/tinydrm/pardata-dbi.h diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index 4592a5e3f20b..435de2f8d8f5 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -10,6 +10,9 @@ menuconfig DRM_TINYDRM config TINYDRM_MIPI_DBI tristate +config TINYDRM_PARDATA_DBI + tristate + config TINYDRM_ILI9225 tristate "DRM support for ILI9225 display panels" depends on DRM_TINYDRM && SPI diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile index 49a111929724..0b52df08b0a4 100644 --- a/drivers/gpu/drm/tinydrm/Makefile +++ b/drivers/gpu/drm/tinydrm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_DRM_TINYDRM) += core/ # Controllers obj-$(CONFIG_TINYDRM_MIPI_DBI) += mipi-dbi.o +obj-$(CONFIG_TINYDRM_PARDATA_DBI) += pardata-dbi.o # Displays obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o diff --git a/drivers/gpu/drm/tinydrm/pardata-dbi.c b/drivers/gpu/drm/tinydrm/pardata-dbi.c new file mode 100644 index 000000000000..09bdfdba6291 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/pardata-dbi.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Parallel Data Display Bus Interface LCD controller support + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * pardata_strobe_8080_write - using the 8080 interface create + * an enable strobe + * + * The interface requires that readwrite and chipselect are + * setup before the strobe of "e". + * There are timings constraints that is handled using udelay(). + * Drivers can use this for the strobe_write callback. + * + * @pdd: pardata data + */ +void pardata_strobe_8080_write(struct pardata_data *pdd) +{ + gpiod_set_value_cansleep(pdd->bus->pin_readwrite, 0); + + if (pdd->pin_cs) + gpiod_set_value_cansleep(pdd->pin_cs, 0); + /* min 90 nsec from cs/rs/rw to e */ + udelay(1); + gpiod_set_value_cansleep(pdd->bus->pin_enable, 1); + /* data setup time 220 ns*/ + udelay(2); + gpiod_set_value_cansleep(pdd->bus->pin_enable, 0); + /* data hold time 20 ns */ + udelay(1); + if (pdd->pin_cs) + gpiod_set_value_cansleep(pdd->pin_cs, 1); +} +EXPORT_SYMBOL_GPL(pardata_strobe_8080_write); + +/** + * pardata_strobe_6800_write - using the 6800 interface create + * an enable strobe + * + * The interface requires that read + write and chipselect are + * setup before the strobe of "e". + * There are timings constraints that is handled using udelay() + * Drivers can use this for the strobe_write callback. + * + * @pdd: pardata data + */ + +void pardata_strobe_6800_write(struct pardata_data *pdd) +{ + gpiod_set_value_cansleep(pdd->bus->pin_read, 0); + gpiod_set_value_cansleep(pdd->bus->pin_write, 1); + + if (pdd->pin_cs) + gpiod_set_value_cansleep(pdd->pin_cs, 0); + /* min 90 nsec from cs/rs/rw to e */ + udelay(1); + gpiod_set_value_cansleep(pdd->bus->pin_enable, 1); + /* data setup time 220 ns*/ + udelay(2); + gpiod_set_value_cansleep(pdd->bus->pin_enable, 0); + /* data hold time 20 ns */ + udelay(1); + if (pdd->pin_cs) + gpiod_set_value_cansleep(pdd->pin_cs, 1); +} +EXPORT_SYMBOL_GPL(pardata_strobe_6800_write); + + +static int pardata_write_clip(struct pardata_data *pdd, + struct drm_framebuffer *fb, + u8 *data, + struct drm_clip_rect *clip) +{ + bool line_by_line; + size_t linelen; + size_t len; + int x, y; + u8 *buf; + + linelen = clip->x2 - clip->x1; + len = linelen * (clip->y2 - clip->y1) / 8; + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * If the clip is the full width then we need only one + * write operation. + */ + line_by_line = (clip->x2 - clip->x1) != fb->width; + + for (y = clip->y1; y < clip->y2; y++) { + for (x = clip->x1; x < clip->x2; x++) { + unsigned int index; + u8 cc; + + index = x + y * linelen; + cc = data[index]; + + /* Most significant bit determine bit on/off */ + if (cc & 0x80) + buf[index / 8] |= BIT(index % 8); + } + if (line_by_line) { + int offset; + + offset = y * fb->width + clip->x1; + pardata_write_buf(pdd, offset, &buf[offset], linelen); + } + } + if (!line_by_line) { + int offset; + + offset = clip->y1 * fb->width; + pardata_write_buf(pdd, offset, &buf[offset], len); + } + + kfree(buf); + + return 0; +} + +/** + * pardata_buf_copy - Copy a framebuffer, transforming it if necessary + * + * @dst: The destination buffer + * @fb: The source framebuffer + * @clip: Clipping rectangle of the area to be copied + * + * Returns: + * Zero on success, negative error code on failure. + */ +static int pardata_buf_copy(void *dst, struct drm_framebuffer *fb, + struct drm_clip_rect *clip) +{ + struct dma_buf_attachment *import_attach; + struct drm_gem_cma_object *cma_obj; + void *src; + int ret; + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + import_attach = cma_obj->base.import_attach; + src = cma_obj->vaddr; + ret = 0; + + if (import_attach) { + ret = dma_buf_begin_cpu_access(import_attach->dmabuf, + DMA_FROM_DEVICE); + if (ret) + return ret; + } + + tinydrm_xrgb8888_to_gray8(dst, src, fb, clip); + + if (import_attach) + ret = dma_buf_end_cpu_access(import_attach->dmabuf, + DMA_FROM_DEVICE); + return ret; +} + +static int pardata_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int flags, + unsigned int color, + struct drm_clip_rect *clips, + unsigned int num_clips) +{ + struct drm_format_name_buf format_name; + struct drm_gem_cma_object *cma_obj; + struct tinydrm_device *tdev; + struct drm_clip_rect clip; + struct pardata_data *pdd; + bool full; + int ret; + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + tdev = fb->dev->dev_private; + pdd = pardata_from_tinydrm(tdev); + ret = 0; + + if (!pdd->enabled) + return 0; + + if (fb->format->format != DRM_FORMAT_XRGB8888) { + dev_err_once(fb->dev->dev, "Format is not supported: %s\n", + drm_get_format_name(fb->format->format, + &format_name)); + return -EINVAL; + } + + full = tinydrm_merge_clips(&clip, clips, num_clips, flags, + fb->width, fb->height); + + DRM_DEV_DEBUG(&pdd->pddev->dev, + "Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", + fb->base.id, clip.x1, clip.x2, clip.y1, clip.y2); + + if (!full) { + ret = pardata_buf_copy(pdd->tx_buf, fb, &clip); + if (!ret) + ret = pardata_write_clip(pdd, fb, pdd->tx_buf, &clip); + } else { + size_t len; + + len = fb->width * fb->height; + ret = pardata_write_buf(pdd, 0, cma_obj->vaddr, len); + } + + return ret; +} + +static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = { + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, + .dirty = tinydrm_fb_dirty, +}; + +/** + * pardata_enable_flush - enable helper + * + * @pdd: pardata data + * @crtc_state: crtc state + * @plane_state: plane state + * + * This function sets &pdd->enabled, flushes the whole framebuffer and + * enables the backlight. Drivers can use this in their + * &drm_simple_display_pipe_funcs->enable callback. + */ +void pardata_enable_flush(struct pardata_data *pdd, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct tinydrm_device *tdev; + struct drm_framebuffer *fb; + + tdev = &pdd->tinydrm; + fb = plane_state->fb; + pdd->enabled = true; + + if (fb) + tdev->fb_dirty(fb, NULL, 0, 0, NULL, 0); + + backlight_enable(pdd->backlight); +} +EXPORT_SYMBOL_GPL(pardata_enable_flush); + +static void pardata_blank(struct pardata_data *pdd) +{ + struct drm_device *drm; + u16 height, width; + size_t len; + + drm = pdd->tinydrm.drm; + height = drm->mode_config.min_height; + width = drm->mode_config.min_width; + + len = width * height; + + memset(pdd->tx_buf, 0, len); + pardata_write_buf(pdd, 0, pdd->tx_buf, len); +} + +/** + * pardata_pipe_disable - pipe disable helper + * + * @pipe: Display pipe + * + * This function disables backlight if present, if not the display memory is + * blanked. The regulator is disabled if in use. Drivers can use this as their + * &drm_simple_display_pipe_funcs->disable callback. + */ +void pardata_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct tinydrm_device *tdev; + struct pardata_data *pdd; + + tdev = pipe_to_tinydrm(pipe); + pdd = pardata_from_tinydrm(tdev); + + pdd->enabled = false; + + if (pdd->backlight) + backlight_disable(pdd->backlight); + else + pardata_blank(pdd); + + if (pdd->regulator) + regulator_disable(pdd->regulator); +} +EXPORT_SYMBOL_GPL(pardata_pipe_disable); + +static const uint32_t pardata_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +/** + * pardata_init - initialization + * @dev: Parent device + * @pdd: pardata data to initialize + * @pipe_funcs: Display pipe functions + * @driver: DRM driver + * @mode: Display mode + * + * This function initializes a &pardata data structure and it's underlying + * @tinydrm_device. It also sets up the display pipeline. + * + * Supported formats: Emulated XRGB8888. + * + * Objects created by this function will be automatically freed on driver + * detach (devres). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int pardata_init(struct device *dev, + struct pardata_data *pdd, + const struct drm_simple_display_pipe_funcs *pipe_funcs, + struct drm_driver *driver, + const struct drm_display_mode *mode) +{ + struct tinydrm_device *tdev; + size_t bufsize; + int ret; + + /* shortcut to pardatabus_data */ + pdd->bus = dev_get_drvdata(dev->parent); + + tdev = &pdd->tinydrm; + bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); + + pdd->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL); + if (!pdd->tx_buf) + return -ENOMEM; + + ret = devm_tinydrm_init(dev, tdev, &mipi_dbi_fb_funcs, driver); + if (ret) + return ret; + + tdev->fb_dirty = pardata_fb_dirty; + + ret = tinydrm_display_pipe_init(tdev, pipe_funcs, + DRM_MODE_CONNECTOR_VIRTUAL, + pardata_formats, + ARRAY_SIZE(pardata_formats), + mode, + 0); + if (ret) + return ret; + + tdev->drm->mode_config.preferred_depth = 16; + + drm_mode_config_reset(tdev->drm); + + return 0; +} +EXPORT_SYMBOL_GPL(pardata_init); + +/** + * pardata_hw_reset - Hardware reset of controller + * + * @pdd: pardata data + * + * Reset controller if the &pardata->pin_reset gpio is set. + */ +void pardata_hw_reset(struct pardata_data *pdd) +{ + if (!pdd->pin_reset) + return; + + gpiod_set_value_cansleep(pdd->pin_reset, 0); + usleep_range(20, 1000); + gpiod_set_value_cansleep(pdd->pin_reset, 1); + msleep(120); +} +EXPORT_SYMBOL_GPL(pardata_hw_reset); + +/** + * pardata_poweron_reset - poweron and reset + * @pdd: pardata data + * + * This function enables the regulator if used and does a hardware reset. + * + * Returns: + * Zero on success, or a negative error code. + */ +int pardata_poweron_reset(struct pardata_data *pdd) +{ + int ret; + + + if (pdd->regulator) { + ret = regulator_enable(pdd->regulator); + if (ret) { + DRM_DEV_ERROR(&pdd->pddev->dev, + "Failed to enable regulator (%d)\n", + ret); + return ret; + } + } + + pardata_hw_reset(pdd); + + return 0; +} +EXPORT_SYMBOL_GPL(pardata_poweron_reset); + +MODULE_LICENSE("GPL v2"); diff --git a/include/drm/tinydrm/pardata-dbi.h b/include/drm/tinydrm/pardata-dbi.h new file mode 100644 index 000000000000..d72d9a1dff27 --- /dev/null +++ b/include/drm/tinydrm/pardata-dbi.h @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Parallel Data Display Bus Interface LCD controller support + */ + +#include + +#include + +/** + * pardata_pins - the data pins + * + * The order is important due to use of writing to gpio arrays. + * The first four pins (PIN_DB0 to PIN_DB3) are not used for + * interfaces using only four data bits. + */ +enum pardata_pins { + PIN_DB0, + PIN_DB1, + PIN_DB2, + PIN_DB3, + PIN_DB4, + PIN_DB5, + PIN_DB6, + PIN_DB7, + PIN_NUM, /* Number of pins */ +}; + +struct pardata_data { + /** + * @tinydrm - tinydrm base + */ + struct tinydrm_device tinydrm; + + /** + * dev - pardata device + */ + struct pardata_device *pddev; + + /** + * @busdata - data for the bus + */ + struct pardatabus_data *bus; + + /** + * @pin_reset: Optional GPIO reset pin + */ + struct gpio_desc *pin_reset; + + /** + * @pin_cs: Optional chip select pin + */ + struct gpio_desc *pin_cs; + + /** + * @tx_buf: Poitner to buffer to transer to display RAM + */ + u8 *tx_buf; + + /** + * @backlight - backlight device (optional) + */ + struct backlight_device *backlight; + + /** + * @regulator - power regulator (optional) + */ + struct regulator *regulator; + + /** + * @strobe_write - activate chip select to move data to controller + * + * This callback is used to set read/write and activate + * pin_e and pin_cs - with respect to the timing required by + * the controller. + * + * Use pardata_strobe_8080_write() for 8080 style interface or + * pardata_strobe_6800_write() for 6800 style interface. + * has individual pins for read and write. + * + * Parameters: + * + * pdd: pardata data + */ + void (*strobe_write)(struct pardata_data *pdd); + + /** + * write_reg - write value to register + * + * This callback is used to write the value to the register. + * + * Parameters: + * + * pdd: pardata data + * reg: The register to write + * value: The value to write to the register + * + * Returns: + * 0 on success, negative value on error + */ + int (*write_reg)(struct pardata_data *pdd, + unsigned int reg, unsigned int value); + + /** + * write_buf - write content of buffer to controller + * + * This callback writes the data to the controller at offset + * and forward. The write operation shall be as efficient as + * possible as this will be called every time the content on + * the display changes + * + * Parameters: + * + * pdd: pardata data + * offset: The offset into the display memory of the controller where + * the data shall be written. + * data: Pointer to the data to write to display memory + * len: Number of bytes to write to the display memory + * + * Returns: + * 0 on success, negative value on error + */ + int (*write_buf)(struct pardata_data *pdd, + u8 offset, + u8 *data, + size_t len); + + /** + * @enabled: Pipeline is enabled (internal) + */ + bool enabled; +}; + +static inline struct pardata_data * +pardata_from_tinydrm(struct tinydrm_device *tdev) +{ + return container_of(tdev, struct pardata_data, tinydrm); +} + +static inline void pardata_strobe_write(struct pardata_data *pdd) +{ + if (pdd && pdd->strobe_write) + pdd->strobe_write(pdd); + else + DRM_DEV_ERROR(&pdd->pddev->dev, "No strobe_write callback"); +} + +static inline int pardata_write_reg(struct pardata_data *pdd, + unsigned int reg, unsigned int value) +{ + if (pdd && pdd->write_reg) + pdd->write_reg(pdd, reg, value); + else + DRM_DEV_ERROR(&pdd->pddev->dev, "No write_reg callback"); + + return 0; +} + +static inline int pardata_write_buf(struct pardata_data *pdd, + u8 offset, u8 *data, size_t len) +{ + if (pdd && pdd->write_buf) + pdd->write_buf(pdd, offset, data, len); + else + DRM_DEV_ERROR(&pdd->pddev->dev, "No write_buf callback"); + + return 0; +} + +/** + * pardata_strobe_8080_write - using the 8080 interface create + * an enable strobe + * + * The interface requires that readwrite and chipselect are + * setup before the strobe of "e". + * There are timings constraints that is handled using udelay(). + * Drivers can use this for the strobe_write callback. + * + * @pdd: pardata data + */ +void pardata_strobe_8080_write(struct pardata_data *pdd); + +/** + * pardata_strobe_6800_write - using the 6800 interface create + * an enable strobe + * + * The interface requires that read + write and chipselect are + * setup before the strobe of "e". + * There are timings constraints that is handled using udelay() + * Drivers can use this for the strobe_write callback. + * + * @pdd: pardata data + */ +void pardata_strobe_6800_write(struct pardata_data *pdd); + +/** + * pardata_poweron_reset - poweron and reset display + * + * @pdd: pardata data + * + * This function enables the regulator if used and does a hardware reset. + * Returns: + * Zero on success, or a negative error code. + */ +int pardata_poweron_reset(struct pardata_data *pdd); + +/** + * pardata_enable_flush - enable helper + * + * @pdd: parDATA DATA + * @crtc_state: crtc state + * @plane_state: plane state + * + * This function sets &pardata->enabled, flushes the whole framebuffer and + * enables the backlight. Drivers can use this in their + * &drm_simple_display_pipe_funcs->enable callback. + */ +void pardata_enable_flush(struct pardata_data *pdd, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state); + +/** + * pardata_pipe_disable - pipe disable helper + * + * @pipe: Display pipe + * + * This function disables backlight if present, if not the display memory is + * blanked. The regulator is disabled if in use. Drivers can use this as their + * &drm_simple_display_pipe_funcs->disable callback. + */ +void pardata_pipe_disable(struct drm_simple_display_pipe *pipe); + +/** + * pardata_init - initialization + * + * @dev: Parent device + * @pdd: pardata data to initialize + * @pipe_funcs: Display pipe functions + * @driver: DRM driver + * @mode: Display mode + * + * This function initializes a &pardata data structure and it's underlying + * @tinydrm_device. It also sets up the display pipeline. + * + * Supported formats: Emulated XRGB8888. + * + * Objects created by this function will be automatically freed on driver + * detach (devres). + * + * Returns: + * Zero on success, negative error code on failure. + */ +int pardata_init(struct device *dev, + struct pardata_data *pdd, + const struct drm_simple_display_pipe_funcs *pipe_funcs, + struct drm_driver *driver, + const struct drm_display_mode *mode); From patchwork Thu Aug 2 19:45:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Ravnborg X-Patchwork-Id: 10554641 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BBF8F13BB for ; Fri, 3 Aug 2018 07:15:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A4CDA2C451 for ; Fri, 3 Aug 2018 07:15:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 990EC2C578; Fri, 3 Aug 2018 07:15:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 52A532C451 for ; Fri, 3 Aug 2018 07:15:59 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C44796E6AD; Fri, 3 Aug 2018 07:14:06 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) by gabe.freedesktop.org (Postfix) with ESMTPS id 978FC6E63A for ; Thu, 2 Aug 2018 19:45:58 +0000 (UTC) Received: by mail-ed1-x542.google.com with SMTP id s16-v6so1328393edq.12 for ; Thu, 02 Aug 2018 12:45:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=56FAvYozzzawTLAmWNQaS/ylLi9OPb10xlYe9IJORGQ=; b=unwwxgmna6Oy4n73rEbNkBVcMzotk4d+I69Zf/eipUEDGN0Q20M91CZvE+TwTci+tK i36DiOk6vI7hqKz4YDjXbZ9EE7vWbuIqo1mU4xqDKeonuvIsXn2ApEBQ4+/oL/D/4eRP kGmHUsLh5qz9XTUErAsG+HxI2PQ/h3VbDXkJd3vLOEKfgWkdTyIXxqNihJdp0dqWhK4T kXDf0ntCQdjCmtEdF6d3cyTdJ2OxAJ1jJsXxH9peUVIUBOXFz2aJOdF1JCkKj5iKJhDq LE3PvSwpjl0vC3zq3rUkIJy94ahjno4IIGlYddwZP0cDU6mJSUKuJ89eU2/bKhS0S5Yh AFSQ== X-Gm-Message-State: AOUpUlHAjeFocvW1hYFH4kwWC4b4udW3JrFRMwCyG2JDcwpRWThK/qKA 8u2nKDoXAwLgWuJACUWghUE= X-Google-Smtp-Source: AAOMgpfdcUOrCHwoD+q2bpSKPs7uFMDY2/LZ8lRq7YeHagqIElc+eilSsvEcwekbZxH+6kC17q9bJA== X-Received: by 2002:a50:ec0b:: with SMTP id g11-v6mr4662180edr.38.1533239157284; Thu, 02 Aug 2018 12:45:57 -0700 (PDT) Received: from jupiter.lan (126.158-248-196.customer.lyse.net. [158.248.196.126]) by smtp.gmail.com with ESMTPSA id g6-v6sm1219655edn.28.2018.08.02.12.45.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Aug 2018 12:45:56 -0700 (PDT) From: Sam Ravnborg To: Noralf T , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [PATCH v1 4/5] dt-bindings: add winstar,wg160160 display bindings Date: Thu, 2 Aug 2018 21:45:35 +0200 Message-Id: <20180802194536.10820-4-sam@ravnborg.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20180802193909.GA11443@ravnborg.org> References: <20180802193909.GA11443@ravnborg.org> X-Mailman-Approved-At: Fri, 03 Aug 2018 07:13:56 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Ravnborg MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The winstar display uses the pardata binding. Signed-off-by: Sam Ravnborg --- .../bindings/display/winstar,wg160160.txt | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/winstar,wg160160.txt diff --git a/Documentation/devicetree/bindings/display/winstar,wg160160.txt b/Documentation/devicetree/bindings/display/winstar,wg160160.txt new file mode 100644 index 000000000000..893065d558c1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/winstar,wg160160.txt @@ -0,0 +1,53 @@ +DT binding for Winstar WG160160 + +This binding is for the Winstar WG160160 display using a RA8822 controller + +Required properties: +- compatible: "winstar,wg160160" +- reg: numeric identifier for the display + +The node for this driver must be a child node of a parallel-data-bus, hence +all mandatory properties described in ../pardata/parallel-data-bus.txt +must be specified. + +Optional properties: +- chipselect-gpios: GPIO used for chipselect of the display +- reset-gpios: GPIO that can be used to do a hardware reset of the display +- backlight: phandle of the backlight device attached to the panel +- regulator: phandle of the regulator that provides the supply voltage + +Example: + + backlight: backlight { + compatible = "gpio-backlight"; + } + + power: regulator@0 { + } + + pardatabus { + compatible = "parallel-data-bus"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pardata>; + data-gpios = <&pioe 0 GPIO_ACTIVE_HIGH + &pioe 1 GPIO_ACTIVE_HIGH + &pioe 2 GPIO_ACTIVE_HIGH + &pioe 3 GPIO_ACTIVE_HIGH + &pioe 4 GPIO_ACTIVE_HIGH + &pioe 5 GPIO_ACTIVE_HIGH + &pioe 6 GPIO_ACTIVE_HIGH + &pioe 7 GPIO_ACTIVE_HIGH>; + enable-gpios = <&pioe 8 GPIO_ACTIVE_HIGH>; + rs-gpios = <&pioe 12 GPIO_ACTIVE_HIGH>; + readdwrite-gpios = <&pioe 11 GPIO_ACTIVE_HIGH>; + + wg160160@0 { + compatible = "winstar,wg160160"; + reg = <1>; + reset-gpios = <&pioe 13 GPIO_ACTIVE_LOW>; + chipselect-gpios = <&pioe 9 GPIO_ACTIVE_LOW>; + backlight = &backlight; + power = &power; + } + } + From patchwork Thu Aug 2 19:45:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Ravnborg X-Patchwork-Id: 10554633 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C3F68157D for ; Fri, 3 Aug 2018 07:15:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id ABAE42C451 for ; Fri, 3 Aug 2018 07:15:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9FAB62C578; Fri, 3 Aug 2018 07:15:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id F0D752C451 for ; Fri, 3 Aug 2018 07:15:05 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 35B5D6E6A4; Fri, 3 Aug 2018 07:14:05 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-ed1-x543.google.com (mail-ed1-x543.google.com [IPv6:2a00:1450:4864:20::543]) by gabe.freedesktop.org (Postfix) with ESMTPS id 50D6F88D39 for ; Thu, 2 Aug 2018 19:46:01 +0000 (UTC) Received: by mail-ed1-x543.google.com with SMTP id o8-v6so1324757edt.13 for ; Thu, 02 Aug 2018 12:46:01 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=x+ldX8WC7G5P7vlIlapCAkFKnlhXy03RAaF+I6SAEiE=; b=fT57TzOyevHK0w1JCeifWSl+PypJ6m9HTbXXil/xIznlT/Gw1scZMuoykFj/0EVcLd wp7f0NEvhMPa6mmW1+YYmozIdcTe3EO2PwMJQZ4SaaXOjAoM8sYtnEKPGCEVDUaAuqmN EunyxrXWZU6IJ8/chqrrzgMJeywvB9Ikj9HFycSleESNuGBscsI18/ORMEai8F1eeHfw syCdWsIhhqqJOh1R1iI+z9Vm6NHFdYLO/oD4miNNuazVVNfkaEwY1tPiGNuaanjApbfR dZfdmtj4TvlaYOUVhKle2dioo3micmELS1ADAIaa/xb0KHki3vnVD7+OWKz93yg76iBv TFXg== X-Gm-Message-State: AOUpUlH9S/UQLKNkeQX0+7lzS5lJFugSgnTrmrD7wJGjMjznjpTMkRjp 9UCyIsEaVL5P03aLeTAvITo= X-Google-Smtp-Source: AAOMgpdy2VwZISIMzYoUuz41YAoW8rPh6LJfMKif4INQHYjHeqN5bSjrckzD5wJRaY7VwS7UnEVO7w== X-Received: by 2002:a50:b8c5:: with SMTP id l63-v6mr4620472ede.80.1533239159828; Thu, 02 Aug 2018 12:45:59 -0700 (PDT) Received: from jupiter.lan (126.158-248-196.customer.lyse.net. [158.248.196.126]) by smtp.gmail.com with ESMTPSA id g6-v6sm1219655edn.28.2018.08.02.12.45.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Aug 2018 12:45:59 -0700 (PDT) From: Sam Ravnborg To: Noralf T , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [PATCH v1 5/5] tinydrm: add winstar wg160160 driver Date: Thu, 2 Aug 2018 21:45:36 +0200 Message-Id: <20180802194536.10820-5-sam@ravnborg.org> X-Mailer: git-send-email 2.12.0 In-Reply-To: <20180802193909.GA11443@ravnborg.org> References: <20180802193909.GA11443@ravnborg.org> X-Mailman-Approved-At: Fri, 03 Aug 2018 07:13:56 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Sam Ravnborg MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add driver for the winstar wg160160 display. The driver utilises pardata-dbi that again utilise the pardata subsystem. Signed-off-by: Sam Ravnborg --- MAINTAINERS | 5 + drivers/gpu/drm/tinydrm/Kconfig | 10 ++ drivers/gpu/drm/tinydrm/Makefile | 1 + drivers/gpu/drm/tinydrm/wg160160.c | 298 +++++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 drivers/gpu/drm/tinydrm/wg160160.c diff --git a/MAINTAINERS b/MAINTAINERS index 4ba7ff7c3e46..d77e53041395 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15501,6 +15501,11 @@ L: linux-watchdog@vger.kernel.org S: Maintained F: drivers/watchdog/ebc-c384_wdt.c +WINSTAR WG160160 DRIVER +M: Sam Ravnborg +S: Maintained +F: drivers/gpu/drm/tinydrm/wg160160.c + WINSYSTEMS WS16C48 GPIO DRIVER M: William Breathitt Gray L: linux-gpio@vger.kernel.org diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig index 435de2f8d8f5..40315680c0bc 100644 --- a/drivers/gpu/drm/tinydrm/Kconfig +++ b/drivers/gpu/drm/tinydrm/Kconfig @@ -65,3 +65,13 @@ config TINYDRM_ST7735R * JD-T18003-T01 1.8" 128x160 TFT If M is selected the module will be called st7735r. + +config TINYDRM_WG160160 + tristate "DRM support for Winstar WG160160" + depends on DRM_TINYDRM && PARDATA + select TINYDRM_PARDATA_DBI + help + DRM driver for Winstar WG160106. + See https://www.winstar.com.tw/products/graphic-lcd-display-module/lcd-graphics.html + + If M is selected the module will be named wg160160. diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile index 0b52df08b0a4..849891fe40cb 100644 --- a/drivers/gpu/drm/tinydrm/Makefile +++ b/drivers/gpu/drm/tinydrm/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o obj-$(CONFIG_TINYDRM_ST7586) += st7586.o obj-$(CONFIG_TINYDRM_ST7735R) += st7735r.o +obj-$(CONFIG_TINYDRM_WG160160) += wg160160.o diff --git a/drivers/gpu/drm/tinydrm/wg160160.c b/drivers/gpu/drm/tinydrm/wg160160.c new file mode 100644 index 000000000000..5477c8ed5599 --- /dev/null +++ b/drivers/gpu/drm/tinydrm/wg160160.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DRM driver for Winstar WG160160 panels + * + * Copyright 2018 Sam Ravnborg + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define WG160160_MODE_REG 0x00 +#define MODE_GRAPHIC_DISP_OFF 0x12 +#define MODE_GRAPHIC_DISP_ON 0x32 + +#define WG160160_PITCH_REG 0x01 +#define WG160160_WIDTH_REG 0x02 +#define WG160160_DUTY_REG 0x03 +#define WG160160_CURSORPOS_REG 0x04 +#define WG160160_ADDRSL_REG 0x08 /* Start lower address */ +#define WG160160_ADDRSU_REG 0x09 /* Start upper address */ +#define WG160160_WRITE_REG 0x0c /* Write byte, inc cursor */ +#define WG160160_READ_REG 0x0d /* Read byte, inc cursor */ + +#define WG160160_BUSY_MASK 0x80 +#define BUSY_ACTIVE 1 +#define BUSY_INACTIVE 0 + +/** + * busy_status -read status of busy flag + * + * @pdd: pardata data + * + * returns: true if busy, false if not + */ +static bool busy_status(struct pardata_data *pdd) +{ + int data; + + gpiod_set_value_cansleep(pdd->bus->pin_rs, 1); + gpiod_set_value_cansleep(pdd->bus->pin_readwrite, 1); + gpiod_set_value_cansleep(pdd->pin_cs, 0); + /* min 90 nsec from cs/rs/rw to e */ + udelay(1); + gpiod_set_value_cansleep(pdd->bus->pin_enable, 1); + /* data setup time 220 ns*/ + udelay(2); + + data = gpiod_get_value(pdd->bus->data_pins->desc[PIN_DB7]); + + gpiod_set_value_cansleep(pdd->bus->pin_enable, 0); + /* data hold time 20 ns */ + udelay(1); + gpiod_set_value_cansleep(pdd->pin_cs, 1); + + return data == 1; +} + +/** + * wait_busy - wait until controller is no longer busy + * + * Logs an ERROR once if we fail to see a not BUSY condition + * + * @pdd: pardata_data + */ +static void wait_busy(struct pardata_data *pdd) +{ + int i; + + i = 0; + + while (busy_status(pdd) && i++ < 10) + udelay(1); + + if (i >= 10) + DRM_DEV_ERROR_RATELIMITED(&pdd->pddev->dev, + "Timeout waiting for BUSY=0\n"); +} + +/** + * write_reg - Write instruction on parallel bus to controller + * + * Check BUSY flag and write instruction + * + * @pdd: pardata data + * @reg: The register to write + * @value: The value of the register + * + * Returns: + * Zero on success, negative error code on failure + */ +int write_reg(struct pardata_data *pdd, unsigned int reg, unsigned int value) +{ + int ins[PIN_NUM]; + int val[PIN_NUM]; + int i; + + for (i = 0; i < PIN_NUM; i++) + ins[PIN_DB0 + i] = !!BIT(reg); + + for (i = 0; i < PIN_NUM; i++) + val[PIN_DB0 + i] = !!(value & BIT(i)); + + gpiod_set_value_cansleep(pdd->bus->pin_rs, 1); + gpiod_set_array_value_cansleep(PIN_NUM, pdd->bus->data_pins->desc, ins); + wait_busy(pdd); + pardata_strobe_write(pdd); + + gpiod_set_value_cansleep(pdd->bus->pin_rs, 0); + gpiod_set_array_value_cansleep(PIN_NUM, pdd->bus->data_pins->desc, val); + wait_busy(pdd); + pardata_strobe_write(pdd); + + return 0; +} + +/** + * write_buf - write buffer on parallel bus to controller + * + * @pdd: pardata data + * @offset: offset into display RAM + * @data: pointer to data to write + * @len: number of bytes to write + * + * Returns: + * Zero on success, negative error code on failure + */ +int write_buf(struct pardata_data *pdd, u8 offset, u8 *data, size_t len) +{ + int ins[PIN_NUM]; + int val[PIN_NUM]; + int bit; + int i; + + /* Setup address */ + write_reg(pdd, WG160160_ADDRSL_REG, offset & 0xff); + write_reg(pdd, WG160160_ADDRSL_REG, (offset >> 8) & 0xff); + + /* prepare to write data */ + for (i = 0; i < PIN_NUM; i++) + ins[PIN_DB0 + i] = !!(WG160160_WRITE_REG & BIT(i)); + + gpiod_set_value_cansleep(pdd->bus->pin_rs, 1); + gpiod_set_array_value_cansleep(PIN_NUM, pdd->bus->data_pins->desc, ins); + wait_busy(pdd); + pardata_strobe_write(pdd); + + /* Write data byte - by byte */ + gpiod_set_value_cansleep(pdd->bus->pin_rs, 0); + + for (i = offset; i < (offset + len); i++) { + for (bit = 0; bit < PIN_NUM; bit++) + val[PIN_DB0 + bit] = !!(data[i] & BIT(bit)); + + gpiod_set_array_value_cansleep(PIN_NUM, + pdd->bus->data_pins->desc, + val); + wait_busy(pdd); + pardata_strobe_write(pdd); + } + + return 0; +} + +static void wg160160_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct tinydrm_device *tdev = pipe_to_tinydrm(pipe); + struct pardata_data *pdd = pardata_from_tinydrm(tdev); + int ret; + + ret = pardata_poweron_reset(pdd); + if (ret) + return; + + /* Init quence for WG160160 display */ + + /* Graphics mode, Master */ + pardata_write_reg(pdd, WG160160_MODE_REG, MODE_GRAPHIC_DISP_ON); + /* Set PITCH to 8 bits/bytes */ + pardata_write_reg(pdd, WG160160_PITCH_REG, 0x7); + /* Duty cycle is the vertical resolution */ + pardata_write_reg(pdd, WG160160_DUTY_REG, 160); + + /* Start address in display RAM */ + pardata_write_reg(pdd, WG160160_ADDRSL_REG, 0x0); + pardata_write_reg(pdd, WG160160_ADDRSU_REG, 0x0); + + pardata_enable_flush(pdd, crtc_state, plane_state); +} + +static const struct drm_simple_display_pipe_funcs wg160160_pipe_funcs = { + .enable = wg160160_pipe_enable, + .disable = pardata_pipe_disable, + .update = tinydrm_display_pipe_update, + .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, +}; + +static const struct drm_display_mode wg160160_mode = { + TINYDRM_MODE(160, 160, 62, 62), +}; + +DEFINE_DRM_GEM_CMA_FOPS(wg160160_fops); + +static struct drm_driver wg160160_drm_driver = { + .driver_features = DRIVER_GEM | + DRIVER_MODESET | + DRIVER_PRIME | + DRIVER_ATOMIC, + .fops = &wg160160_fops, + TINYDRM_GEM_DRIVER_OPS, + .lastclose = drm_fb_helper_lastclose, + .name = "wg160160", + .desc = "Winstar WG160160", + .date = "20180808", + .major = 1, + .minor = 0, +}; + +static const struct of_device_id wg160160_of_match[] = { + { .compatible = "winstar,wg160160" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, wg160160_of_match); + +static struct pardata_driver wg160160_pardata_driver; + +static int wg160160_probe(struct pardata_device *pddev) +{ + struct device *dev = &pddev->dev; + struct pardata_data *pdd; + int ret; + + pdd = devm_kzalloc(dev, sizeof(*pdd), GFP_KERNEL); + if (!pdd) + return -ENOMEM; + + /* Find all pins */ + pdd->pin_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pdd->pin_reset)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); + return PTR_ERR(pdd->pin_reset); + } + + pdd->pin_cs = devm_gpiod_get(dev, "cs", GPIOD_OUT_HIGH); + if (IS_ERR(pdd->pin_cs)) { + DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n"); + return PTR_ERR(pdd->pin_cs); + } + + pdd->backlight = devm_of_find_backlight(dev); + if (IS_ERR(pdd->backlight)) + return PTR_ERR(pdd->backlight); + + pdd->strobe_write = pardata_strobe_8080_write; + pdd->write_reg = write_reg; + pdd->write_buf = write_buf; + + ret = pardata_init(dev, pdd, &wg160160_pipe_funcs, + &wg160160_drm_driver, &wg160160_mode); + if (ret) + return ret; + + pardata_dev_set_drvdata(pddev, pdd); + + return devm_tinydrm_register(&pdd->tinydrm); +} + +static void wg160160_shutdown(struct pardata_device *pddev) +{ + struct pardata_data *dpp = pardata_dev_get_drvdata(pddev); + + tinydrm_shutdown(&dpp->tinydrm); +} + +static struct pardata_driver wg160160_pardata_driver = { + .driver = { + .name = "wg160160", + .owner = THIS_MODULE, + .of_match_table = wg160160_of_match, + }, + .probe = wg160160_probe, + .shutdown = wg160160_shutdown, +}; +module_pardata_driver(wg160160_pardata_driver); + +MODULE_DESCRIPTION("Winstar WG160160 DRM driver"); +MODULE_AUTHOR("Sam Ravnborg "); +MODULE_LICENSE("GPL v2");