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