From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.218.45 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793287776; b=JRdQu3DqtFiswwM6kxzBYD2vbLPpusms5sYCfZ92kjc4+EYVd68VQr+X2mfMSQ2HBBYu MHMQPpVsFX+JQgKTbkN0C3D9VoIKudNRFNniEqNgC6i3GaJdN2DeGblXY3VIIH9e6F7zj t9mqEO8pfHLbf9uFsApAn/rmfWHcnwgzuqjH9a44rYlDNPWXy6h5+rsWUTfyvHHNpI2Yv 0JrMsYnw/ksyl+An3M2E5Y1VfWrrrAgYpEMrSv2mAYs5pzbLLG5CDcnGlLXkwLeDl1IoY b8Y6YyLOaphS+ehvNPYR9wDmhbxlUgE50HDctCMIcPrwfLs8IIcKX9+gjTGpdJnYs8A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793287776; 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=Ch3qpm6uuwG5LsJna72YzE2rF2Y6s+0sX2QasmrDSW8NebEDhfX99xdYxdpDjpLsyFiz DkEVRMpDflF+SP2H/D2WZ07YWh9FNMaAxm/Cfh2SUu80hPd0U5bp6vwfsbChbIQTyaIoE l0i9yMBl4aFlpbe2RA1G3/Zt3vv2ZTqRoSIoKVYC8MxQ25ZxgpT7oWtBj4vvlZj4HH0hJ g9cgHbszMnfVi/VQea4o2HkVXpm4d147mcHV2mw/HQIaQbLpKXIflayG9tOfQ6KHdFcoU Eoe+USQC2QGpKcZdMD5jUAD3hU62OQIZp1CRGskJJMfRW1kCvqtWd8zl1Ay3TYYKbCw== 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.45 Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) (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 17BD5606A31E for ; Thu, 29 Oct 2026 15:29:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 17BD5606A31E 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-f45.google.com with SMTP id a640c23a62f3a-c418e215671so301740666b.0 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=TS5BM0tqN7UDdnihyc7wXL1HiwBzpGD02DkxEshFMyCoGYbfMFjwN70nMDesNzmcI8 AvBAccWly9d0fgY0rbrM45tGkGcWOHi8f3DMNmAsF/o5tiYZROZLFRLEtRdGKODtirHV IFyCTaRtxVdAA75sVV5Dpq4/inw4HZc4RN8YgqOBbUzw5YyPOJLyxFk8r2euncaZ/g7M yzvCCfKnfKzRAxQ+84lgx4YJSZypvd4sA8/e6G3d3ehAgySTfxjKu81pwGDom45ggaeA B1NnChAlctp1PUO4NY1BXFvUBasVX3WdyeahdiSx/K96vvRaW54L1L/Ms9Mw8EwPo8Gk EQgw== X-Forwarded-Encrypted: i=1; AKwUvBzcyF5+gElp6YKNXc1P6jMXXjqlkuKIP7aYc0Jw5f8X0rr0P5FoMgRyLqMNR8Z3XRCGK5B+RmbEk2gbPg==@vger.kernel.org X-Gm-Message-State: AFq9FYKjhwHKxp84RvghlJUjoWRR9LNlYHWGY0qNgGLsEq9Q7/NVYn0L uyVktM5wodk70K0Q6NULW2545fdhHk82PxfmUs5CDrTXD/4ogOzK2hzU X-Gm-Gg: AYBFou1zQD/ucA2RJdp3otQXEGRmZYBUc7GKUmGweK1ELhLQ7ML82Et/nUImBz52FZH nT2mSu9wo648R7UNlr0ForBYkYokXBG6yXL6bt8tFSagCTtpo2j+LAehT+pPtN9qFP/Ga/YGEh3 WiDVCW/XRFjEx1Z36BLstLFp4Yyx4JXZS2cfng9VbBAT6WPKNRwXVhHt1yLa1ZxB9gVUSEP/vfO LrZSHLVzk2hR838gmBtE0CeU1Vb0EIVYQK4w+QZGuKd6jQGsqMsCIhZTmsehFy5i+YGmpiFnDbm kpLG4WqewU/61VAiJHzCV46kIu2gjOQ7os8PcLm/Zrrh2QuO8Nih1mSdWhLu35TT9261FxQPkZQ G+3yrTEGXjZ42wWrHNosOwZcWbfcOpjLjZCAUlIupZ4V1GbP8F7zrDUCbl9aTiqH/QL1508hEhr 66JFidWtpy88m1TygbqBVUIg/u7xcJOMXoNnsrvSe533GIuyQuwt86dlVsP+4HdaZZ81sAFVmaQ TLM8l3rc271wSOX3cikaqwjnhJdipP62KQ7j7POJFOx1UUtdGiD3yIW9yDTmcI1+C8xv4zBvo0N xk06wLaKntlgZ4Ky1LzobGzzrYe02Le56mCS7aSVDGywOHP5Ag== 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-tegra@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