From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.218.47 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793287775; b=SzAmZfh15h82Pb8wY8UX4xGHmc6gaECntoLkfZSYHV0v4Dsy/4CDBKQE5knuYGa/vyYU GMX7uaagcJdnrV5I2Kc3YmgDirTCPXqO2gEBea72bEZ7sH0ORvij7jRIdU8gHhtPhg5b+ MfCGACWVnll2gzdbgEyM0FjLAX0v5/Zxps++s28MM8lP2yIJ/2FbsezFdWjT2nS8xaZW8 ZrDYFjCJeZiXVw3eK+DwcDOrIUYsCK7V34dgedPOFZcJ3l5EzEAjD7G3sKambGPJNmVt4 QL0iT0yLakbuv0JUFy2fwv3aRY4Q7tR8TF9n95kDUCmGTLxAPEhRAorPhdOaBwURL9w== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793287775; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:To:Cc:Subject:Date:Message-ID:X-Mailer:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding; bh=5p+lvZSKx4OvMfgb3ZIACVMr3fIQoua1cT9i4xAtcVo=; b=m9kqFYIqUwS2nQu/uBQz0DM7Jkqknw4skHQLREuIlcvu7WoPG69w1d8/9obIuSjXa/Ce 1N5vu1qLuk3iqzWW6u7t5t1MeiSXpeGsYynPuKPz46pqSzEC+COi9IDZRx2ZpkfUYGcC/ 2+dUeichwHW7IysYGyZ26HgXlRS2lHNv8PXgIqyM6I8To0FyI3T7DRRCBTGH5srU/cOrR 4hrB1r1hNW0SDO5DFNXl3Ih+ZNEr9N2HFLTioL1Jq2w0OPGSRxeDqGSLI/2KEI2iae0wJ berk2c7w61CsTrPSgp1kFDR1309J/qdkPlZvz5z78uTOjn0XTrXAOv0+8OqQHuVrazw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=k0Wh9hkm; arc=none smtp.remote-ip=209.85.218.47 Received: from mail-ej1-f47.google.com (mail-ej1-f47.google.com [209.85.218.47]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DBC7606A32C for ; Thu, 29 Oct 2026 15:29:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 1DBC7606A32C Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=k0Wh9hkm Received: by mail-ej1-f47.google.com with SMTP id a640c23a62f3a-c423b145922so328928266b.3 for ; Thu, 29 Oct 2026 08:29:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1793287773; x=1793892573; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5p+lvZSKx4OvMfgb3ZIACVMr3fIQoua1cT9i4xAtcVo=; b=k0Wh9hkm+iPlSOI3nIHN7Dd1ogP4BU2u2QdOsBmV1G0NuYZs2q48Iag90UG0ZpXoRe 9z+e2zcR92d0hsG7mfpqHBOfvLucAYNesMPwpLzqABvvUtzOLZklzaA7+v9QfDbIBAe+ oWcTgGPhfhT+/SNzl/HGudvS/+224+U1ue0ILggh8/pZy0C7s5OijWTEkpaINDg0ZgWA 3o6GRZiaUUhU5lhZI04StywuFvVQzR80D2BBMluAqS07cw25ThQUev09a3ZDtJPaj8HM jQ4SG1oKo0OjEEJGtSJXUtCEJy8pSLshvrcYLIZFNkprybARq2llL0CwctoHH7hMo3ol YmJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793287773; x=1793892573; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=5p+lvZSKx4OvMfgb3ZIACVMr3fIQoua1cT9i4xAtcVo=; b=vWi2x66DkWH6ZaY8abHK176F/iISALh+2URiAgKl9pB8NAAht1yjSzNDmwYVZIYeB9 9YqDphJ1tT8yHdZ6OzpMaCnSbQZlj3JHJo2Mn9O9YwxjzdQITm0en1KcSDom2NAYlALB Z8gLX22NbwrXkzKuBBEWbjLwbDy6Zrs8bBa/lppN8PZ9SkGdKKf+3hyARcvd3hkcq+5i 1f97Xm/3j8iQSldODZuUUY+LexsQANs0SyoL1AAcIqaBrAvXyi+D7EA6gLcXxImikFM3 9kKlIU0cQtN/bpUykRxwxVa8nJkkOve7h0wM1up5FUCap3DCIrEWiSUTFQpZs4XQmJCe fnIg== X-Forwarded-Encrypted: i=1; AKwUvBz3k0qRAUOWwuVQDnO2LM0hX4IxF+zF48rWqWjad5IWlE+0IoXGYbb3aDEaoaOxGoS3rIv92Q6MaGJIa74=@vger.kernel.org X-Gm-Message-State: AFq9FYLvYfiVXt+LC55PPXXpOwKPkcFr22JV8jQ9r6gKhRelyQbhxX8I IlR/MhWaTWN66dteIXD8hmSqRM/YYddM/vDellg/kwDxGXFgQB4YV5B5 X-Gm-Gg: AYBFou1zZu2waSNw25T8l9est/r731SsuAa1Gxshy+YiRqru6tWronnT9tfIpLfxbwX YW6JkH7sDGNgGxBcKdd0nFJKVEdT//HVrfVmlNiF34ujzYE1JTkha/kHJ1aY3XSWXJIMmIg3c2f ABNFPJfwLbYJBy5sDlzfDmGk88kFgK3s662M/5iUCeehZlzRN3KUYYQ79Eomz4dklR6L/LtPdmN i/3sBZ8PcaPe1nRvP9y/V0o7Vql1IIxpHsL+Q0viyNfA6Um4nV8Zg+KD7/GSDtmltLz+pqYcqMp zbbwr+7dWMKvscC7+NhA2DUrzUbHEDuH9uHMsAaPEQHWewEOB0nlbHCcRcpqIeXIp4D1J6OHEsa YDn067ONj4DEnSrSE3A6JfIeAQwBPtprLvszepV/uGWFekdPW6cbZ1yzgUjER1d54LNuBhQYPcL nOe1V+HBewGw0fe8jNh+ewSpUUR+Pht3IJNH5EUHm7S1X1jGj4VrygOTQKyZc+6CKchP8+NRgB/ FRGgLXQgXS5gaioUg+RJt/HbJOzxBrR93RM/+9q97JYjYlAEj3ZmcPNAiJpt5z0uAIEF54yYw6m mnxp6ZPbsNGs+KAedPgzt6tO4VIfhYWRh9q9kqCmBa0d+M9gJA== X-Received: by 2002:a17:907:1b15:b0:c37:c0dd:6642 with SMTP id a640c23a62f3a-c49bdf39b8emr489279566b.37.1793287773105; Thu, 29 Oct 2026 08:29:33 -0700 (PDT) Received: from xeon ([188.163.112.62]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c4ca27984f8sm1252166b.80.2026.10.29.08.29.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2026 08:29:32 -0700 (PDT) From: Svyatoslav Ryhel To: Neil Armstrong , Jessica Zhang , David Airlie , Simona Vetter , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Thierry Reding , Jonathan Hunter , Mikko Perttunen , Svyatoslav Ryhel Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org Subject: [PATCH v5 01/11] drm: Add MIPI DBI bus support Date: Thu, 29 Oct 2026 17:28:59 +0200 Message-ID: <20261029152909.234584-2-clamor95@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261029152909.234584-1-clamor95@gmail.com> References: <20261029152909.234584-1-clamor95@gmail.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Mikko Perttunen The MIPI DBI bus allows modeling DBI hosts and peripherals using the Linux driver model. DBI hosts are registered by their respective drivers. During registration, DBI peripherals are created from the device tree nodes provided by the host. DBI hosts expose operations that can be used by peripheral drivers to access associated devices. The original code was authored by Mikko Perttunen; I have aligned it with the existing MIPI DSI implementation. Co-developed-by: Svyatoslav Ryhel Signed-off-by: Svyatoslav Ryhel --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_mipi_dbi_bus.c | 232 +++++++++++++++++++++++++++++ include/drm/drm_mipi_dbi_bus.h | 103 +++++++++++++ 3 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_mipi_dbi_bus.c create mode 100644 include/drm/drm_mipi_dbi_bus.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2c6bc093a923a..880f0dc930b85 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -166,7 +166,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o obj-y += tests/ -obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o +obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o drm_mipi_dbi_bus.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o obj-y += arm/ obj-y += clients/ diff --git a/drivers/gpu/drm/drm_mipi_dbi_bus.c b/drivers/gpu/drm/drm_mipi_dbi_bus.c new file mode 100644 index 0000000000000..afad1f1508064 --- /dev/null +++ b/drivers/gpu/drm/drm_mipi_dbi_bus.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MIPI DBI devices with command transport through parallel interface + * + * Copyright 2026 Mikko Perttunen + * Copyright 2026 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static int mipi_dbi_match(struct device *dev, const struct device_driver *driver) +{ + return of_driver_match_device(dev, driver); +} + +static int mipi_dbi_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + return of_device_uevent_modalias(dev, env); +} + +static const struct dev_pm_ops mipi_dbi_device_pm_ops = { + .runtime_suspend = pm_generic_runtime_suspend, + .runtime_resume = pm_generic_runtime_resume, + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, +}; + +static const struct bus_type mipi_dbi_bus_type = { + .name = "mipi-dbi", + .match = mipi_dbi_match, + .uevent = mipi_dbi_uevent, + .pm = &mipi_dbi_device_pm_ops, +}; + +static void mipi_dbi_device_release(struct device *dev) +{ + of_node_put(dev->of_node); + kfree(to_mipi_dbi_device(dev)); +} + +static void mipi_dbi_device_unregister(void *data) +{ + struct device *dev = data; + + of_node_clear_flag(dev->of_node, OF_POPULATED); + device_unregister(dev); +} + +/** + * devm_mipi_dbi_device_register() - create a managed MIPI DBI device + * @dev: device to tie the MIPI-DBI device lifetime to + * @host: DBI host to which this device is connected + * @np: device tree node associated with the panel MIPI DBI device + * + * This function is called by DBI host driver to create panel device. + * It automatically calls mipi_dbi_device_unregister() when @dev is + * unbound. It does not automatically free passed device node pointer, + * it must be put manually. + * + * Returns: + * A pointer to the newly created MIPI DBI device, or, a pointer encoded + * with an error. + */ +struct mipi_dbi_device * +devm_mipi_dbi_device_register(struct device *dev, struct mipi_dbi_host *host, + struct device_node *np) +{ + struct mipi_dbi_device *dbidev; + int ret; + + if (!host || !host->dev || !host->dbi || !host->dbi->command || + !host->ops || !host->ops->attach || !host->ops->detach || !np) + return ERR_PTR(-EINVAL); + + if (!of_device_is_available(np)) + return ERR_PTR(-ENODEV); + + dbidev = kzalloc_obj(*dbidev); + if (!dbidev) + return ERR_PTR(-ENOMEM); + + if (of_node_test_and_set_flag(np, OF_POPULATED)) { + kfree(dbidev); + return ERR_PTR(-EBUSY); + } + + dbidev->host = host; + dbidev->dbi = host->dbi; + device_initialize(&dbidev->dev); + dbidev->dev.parent = host->dev; + dbidev->dev.bus = &mipi_dbi_bus_type; + dbidev->dev.release = mipi_dbi_device_release; + device_set_node(&dbidev->dev, of_fwnode_handle(of_node_get(np))); + + ret = dev_set_name(&dbidev->dev, "%s:%s", dev_name(host->dev), np->full_name); + if (ret) + goto put_device; + + ret = device_add(&dbidev->dev); + if (ret) + goto put_device; + + ret = devm_add_action_or_reset(dev, mipi_dbi_device_unregister, + &dbidev->dev); + if (ret) + return ERR_PTR(ret); + + return dbidev; + +put_device: + of_node_clear_flag(np, OF_POPULATED); + put_device(&dbidev->dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(devm_mipi_dbi_device_register); + +static void mipi_dbi_detach(void *data) +{ + struct mipi_dbi_device *dbidev = data; + + dbidev->host->ops->detach(dbidev->host, dbidev); + dbidev->attached = false; +} + +/** + * devm_mipi_dbi_attach() - managed attach of DBI device to its DBI host + * @dev: device to tie the MIPI-DBI device lifetime to + * @dbidev: DBI peripheral + */ +int devm_mipi_dbi_attach(struct device *dev, struct mipi_dbi_device *dbidev) +{ + int ret; + + if (dbidev->attached) + return -EBUSY; + + ret = dbidev->host->ops->attach(dbidev->host, dbidev); + if (ret) + return ret; + + dbidev->attached = true; + return devm_add_action_or_reset(dev, mipi_dbi_detach, dbidev); +} +EXPORT_SYMBOL_GPL(devm_mipi_dbi_attach); + +static int mipi_dbi_drv_probe(struct device *dev) +{ + struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver); + struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev); + + return drv->probe(dbidev); +} + +static int mipi_dbi_drv_remove(struct device *dev) +{ + struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver); + struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev); + + drv->remove(dbidev); + + return 0; +} + +static void mipi_dbi_drv_shutdown(struct device *dev) +{ + struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver); + struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev); + + drv->shutdown(dbidev); +} + +/** + * __mipi_dbi_driver_register() - register a driver for DBI devices + * @drv: DBI driver structure + * @owner: owner module + * + * Return: 0 on success or a negative error code on failure. + */ +int __mipi_dbi_driver_register(struct mipi_dbi_driver *drv, struct module *owner) +{ + drv->driver.bus = &mipi_dbi_bus_type; + drv->driver.owner = owner; + + if (drv->probe) + drv->driver.probe = mipi_dbi_drv_probe; + if (drv->remove) + drv->driver.remove = mipi_dbi_drv_remove; + if (drv->shutdown) + drv->driver.shutdown = mipi_dbi_drv_shutdown; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__mipi_dbi_driver_register); + +/** + * mipi_dbi_driver_unregister() - unregister a driver for DBI devices + * @drv: DBI driver structure + * + * Return: 0 on success or a negative error code on failure. + */ +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister); + +static int __init mipi_dbi_bus_init(void) +{ + return bus_register(&mipi_dbi_bus_type); +} +subsys_initcall(mipi_dbi_bus_init); + +static void __exit mipi_dbi_bus_exit(void) +{ + bus_unregister(&mipi_dbi_bus_type); +} +module_exit(mipi_dbi_bus_exit); + +MODULE_DESCRIPTION("MIPI DBI devices with command transport through parallel interface"); +MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_mipi_dbi_bus.h b/include/drm/drm_mipi_dbi_bus.h new file mode 100644 index 0000000000000..2b8b59bffd0b8 --- /dev/null +++ b/include/drm/drm_mipi_dbi_bus.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * MIPI DBI devices with command transport through parallel interface + * + * Copyright 2026 Mikko Perttunen + * Copyright 2026 Svyatoslav Ryhel + */ + +#ifndef __DRM_MIPI_DBI_BUS_H__ +#define __DRM_MIPI_DBI_BUS_H__ + +#include +#include + +struct device_node; +struct mipi_dbi; +struct mipi_dbi_device; +struct mipi_dbi_host; + +/** + * struct mipi_dbi_host_ops - DBI bus operations + * @attach: attach DBI device to DBI host + * @detach: detach DBI device from DBI host + */ +struct mipi_dbi_host_ops { + int (*attach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev); + void (*detach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev); +}; + +/** + * struct mipi_dbi_host - DBI host device + * @dev: driver model device node for this DBI host + * @dbi: MIPI DBI interface + * @ops: DBI host operations + */ +struct mipi_dbi_host { + struct device *dev; + struct mipi_dbi *dbi; + const struct mipi_dbi_host_ops *ops; +}; + +/** + * struct mipi_dbi_device - DBI peripheral device + * @dev: driver model device node for this peripheral + * @dbi: MIPI DBI interface + * @host: DBI host for this peripheral + * @attached: the DBI device has been successfully attached + */ +struct mipi_dbi_device { + struct device dev; + struct mipi_dbi *dbi; + struct mipi_dbi_host *host; + bool attached; +}; + +#define to_mipi_dbi_device(__dev) container_of_const(__dev, struct mipi_dbi_device, dev) + +struct mipi_dbi_device * +devm_mipi_dbi_device_register(struct device *dev, struct mipi_dbi_host *host, + struct device_node *np); +int devm_mipi_dbi_attach(struct device *dev, struct mipi_dbi_device *dbidev); + +/** + * struct mipi_dbi_driver - DBI driver + * @driver: device driver model driver + * @probe: callback for device binding + * @remove: callback for device unbinding + * @shutdown: called at shutdown time to quiesce the device + */ +struct mipi_dbi_driver { + struct device_driver driver; + int (*probe)(struct mipi_dbi_device *dbi); + void (*remove)(struct mipi_dbi_device *dbi); + void (*shutdown)(struct mipi_dbi_device *dbi); +}; + +static inline struct mipi_dbi_driver * +to_mipi_dbi_driver(struct device_driver *driver) +{ + return container_of(driver, struct mipi_dbi_driver, driver); +} + +static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dbi) +{ + return dev_get_drvdata(&dbi->dev); +} + +static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dbi, void *data) +{ + dev_set_drvdata(&dbi->dev, data); +} + +int __mipi_dbi_driver_register(struct mipi_dbi_driver *drv, struct module *owner); +void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv); + +#define mipi_dbi_driver_register(driver) \ + __mipi_dbi_driver_register(driver, THIS_MODULE) + +#define module_mipi_dbi_driver(__mipi_dbi_driver) \ + module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \ + mipi_dbi_driver_unregister) + +#endif /* __DRM_MIPI_DBI_BUS__ */ -- 2.53.0