1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2019 Renesas Electronics Corporation
4 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 #include <linux/gpio/consumer.h>
8 #include <linux/module.h>
10 #include <linux/of_device.h>
11 #include <linux/of_graph.h>
12 #include <linux/platform_device.h>
14 #include <drm/drm_bridge.h>
15 #include <drm/drm_panel.h>
18 struct drm_bridge bridge
;
19 struct drm_bridge
*panel_bridge
;
20 struct gpio_desc
*powerdown_gpio
;
24 static int lvds_codec_attach(struct drm_bridge
*bridge
)
26 struct lvds_codec
*lvds_codec
= container_of(bridge
,
27 struct lvds_codec
, bridge
);
29 return drm_bridge_attach(bridge
->encoder
, lvds_codec
->panel_bridge
,
33 static void lvds_codec_enable(struct drm_bridge
*bridge
)
35 struct lvds_codec
*lvds_codec
= container_of(bridge
,
36 struct lvds_codec
, bridge
);
38 if (lvds_codec
->powerdown_gpio
)
39 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 0);
42 static void lvds_codec_disable(struct drm_bridge
*bridge
)
44 struct lvds_codec
*lvds_codec
= container_of(bridge
,
45 struct lvds_codec
, bridge
);
47 if (lvds_codec
->powerdown_gpio
)
48 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 1);
51 static struct drm_bridge_funcs funcs
= {
52 .attach
= lvds_codec_attach
,
53 .enable
= lvds_codec_enable
,
54 .disable
= lvds_codec_disable
,
57 static int lvds_codec_probe(struct platform_device
*pdev
)
59 struct device
*dev
= &pdev
->dev
;
60 struct device_node
*panel_node
;
61 struct drm_panel
*panel
;
62 struct lvds_codec
*lvds_codec
;
64 lvds_codec
= devm_kzalloc(dev
, sizeof(*lvds_codec
), GFP_KERNEL
);
68 lvds_codec
->connector_type
= (uintptr_t)of_device_get_match_data(dev
);
69 lvds_codec
->powerdown_gpio
= devm_gpiod_get_optional(dev
, "powerdown",
71 if (IS_ERR(lvds_codec
->powerdown_gpio
)) {
72 int err
= PTR_ERR(lvds_codec
->powerdown_gpio
);
74 if (err
!= -EPROBE_DEFER
)
75 dev_err(dev
, "powerdown GPIO failure: %d\n", err
);
79 /* Locate the panel DT node. */
80 panel_node
= of_graph_get_remote_node(dev
->of_node
, 1, 0);
82 dev_dbg(dev
, "panel DT node not found\n");
86 panel
= of_drm_find_panel(panel_node
);
87 of_node_put(panel_node
);
89 dev_dbg(dev
, "panel not found, deferring probe\n");
90 return PTR_ERR(panel
);
93 lvds_codec
->panel_bridge
=
94 devm_drm_panel_bridge_add_typed(dev
, panel
,
95 lvds_codec
->connector_type
);
96 if (IS_ERR(lvds_codec
->panel_bridge
))
97 return PTR_ERR(lvds_codec
->panel_bridge
);
100 * The panel_bridge bridge is attached to the panel's of_node,
101 * but we need a bridge attached to our of_node for our user
104 lvds_codec
->bridge
.of_node
= dev
->of_node
;
105 lvds_codec
->bridge
.funcs
= &funcs
;
106 drm_bridge_add(&lvds_codec
->bridge
);
108 platform_set_drvdata(pdev
, lvds_codec
);
113 static int lvds_codec_remove(struct platform_device
*pdev
)
115 struct lvds_codec
*lvds_codec
= platform_get_drvdata(pdev
);
117 drm_bridge_remove(&lvds_codec
->bridge
);
122 static const struct of_device_id lvds_codec_match
[] = {
124 .compatible
= "lvds-decoder",
125 .data
= (void *)DRM_MODE_CONNECTOR_DPI
,
128 .compatible
= "lvds-encoder",
129 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
132 .compatible
= "thine,thc63lvdm83d",
133 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
137 MODULE_DEVICE_TABLE(of
, lvds_codec_match
);
139 static struct platform_driver lvds_codec_driver
= {
140 .probe
= lvds_codec_probe
,
141 .remove
= lvds_codec_remove
,
143 .name
= "lvds-codec",
144 .of_match_table
= lvds_codec_match
,
147 module_platform_driver(lvds_codec_driver
);
149 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
150 MODULE_DESCRIPTION("LVDS encoders and decoders");
151 MODULE_LICENSE("GPL");