[PATCH 33/39] drm: bridge: imx: Add i.MX95 LVDS Display Bridge (LDB) driver
Marek Vasut
marek.vasut at mailbox.org
Sat Oct 11 09:51:48 PDT 2025
From: Sandor Yu <Sandor.yu at nxp.com>
Add i.MX95 LVDS Display Bridge (LDB) driver.
Signed-off-by: Sandor Yu <Sandor.yu at nxp.com>
---
Cc: Abel Vesa <abelvesa at kernel.org>
Cc: Conor Dooley <conor+dt at kernel.org>
Cc: Fabio Estevam <festevam at gmail.com>
Cc: Krzysztof Kozlowski <krzk+dt at kernel.org>
Cc: Laurent Pinchart <Laurent.pinchart at ideasonboard.com>
Cc: Liu Ying <victor.liu at nxp.com>
Cc: Lucas Stach <l.stach at pengutronix.de>
Cc: Peng Fan <peng.fan at nxp.com>
Cc: Pengutronix Kernel Team <kernel at pengutronix.de>
Cc: Rob Herring <robh at kernel.org>
Cc: Shawn Guo <shawnguo at kernel.org>
Cc: Thomas Zimmermann <tzimmermann at suse.de>
Cc: devicetree at vger.kernel.org
Cc: dri-devel at lists.freedesktop.org
Cc: imx at lists.linux.dev
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-clk at vger.kernel.org
---
drivers/gpu/drm/bridge/imx/Kconfig | 10 +
drivers/gpu/drm/bridge/imx/Makefile | 1 +
drivers/gpu/drm/bridge/imx/imx-ldb-helper.h | 2 +
drivers/gpu/drm/bridge/imx/imx95-ldb.c | 470 ++++++++++++++++++++
4 files changed, 483 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/imx/imx95-ldb.c
diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig
index 8baa335deac49..350432f77801c 100644
--- a/drivers/gpu/drm/bridge/imx/Kconfig
+++ b/drivers/gpu/drm/bridge/imx/Kconfig
@@ -88,6 +88,16 @@ config DRM_IMX93_MIPI_DSI
Choose this to enable MIPI DSI controller found in Freescale i.MX93
processor.
+config DRM_IMX95_LDB
+ tristate "Freescale i.MX95 LVDS display bridge"
+ depends on OF
+ depends on COMMON_CLK
+ select DRM_IMX_LDB_HELPER
+ select DRM_KMS_HELPER
+ help
+ Choose this to enable the internal LVDS Display Bridge(LDB) found in
+ Freescale i.MX95 processor.
+
config DRM_IMX95_PIXEL_INTERLEAVER
tristate "NXP i.MX95 pixel interleaver"
depends on OF && MFD_SYSCON && COMMON_CLK
diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile
index b6b2e1bc8d4bd..1c78cfba16cad 100644
--- a/drivers/gpu/drm/bridge/imx/Makefile
+++ b/drivers/gpu/drm/bridge/imx/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER) += imx8qxp-pixel-combiner.o
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
obj-$(CONFIG_DRM_IMX93_MIPI_DSI) += imx93-mipi-dsi.o
+obj-$(CONFIG_DRM_IMX95_LDB) += imx95-ldb.o
obj-$(CONFIG_DRM_IMX95_PIXEL_INTERLEAVER) += imx95-pixel-interleaver.o
obj-$(CONFIG_DRM_IMX95_PIXEL_LINK) += imx95-pixel-link.o
diff --git a/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h b/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h
index de187e3269996..55fad97c65d2f 100644
--- a/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h
+++ b/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h
@@ -31,6 +31,8 @@
#define LDB_BIT_MAP_CH1_JEIDA BIT(8)
#define LDB_DI0_VS_POL_ACT_LOW BIT(9)
#define LDB_DI1_VS_POL_ACT_LOW BIT(10)
+#define LDB_DI0_HS_POL_ACT_LOW BIT(13)
+#define LDB_DI1_HS_POL_ACT_LOW BIT(14)
#define MAX_LDB_CHAN_NUM 2
diff --git a/drivers/gpu/drm/bridge/imx/imx95-ldb.c b/drivers/gpu/drm/bridge/imx/imx95-ldb.c
new file mode 100644
index 0000000000000..19e2d71b9c0c7
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx95-ldb.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2023 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+#include "imx-ldb-helper.h"
+
+#define LVDS_PHY_CLK_CTRL 0x00
+#define LVDS_PHY_DIV2 BIT(0)
+
+#define LDB_DI0_HS_POL_ACT_LOW BIT(13)
+#define LDB_DI1_HS_POL_ACT_LOW BIT(14)
+#define LDB_VSYNC_ADJ_EN BIT(19)
+
+#define DRIVER_NAME "imx95-ldb"
+
+struct imx95_ldb_channel {
+ struct ldb_channel base;
+};
+
+struct imx95_ldb {
+ struct ldb base;
+ struct device *dev;
+ struct imx95_ldb_channel *channel[MAX_LDB_CHAN_NUM];
+ struct clk *clk_ch[MAX_LDB_CHAN_NUM];
+ struct clk *clk_di[MAX_LDB_CHAN_NUM];
+ int active_chno;
+};
+
+static inline struct imx95_ldb_channel *
+base_to_imx95_ldb_channel(struct ldb_channel *base)
+{
+ return container_of(base, struct imx95_ldb_channel, base);
+}
+
+static inline struct imx95_ldb *base_to_imx95_ldb(struct ldb *base)
+{
+ return container_of(base, struct imx95_ldb, base);
+}
+
+static int imx95_ldb_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ return ldb_bridge_atomic_check_helper(bridge, bridge_state,
+ crtc_state, conn_state);
+}
+
+static void
+imx95_ldb_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct ldb_channel *ldb_ch = bridge->driver_private;
+ struct ldb *ldb = ldb_ch->ldb;
+ struct imx95_ldb *imx95_ldb = base_to_imx95_ldb(ldb);
+ struct device *dev = imx95_ldb->dev;
+ unsigned long di_clk = adjusted_mode->clock * 1000;
+ bool is_split = ldb_channel_is_split_link(ldb_ch);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ dev_err(dev, "failed to get runtime PM sync: %d\n", ret);
+
+ /* set lvds di clock rate */
+ clk_set_rate(imx95_ldb->clk_di[ldb_ch->chno], di_clk);
+
+ ldb->ldb_ctrl |= LDB_VSYNC_ADJ_EN;
+
+ if (ldb_ch->chno == 0 || is_split) {
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
+ else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ ldb->ldb_ctrl |= LDB_DI0_HS_POL_ACT_LOW;
+ else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI0_HS_POL_ACT_LOW;
+ }
+ if (ldb_ch->chno == 1 || is_split) {
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
+ else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
+
+ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ ldb->ldb_ctrl |= LDB_DI1_HS_POL_ACT_LOW;
+ else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ ldb->ldb_ctrl &= ~LDB_DI1_HS_POL_ACT_LOW;
+ }
+
+ ldb_bridge_mode_set_helper(bridge, mode, adjusted_mode);
+}
+
+static void
+imx95_ldb_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct ldb_channel *ldb_ch = bridge->driver_private;
+ struct ldb *ldb = ldb_ch->ldb;
+ struct imx95_ldb *imx95_ldb = base_to_imx95_ldb(ldb);
+ bool is_split = ldb_channel_is_split_link(ldb_ch);
+
+ clk_prepare_enable(imx95_ldb->clk_ch[ldb_ch->chno]);
+ clk_prepare_enable(imx95_ldb->clk_di[ldb_ch->chno]);
+ if (is_split) {
+ clk_prepare_enable(imx95_ldb->clk_ch[ldb_ch->chno ^ 1]);
+ clk_prepare_enable(imx95_ldb->clk_di[ldb_ch->chno ^ 1]);
+ }
+
+ if (ldb_ch->chno == 0 || is_split) {
+ ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+ ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
+ }
+ if (ldb_ch->chno == 1 || is_split) {
+ ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+ ldb->ldb_ctrl |= is_split ?
+ LDB_CH1_MODE_EN_TO_DI0 : LDB_CH1_MODE_EN_TO_DI1;
+ }
+
+ if (is_split) {
+ /* PHY clock divider 2 */
+ regmap_update_bits(ldb->regmap, LVDS_PHY_CLK_CTRL,
+ LVDS_PHY_DIV2, LVDS_PHY_DIV2);
+ }
+
+ ldb_bridge_enable_helper(bridge);
+}
+
+static void
+imx95_ldb_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct ldb_channel *ldb_ch = bridge->driver_private;
+ struct ldb *ldb = ldb_ch->ldb;
+ struct imx95_ldb *imx95_ldb = base_to_imx95_ldb(ldb);
+ struct device *dev = imx95_ldb->dev;
+ bool is_split = ldb_channel_is_split_link(ldb_ch);
+ int ret;
+
+ ldb_bridge_disable_helper(bridge);
+
+ if (is_split) {
+ /* clean PHY clock divider 2 */
+ regmap_update_bits(ldb->regmap, LVDS_PHY_CLK_CTRL, LVDS_PHY_DIV2, 0);
+ }
+
+ if (is_split) {
+ clk_disable_unprepare(imx95_ldb->clk_di[ldb_ch->chno ^ 1]);
+ clk_disable_unprepare(imx95_ldb->clk_ch[ldb_ch->chno ^ 1]);
+ }
+ clk_disable_unprepare(imx95_ldb->clk_di[ldb_ch->chno]);
+ clk_disable_unprepare(imx95_ldb->clk_ch[ldb_ch->chno]);
+
+ ret = pm_runtime_put(dev);
+ if (ret < 0)
+ dev_err(dev, "failed to put runtime PM: %d\n", ret);
+}
+
+static const u32 imx95_ldb_bus_output_fmts[] = {
+ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+ MEDIA_BUS_FMT_FIXED,
+};
+
+static bool imx95_ldb_bus_output_fmt_supported(u32 fmt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx95_ldb_bus_output_fmts); i++) {
+ if (imx95_ldb_bus_output_fmts[i] == fmt)
+ return true;
+ }
+
+ return false;
+}
+
+static u32 *
+imx95_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ struct drm_display_info *di;
+ const struct drm_format_info *finfo;
+ u32 *input_fmts;
+
+ if (!imx95_ldb_bus_output_fmt_supported(output_fmt))
+ return NULL;
+
+ *num_input_fmts = 1;
+
+ input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ switch (output_fmt) {
+ case MEDIA_BUS_FMT_FIXED:
+ di = &conn_state->connector->display_info;
+
+ /*
+ * Look at the first bus format to determine input format.
+ * Default to MEDIA_BUS_FMT_RGB888_1X36_CPADLO, if no match.
+ */
+ if (di->num_bus_formats) {
+ finfo = drm_format_info(di->bus_formats[0]);
+
+ input_fmts[0] = finfo->depth == 18 ?
+ MEDIA_BUS_FMT_RGB666_1X36_CPADLO :
+ MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+ } else {
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+ }
+ break;
+ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+ case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+ input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+ break;
+ default:
+ kfree(input_fmts);
+ input_fmts = NULL;
+ break;
+ }
+
+ return input_fmts;
+}
+
+static u32 *
+imx95_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ *num_output_fmts = ARRAY_SIZE(imx95_ldb_bus_output_fmts);
+ return kmemdup(imx95_ldb_bus_output_fmts,
+ sizeof(imx95_ldb_bus_output_fmts), GFP_KERNEL);
+}
+
+static enum drm_mode_status
+imx95_ldb_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct ldb_channel *ldb_ch = bridge->driver_private;
+ struct ldb *ldb = ldb_ch->ldb;
+ struct imx95_ldb *imx95_ldb = base_to_imx95_ldb(ldb);
+ bool is_single = ldb_channel_is_single_link(ldb_ch);
+ struct drm_bridge *next_bridge = bridge;
+ unsigned long pixel_clock_rate;
+ unsigned long rounded_rate;
+
+ if (mode->clock > 330000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock > 165000 && is_single)
+ return MODE_CLOCK_HIGH;
+
+ while (drm_bridge_get_next_bridge(next_bridge))
+ next_bridge = drm_bridge_get_next_bridge(next_bridge);
+
+ if ((next_bridge->ops & DRM_BRIDGE_OP_DETECT) &&
+ (next_bridge->ops & DRM_BRIDGE_OP_EDID)) {
+ if (imx95_ldb->clk_di[ldb_ch->chno]) {
+ pixel_clock_rate = mode->clock * 1000;
+ rounded_rate = clk_round_rate(imx95_ldb->clk_di[ldb_ch->chno],
+ pixel_clock_rate);
+ if (rounded_rate != pixel_clock_rate)
+ return MODE_CLOCK_RANGE;
+ }
+ }
+
+ return MODE_OK;
+}
+
+static const struct drm_bridge_funcs imx95_ldb_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .mode_valid = imx95_ldb_bridge_mode_valid,
+ .attach = ldb_bridge_attach_helper,
+ .atomic_check = imx95_ldb_bridge_atomic_check,
+ .mode_set = imx95_ldb_bridge_mode_set,
+ .atomic_enable = imx95_ldb_bridge_atomic_enable,
+ .atomic_disable = imx95_ldb_bridge_atomic_disable,
+ .atomic_get_input_bus_fmts =
+ imx95_ldb_bridge_atomic_get_input_bus_fmts,
+ .atomic_get_output_bus_fmts =
+ imx95_ldb_bridge_atomic_get_output_bus_fmts,
+};
+
+static int imx95_ldb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx95_ldb *imx95_ldb;
+ struct imx95_ldb_channel *imx95_ldb_ch;
+ struct ldb *ldb;
+ struct ldb_channel *ldb_ch;
+ struct device_node *port1, *port2;
+ int pixel_order;
+ int ret, i;
+
+ imx95_ldb = devm_kzalloc(dev, sizeof(*imx95_ldb), GFP_KERNEL);
+ if (!imx95_ldb)
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+ char clk_name_ldb_ch[8], clk_name_ldb_di[8];
+
+ snprintf(clk_name_ldb_ch, sizeof(clk_name_ldb_ch), "ldb_ch%d", i);
+ snprintf(clk_name_ldb_di, sizeof(clk_name_ldb_di), "ldb_di%d", i);
+
+ imx95_ldb->clk_ch[i] = devm_clk_get(dev, clk_name_ldb_ch);
+ if (IS_ERR(imx95_ldb->clk_ch[i]))
+ return dev_err_probe(dev, PTR_ERR(imx95_ldb->clk_ch[i]),
+ "failed to get ldb ch%d clock\n", i);
+
+ imx95_ldb->clk_di[i] = devm_clk_get(dev, clk_name_ldb_di);
+ if (IS_ERR(imx95_ldb->clk_di[i]))
+ return dev_err_probe(dev, PTR_ERR(imx95_ldb->clk_di[i]),
+ "failed to get ldb di%d clock\n", i);
+ }
+
+ imx95_ldb->dev = dev;
+
+ ldb = &imx95_ldb->base;
+ ldb->dev = dev;
+ ldb->ctrl_reg = 0x4;
+ ldb->ldb_ctrl = 0;
+
+ for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+ imx95_ldb->channel[i] =
+ devm_drm_bridge_alloc(dev, struct imx95_ldb_channel,
+ base.bridge, &imx95_ldb_bridge_funcs);
+ if (IS_ERR(imx95_ldb->channel[i]))
+ return PTR_ERR(imx95_ldb->channel[i]);
+ }
+
+ for (i = 0; i < MAX_LDB_CHAN_NUM; i++)
+ ldb->channel[i] = &imx95_ldb->channel[i]->base;
+
+ ret = ldb_init_helper(ldb);
+ if (ret)
+ return ret;
+
+ if (ldb->available_ch_cnt == 0) {
+ dev_dbg(dev, "no available channel\n");
+ return 0;
+ }
+
+ if (ldb->available_ch_cnt == 2) {
+ port1 = of_graph_get_port_by_id(ldb->channel[0]->np, 1);
+ port2 = of_graph_get_port_by_id(ldb->channel[1]->np, 1);
+ pixel_order =
+ drm_of_lvds_get_dual_link_pixel_order(port1, port2);
+ of_node_put(port1);
+ of_node_put(port2);
+
+ if (pixel_order == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS) {
+ dev_err(dev, "invalid dual link pixel order: %d\n", pixel_order);
+ return -EINVAL;
+ }
+
+ imx95_ldb->active_chno = 0;
+ imx95_ldb_ch = imx95_ldb->channel[0];
+ ldb_ch = &imx95_ldb_ch->base;
+ ldb_ch->link_type = pixel_order;
+ } else {
+ for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+ imx95_ldb_ch = imx95_ldb->channel[i];
+ ldb_ch = &imx95_ldb_ch->base;
+
+ if (ldb_ch->is_available) {
+ imx95_ldb->active_chno = ldb_ch->chno;
+ break;
+ }
+ }
+ ldb_ch->link_type = LDB_CH_SINGLE_LINK;
+ }
+
+ ret = ldb_find_next_bridge_helper(ldb);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, imx95_ldb);
+ pm_runtime_enable(dev);
+
+ ldb_add_bridge_helper(ldb);
+
+ return ret;
+}
+
+static void imx95_ldb_remove(struct platform_device *pdev)
+{
+ struct imx95_ldb *imx95_ldb = platform_get_drvdata(pdev);
+ struct ldb *ldb = &imx95_ldb->base;
+
+ ldb_remove_bridge_helper(ldb);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int __maybe_unused imx95_ldb_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused imx95_ldb_runtime_resume(struct device *dev)
+{
+ struct imx95_ldb *imx95_ldb = dev_get_drvdata(dev);
+ struct ldb *ldb = &imx95_ldb->base;
+
+ /* disable LDB by resetting the control register to POR default */
+ regmap_write(ldb->regmap, ldb->ctrl_reg, 0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx95_ldb_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx95_ldb_runtime_suspend,
+ imx95_ldb_runtime_resume, NULL)
+};
+
+static const struct of_device_id imx95_ldb_dt_ids[] = {
+ { .compatible = "fsl,imx95-lvds" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx95_ldb_dt_ids);
+
+static struct platform_driver imx95_ldb_driver = {
+ .probe = imx95_ldb_probe,
+ .remove = imx95_ldb_remove,
+ .driver = {
+ .pm = &imx95_ldb_pm_ops,
+ .name = DRIVER_NAME,
+ .of_match_table = imx95_ldb_dt_ids,
+ },
+};
+module_platform_driver(imx95_ldb_driver);
+
+MODULE_DESCRIPTION("i.MX95 LVDS Display Bridge(LDB) bridge driver");
+MODULE_AUTHOR("Sandor Yu <Sandor.yu at nxp.com>");
+MODULE_LICENSE("GPL");
--
2.51.0
More information about the linux-arm-kernel
mailing list