xref: /linux/drivers/gpu/drm/bridge/waveshare-dsi.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2025 NXP
4  * Based on panel-raspberrypi-touchscreen by Broadcom
5  */
6 
7 #include <linux/backlight.h>
8 #include <linux/err.h>
9 #include <linux/i2c.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_graph.h>
13 #include <linux/regmap.h>
14 
15 #include <drm/drm_atomic_state_helper.h>
16 #include <drm/drm_bridge.h>
17 #include <drm/drm_mipi_dsi.h>
18 #include <drm/drm_of.h>
19 #include <drm/drm_panel.h>
20 
21 struct ws_bridge {
22 	struct drm_bridge bridge;
23 	struct drm_bridge *next_bridge;
24 	struct backlight_device *backlight;
25 	struct device *dev;
26 	struct regmap *reg_map;
27 };
28 
29 static const struct regmap_config ws_regmap_config = {
30 	.reg_bits = 8,
31 	.val_bits = 8,
32 	.max_register = 0xff,
33 };
34 
35 static struct ws_bridge *bridge_to_ws_bridge(struct drm_bridge *bridge)
36 {
37 	return container_of(bridge, struct ws_bridge, bridge);
38 }
39 
40 static int ws_bridge_attach_dsi(struct ws_bridge *ws)
41 {
42 	const struct mipi_dsi_device_info info = {
43 		.type = "ws-bridge",
44 		.channel = 0,
45 		.node = NULL,
46 	};
47 	struct device_node *dsi_host_node;
48 	struct device *dev = ws->dev;
49 	struct mipi_dsi_device *dsi;
50 	struct mipi_dsi_host *host;
51 	int ret;
52 
53 	dsi_host_node = of_graph_get_remote_node(dev->of_node, 0, 0);
54 	if (!dsi_host_node) {
55 		dev_err(dev, "Failed to get remote port\n");
56 		return -ENODEV;
57 	}
58 	host = of_find_mipi_dsi_host_by_node(dsi_host_node);
59 	of_node_put(dsi_host_node);
60 	if (!host)
61 		return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find dsi_host\n");
62 
63 	dsi = devm_mipi_dsi_device_register_full(dev, host, &info);
64 	if (IS_ERR(dsi))
65 		return dev_err_probe(dev, PTR_ERR(dsi), "Failed to create dsi device\n");
66 
67 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
68 			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
69 	dsi->format = MIPI_DSI_FMT_RGB888;
70 	ret = drm_of_get_data_lanes_count_ep(dev->of_node, 0, 0, 1, 4);
71 	if (ret < 0) {
72 		dev_warn(dev, "Invalid or missing DSI lane count %d, falling back to 2 lanes\n",
73 			 ret);
74 		dsi->lanes = 2;	/* Old DT backward compatibility */
75 	} else {
76 		dsi->lanes = ret;
77 	}
78 
79 	ret = devm_mipi_dsi_attach(dev, dsi);
80 	if (ret < 0)
81 		return dev_err_probe(dev, ret, "Failed to attach dsi to host\n");
82 
83 	return 0;
84 }
85 
86 static int ws_bridge_bridge_attach(struct drm_bridge *bridge,
87 				   struct drm_encoder *encoder,
88 				   enum drm_bridge_attach_flags flags)
89 {
90 	struct ws_bridge *ws = bridge_to_ws_bridge(bridge);
91 
92 	return drm_bridge_attach(encoder, ws->next_bridge,
93 				 &ws->bridge, flags);
94 }
95 
96 static void ws_bridge_bridge_enable(struct drm_bridge *bridge,
97 				    struct drm_atomic_commit *commit)
98 {
99 	struct ws_bridge *ws = bridge_to_ws_bridge(bridge);
100 
101 	regmap_write(ws->reg_map, 0xad, 0x01);
102 	backlight_enable(ws->backlight);
103 }
104 
105 static void ws_bridge_bridge_disable(struct drm_bridge *bridge,
106 				     struct drm_atomic_commit *commit)
107 {
108 	struct ws_bridge *ws = bridge_to_ws_bridge(bridge);
109 
110 	backlight_disable(ws->backlight);
111 	regmap_write(ws->reg_map, 0xad, 0x00);
112 }
113 
114 static const struct drm_bridge_funcs ws_bridge_bridge_funcs = {
115 	.atomic_create_state = drm_atomic_helper_bridge_create_state,
116 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
117 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
118 	.atomic_enable = ws_bridge_bridge_enable,
119 	.atomic_disable = ws_bridge_bridge_disable,
120 	.attach = ws_bridge_bridge_attach,
121 };
122 
123 static int ws_bridge_bl_update_status(struct backlight_device *bl)
124 {
125 	struct ws_bridge *ws = bl_get_data(bl);
126 
127 	regmap_write(ws->reg_map, 0xab, 0xff - backlight_get_brightness(bl));
128 	regmap_write(ws->reg_map, 0xaa, 0x01);
129 
130 	return 0;
131 }
132 
133 static const struct backlight_ops ws_bridge_bl_ops = {
134 	.update_status = ws_bridge_bl_update_status,
135 };
136 
137 static struct backlight_device *ws_bridge_create_backlight(struct ws_bridge *ws)
138 {
139 	const struct backlight_properties props = {
140 		.type = BACKLIGHT_RAW,
141 		.brightness = 255,
142 		.max_brightness = 255,
143 	};
144 	struct device *dev = ws->dev;
145 
146 	return devm_backlight_device_register(dev, dev_name(dev), dev, ws,
147 					      &ws_bridge_bl_ops, &props);
148 }
149 
150 static int ws_bridge_probe(struct i2c_client *i2c)
151 {
152 	struct device *dev = &i2c->dev;
153 	struct drm_panel *panel;
154 	struct ws_bridge *ws;
155 	int ret;
156 
157 	ws = devm_drm_bridge_alloc(dev, struct ws_bridge, bridge, &ws_bridge_bridge_funcs);
158 	if (IS_ERR(ws))
159 		return PTR_ERR(ws);
160 
161 	ws->dev = dev;
162 
163 	ws->reg_map = devm_regmap_init_i2c(i2c, &ws_regmap_config);
164 	if (IS_ERR(ws->reg_map))
165 		return dev_err_probe(dev, PTR_ERR(ws->reg_map), "Failed to allocate regmap\n");
166 
167 	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, NULL);
168 	if (ret)
169 		return dev_err_probe(dev, ret, "Failed to find remote panel\n");
170 
171 	ws->next_bridge = devm_drm_panel_bridge_add(dev, panel);
172 	drm_panel_put(panel);
173 	if (IS_ERR(ws->next_bridge))
174 		return PTR_ERR(ws->next_bridge);
175 
176 	ws->backlight = ws_bridge_create_backlight(ws);
177 	if (IS_ERR(ws->backlight)) {
178 		ret = PTR_ERR(ws->backlight);
179 		dev_err(dev, "Failed to create backlight: %d\n", ret);
180 		return ret;
181 	}
182 
183 	regmap_write(ws->reg_map, 0xc0, 0x01);
184 	regmap_write(ws->reg_map, 0xc2, 0x01);
185 	regmap_write(ws->reg_map, 0xac, 0x01);
186 
187 	ws->bridge.type = (uintptr_t)i2c_get_match_data(i2c);
188 	ws->bridge.of_node = dev->of_node;
189 	devm_drm_bridge_add(dev, &ws->bridge);
190 
191 	return ws_bridge_attach_dsi(ws);
192 }
193 
194 static const struct of_device_id ws_bridge_of_ids[] = {
195 	{.compatible = "waveshare,dsi2dpi", .data = (void *)DRM_MODE_CONNECTOR_DPI, },
196 	{.compatible = "waveshare,dsi2lvds", .data = (void *)DRM_MODE_CONNECTOR_LVDS, },
197 	{ }
198 };
199 
200 MODULE_DEVICE_TABLE(of, ws_bridge_of_ids);
201 
202 static struct i2c_driver ws_bridge_driver = {
203 	.driver = {
204 		.name = "ws_dsi2dpi",
205 		.of_match_table = ws_bridge_of_ids,
206 	},
207 	.probe = ws_bridge_probe,
208 };
209 module_i2c_driver(ws_bridge_driver);
210 
211 MODULE_AUTHOR("Joseph Guo <qijian.guo@nxp.com>");
212 MODULE_DESCRIPTION("Waveshare DSI2DPI bridge driver");
213 MODULE_LICENSE("GPL");
214