Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0+
2
3/*
4 * Copyright 2020,2022 NXP
5 */
6
7#include <linux/firmware/imx/svc/misc.h>
8#include <linux/media-bus-format.h>
9#include <linux/module.h>
10#include <linux/of.h>
11#include <linux/of_graph.h>
12#include <linux/platform_device.h>
13
14#include <drm/drm_atomic_state_helper.h>
15#include <drm/drm_bridge.h>
16#include <drm/drm_print.h>
17
18#include <dt-bindings/firmware/imx/rsrc.h>
19
20#define DRIVER_NAME "imx8qxp-display-pixel-link"
21#define PL_MAX_MST_ADDR 3
22#define PL_MAX_NEXT_BRIDGES 2
23
24struct imx8qxp_pixel_link {
25 struct drm_bridge bridge;
26 struct device *dev;
27 struct imx_sc_ipc *ipc_handle;
28 u8 stream_id;
29 u8 dc_id;
30 u32 sink_rsc;
31 u32 mst_addr;
32 u8 mst_addr_ctrl;
33 u8 mst_en_ctrl;
34 u8 mst_vld_ctrl;
35 u8 sync_ctrl;
36};
37
38static void imx8qxp_pixel_link_enable_mst_en(struct imx8qxp_pixel_link *pl)
39{
40 int ret;
41
42 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
43 pl->mst_en_ctrl, true);
44 if (ret)
45 DRM_DEV_ERROR(pl->dev,
46 "failed to enable DC%u stream%u pixel link mst_en: %d\n",
47 pl->dc_id, pl->stream_id, ret);
48}
49
50static void imx8qxp_pixel_link_enable_mst_vld(struct imx8qxp_pixel_link *pl)
51{
52 int ret;
53
54 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
55 pl->mst_vld_ctrl, true);
56 if (ret)
57 DRM_DEV_ERROR(pl->dev,
58 "failed to enable DC%u stream%u pixel link mst_vld: %d\n",
59 pl->dc_id, pl->stream_id, ret);
60}
61
62static void imx8qxp_pixel_link_enable_sync(struct imx8qxp_pixel_link *pl)
63{
64 int ret;
65
66 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
67 pl->sync_ctrl, true);
68 if (ret)
69 DRM_DEV_ERROR(pl->dev,
70 "failed to enable DC%u stream%u pixel link sync: %d\n",
71 pl->dc_id, pl->stream_id, ret);
72}
73
74static int imx8qxp_pixel_link_disable_mst_en(struct imx8qxp_pixel_link *pl)
75{
76 int ret;
77
78 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
79 pl->mst_en_ctrl, false);
80 if (ret)
81 DRM_DEV_ERROR(pl->dev,
82 "failed to disable DC%u stream%u pixel link mst_en: %d\n",
83 pl->dc_id, pl->stream_id, ret);
84
85 return ret;
86}
87
88static int imx8qxp_pixel_link_disable_mst_vld(struct imx8qxp_pixel_link *pl)
89{
90 int ret;
91
92 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
93 pl->mst_vld_ctrl, false);
94 if (ret)
95 DRM_DEV_ERROR(pl->dev,
96 "failed to disable DC%u stream%u pixel link mst_vld: %d\n",
97 pl->dc_id, pl->stream_id, ret);
98
99 return ret;
100}
101
102static int imx8qxp_pixel_link_disable_sync(struct imx8qxp_pixel_link *pl)
103{
104 int ret;
105
106 ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
107 pl->sync_ctrl, false);
108 if (ret)
109 DRM_DEV_ERROR(pl->dev,
110 "failed to disable DC%u stream%u pixel link sync: %d\n",
111 pl->dc_id, pl->stream_id, ret);
112
113 return ret;
114}
115
116static void imx8qxp_pixel_link_set_mst_addr(struct imx8qxp_pixel_link *pl)
117{
118 int ret;
119
120 ret = imx_sc_misc_set_control(pl->ipc_handle,
121 pl->sink_rsc, pl->mst_addr_ctrl,
122 pl->mst_addr);
123 if (ret)
124 DRM_DEV_ERROR(pl->dev,
125 "failed to set DC%u stream%u pixel link mst addr(%u): %d\n",
126 pl->dc_id, pl->stream_id, pl->mst_addr, ret);
127}
128
129static int imx8qxp_pixel_link_bridge_attach(struct drm_bridge *bridge,
130 struct drm_encoder *encoder,
131 enum drm_bridge_attach_flags flags)
132{
133 struct imx8qxp_pixel_link *pl = bridge->driver_private;
134
135 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
136 DRM_DEV_ERROR(pl->dev,
137 "do not support creating a drm_connector\n");
138 return -EINVAL;
139 }
140
141 return drm_bridge_attach(encoder,
142 pl->bridge.next_bridge, bridge,
143 DRM_BRIDGE_ATTACH_NO_CONNECTOR);
144}
145
146static void
147imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,
148 const struct drm_display_mode *mode,
149 const struct drm_display_mode *adjusted_mode)
150{
151 struct imx8qxp_pixel_link *pl = bridge->driver_private;
152
153 imx8qxp_pixel_link_set_mst_addr(pl);
154}
155
156static void imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,
157 struct drm_atomic_state *state)
158{
159 struct imx8qxp_pixel_link *pl = bridge->driver_private;
160
161 imx8qxp_pixel_link_enable_mst_en(pl);
162 imx8qxp_pixel_link_enable_mst_vld(pl);
163 imx8qxp_pixel_link_enable_sync(pl);
164}
165
166static void imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,
167 struct drm_atomic_state *state)
168{
169 struct imx8qxp_pixel_link *pl = bridge->driver_private;
170
171 imx8qxp_pixel_link_disable_mst_en(pl);
172 imx8qxp_pixel_link_disable_mst_vld(pl);
173 imx8qxp_pixel_link_disable_sync(pl);
174}
175
176static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {
177 MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
178 MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
179};
180
181static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)
182{
183 int i;
184
185 for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {
186 if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)
187 return true;
188 }
189
190 return false;
191}
192
193static u32 *
194imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
195 struct drm_bridge_state *bridge_state,
196 struct drm_crtc_state *crtc_state,
197 struct drm_connector_state *conn_state,
198 u32 output_fmt,
199 unsigned int *num_input_fmts)
200{
201 u32 *input_fmts;
202
203 if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))
204 return NULL;
205
206 *num_input_fmts = 1;
207
208 input_fmts = kmalloc_obj(*input_fmts);
209 if (!input_fmts)
210 return NULL;
211
212 input_fmts[0] = output_fmt;
213
214 return input_fmts;
215}
216
217static u32 *
218imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
219 struct drm_bridge_state *bridge_state,
220 struct drm_crtc_state *crtc_state,
221 struct drm_connector_state *conn_state,
222 unsigned int *num_output_fmts)
223{
224 *num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);
225 return kmemdup(imx8qxp_pixel_link_bus_output_fmts,
226 sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);
227}
228
229static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {
230 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
231 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
232 .atomic_reset = drm_atomic_helper_bridge_reset,
233 .attach = imx8qxp_pixel_link_bridge_attach,
234 .mode_set = imx8qxp_pixel_link_bridge_mode_set,
235 .atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable,
236 .atomic_disable = imx8qxp_pixel_link_bridge_atomic_disable,
237 .atomic_get_input_bus_fmts =
238 imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,
239 .atomic_get_output_bus_fmts =
240 imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,
241};
242
243static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)
244{
245 int ret;
246
247 ret = imx8qxp_pixel_link_disable_mst_en(pl);
248 if (ret)
249 return ret;
250
251 ret = imx8qxp_pixel_link_disable_mst_vld(pl);
252 if (ret)
253 return ret;
254
255 return imx8qxp_pixel_link_disable_sync(pl);
256}
257
258static int imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)
259{
260 struct device_node *np = pl->dev->of_node;
261 struct device_node *port;
262 u32 port_id;
263 bool found_port = false;
264 int reg;
265
266 for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {
267 port = of_graph_get_port_by_id(np, port_id);
268 if (!port)
269 continue;
270
271 if (of_device_is_available(port)) {
272 found_port = true;
273 of_node_put(port);
274 break;
275 }
276
277 of_node_put(port);
278 }
279
280 if (!found_port) {
281 DRM_DEV_ERROR(pl->dev, "no available output port\n");
282 return -ENODEV;
283 }
284
285 for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {
286 struct device_node *remote __free(device_node) =
287 of_graph_get_remote_node(np, port_id, reg);
288 if (!remote)
289 continue;
290
291 if (!of_device_is_available(remote->parent)) {
292 DRM_DEV_DEBUG(pl->dev,
293 "port%u endpoint%u remote parent is not available\n",
294 port_id, reg);
295 continue;
296 }
297
298 if (!pl->bridge.next_bridge) {
299 /* Select the first bridge by default... */
300 pl->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
301 if (!pl->bridge.next_bridge)
302 return -EPROBE_DEFER;
303 } else if (of_property_present(remote, "fsl,companion-pxl2dpi")) {
304 /* ... but prefer the companion PXL2DPI if present */
305 drm_bridge_put(pl->bridge.next_bridge);
306 pl->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
307 if (!pl->bridge.next_bridge)
308 return -EPROBE_DEFER;
309 }
310 }
311
312 pl->mst_addr = port_id - 1;
313
314 return 0;
315}
316
317static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
318{
319 struct imx8qxp_pixel_link *pl;
320 struct device *dev = &pdev->dev;
321 struct device_node *np = dev->of_node;
322 int ret;
323
324 pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge,
325 &imx8qxp_pixel_link_bridge_funcs);
326 if (IS_ERR(pl))
327 return PTR_ERR(pl);
328
329 ret = imx_scu_get_handle(&pl->ipc_handle);
330 if (ret) {
331 if (ret != -EPROBE_DEFER)
332 DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
333 ret);
334 return ret;
335 }
336
337 ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);
338 if (ret) {
339 DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);
340 return ret;
341 }
342
343 ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);
344 if (ret) {
345 DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);
346 return ret;
347 }
348
349 pl->dev = dev;
350
351 pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;
352
353 if (pl->stream_id == 0) {
354 pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;
355 pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST1_ENB;
356 pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST1_VLD;
357 pl->sync_ctrl = IMX_SC_C_SYNC_CTRL0;
358 } else {
359 pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;
360 pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST2_ENB;
361 pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST2_VLD;
362 pl->sync_ctrl = IMX_SC_C_SYNC_CTRL1;
363 }
364
365 /* disable all controls to POR default */
366 ret = imx8qxp_pixel_link_disable_all_controls(pl);
367 if (ret)
368 return ret;
369
370 ret = imx8qxp_pixel_link_find_next_bridge(pl);
371 if (ret)
372 return ret;
373
374 platform_set_drvdata(pdev, pl);
375
376 pl->bridge.driver_private = pl;
377 pl->bridge.of_node = np;
378
379 drm_bridge_add(&pl->bridge);
380
381 return ret;
382}
383
384static void imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)
385{
386 struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);
387
388 drm_bridge_remove(&pl->bridge);
389}
390
391static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {
392 { .compatible = "fsl,imx8qm-dc-pixel-link", },
393 { .compatible = "fsl,imx8qxp-dc-pixel-link", },
394 { /* sentinel */ }
395};
396MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);
397
398static struct platform_driver imx8qxp_pixel_link_bridge_driver = {
399 .probe = imx8qxp_pixel_link_bridge_probe,
400 .remove = imx8qxp_pixel_link_bridge_remove,
401 .driver = {
402 .of_match_table = imx8qxp_pixel_link_dt_ids,
403 .name = DRIVER_NAME,
404 },
405};
406module_platform_driver(imx8qxp_pixel_link_bridge_driver);
407
408MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");
409MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
410MODULE_LICENSE("GPL v2");
411MODULE_ALIAS("platform:" DRIVER_NAME);