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>
13 #include <linux/regulator/consumer.h>
15 #include <drm/drm_bridge.h>
16 #include <drm/drm_panel.h>
20 struct drm_bridge bridge
;
21 struct drm_bridge
*panel_bridge
;
22 struct regulator
*vcc
;
23 struct gpio_desc
*powerdown_gpio
;
27 static inline struct lvds_codec
*to_lvds_codec(struct drm_bridge
*bridge
)
29 return container_of(bridge
, struct lvds_codec
, bridge
);
32 static int lvds_codec_attach(struct drm_bridge
*bridge
,
33 enum drm_bridge_attach_flags flags
)
35 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
37 return drm_bridge_attach(bridge
->encoder
, lvds_codec
->panel_bridge
,
41 static void lvds_codec_enable(struct drm_bridge
*bridge
)
43 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
46 ret
= regulator_enable(lvds_codec
->vcc
);
48 dev_err(lvds_codec
->dev
,
49 "Failed to enable regulator \"vcc\": %d\n", ret
);
53 if (lvds_codec
->powerdown_gpio
)
54 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 0);
57 static void lvds_codec_disable(struct drm_bridge
*bridge
)
59 struct lvds_codec
*lvds_codec
= to_lvds_codec(bridge
);
62 if (lvds_codec
->powerdown_gpio
)
63 gpiod_set_value_cansleep(lvds_codec
->powerdown_gpio
, 1);
65 ret
= regulator_disable(lvds_codec
->vcc
);
67 dev_err(lvds_codec
->dev
,
68 "Failed to disable regulator \"vcc\": %d\n", ret
);
71 static const struct drm_bridge_funcs funcs
= {
72 .attach
= lvds_codec_attach
,
73 .enable
= lvds_codec_enable
,
74 .disable
= lvds_codec_disable
,
77 static int lvds_codec_probe(struct platform_device
*pdev
)
79 struct device
*dev
= &pdev
->dev
;
80 struct device_node
*panel_node
;
81 struct drm_panel
*panel
;
82 struct lvds_codec
*lvds_codec
;
84 lvds_codec
= devm_kzalloc(dev
, sizeof(*lvds_codec
), GFP_KERNEL
);
88 lvds_codec
->dev
= &pdev
->dev
;
89 lvds_codec
->connector_type
= (uintptr_t)of_device_get_match_data(dev
);
91 lvds_codec
->vcc
= devm_regulator_get(lvds_codec
->dev
, "power");
92 if (IS_ERR(lvds_codec
->vcc
))
93 return dev_err_probe(dev
, PTR_ERR(lvds_codec
->vcc
),
94 "Unable to get \"vcc\" supply\n");
96 lvds_codec
->powerdown_gpio
= devm_gpiod_get_optional(dev
, "powerdown",
98 if (IS_ERR(lvds_codec
->powerdown_gpio
))
99 return dev_err_probe(dev
, PTR_ERR(lvds_codec
->powerdown_gpio
),
100 "powerdown GPIO failure\n");
102 /* Locate the panel DT node. */
103 panel_node
= of_graph_get_remote_node(dev
->of_node
, 1, 0);
105 dev_dbg(dev
, "panel DT node not found\n");
109 panel
= of_drm_find_panel(panel_node
);
110 of_node_put(panel_node
);
112 dev_dbg(dev
, "panel not found, deferring probe\n");
113 return PTR_ERR(panel
);
116 lvds_codec
->panel_bridge
=
117 devm_drm_panel_bridge_add_typed(dev
, panel
,
118 lvds_codec
->connector_type
);
119 if (IS_ERR(lvds_codec
->panel_bridge
))
120 return PTR_ERR(lvds_codec
->panel_bridge
);
123 * The panel_bridge bridge is attached to the panel's of_node,
124 * but we need a bridge attached to our of_node for our user
127 lvds_codec
->bridge
.of_node
= dev
->of_node
;
128 lvds_codec
->bridge
.funcs
= &funcs
;
129 drm_bridge_add(&lvds_codec
->bridge
);
131 platform_set_drvdata(pdev
, lvds_codec
);
136 static int lvds_codec_remove(struct platform_device
*pdev
)
138 struct lvds_codec
*lvds_codec
= platform_get_drvdata(pdev
);
140 drm_bridge_remove(&lvds_codec
->bridge
);
145 static const struct of_device_id lvds_codec_match
[] = {
147 .compatible
= "lvds-decoder",
148 .data
= (void *)DRM_MODE_CONNECTOR_DPI
,
151 .compatible
= "lvds-encoder",
152 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
155 .compatible
= "thine,thc63lvdm83d",
156 .data
= (void *)DRM_MODE_CONNECTOR_LVDS
,
160 MODULE_DEVICE_TABLE(of
, lvds_codec_match
);
162 static struct platform_driver lvds_codec_driver
= {
163 .probe
= lvds_codec_probe
,
164 .remove
= lvds_codec_remove
,
166 .name
= "lvds-codec",
167 .of_match_table
= lvds_codec_match
,
170 module_platform_driver(lvds_codec_driver
);
172 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
173 MODULE_DESCRIPTION("LVDS encoders and decoders");
174 MODULE_LICENSE("GPL");