Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
26517 views
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
struct drm_encoder *encoder,
132
enum drm_bridge_attach_flags flags)
133
{
134
struct imx8qxp_pixel_link *pl = bridge->driver_private;
135
136
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
137
DRM_DEV_ERROR(pl->dev,
138
"do not support creating a drm_connector\n");
139
return -EINVAL;
140
}
141
142
return drm_bridge_attach(encoder,
143
pl->next_bridge, bridge,
144
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
145
}
146
147
static void
148
imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,
149
const struct drm_display_mode *mode,
150
const struct drm_display_mode *adjusted_mode)
151
{
152
struct imx8qxp_pixel_link *pl = bridge->driver_private;
153
154
imx8qxp_pixel_link_set_mst_addr(pl);
155
}
156
157
static void imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,
158
struct drm_atomic_state *state)
159
{
160
struct imx8qxp_pixel_link *pl = bridge->driver_private;
161
162
imx8qxp_pixel_link_enable_mst_en(pl);
163
imx8qxp_pixel_link_enable_mst_vld(pl);
164
imx8qxp_pixel_link_enable_sync(pl);
165
}
166
167
static void imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,
168
struct drm_atomic_state *state)
169
{
170
struct imx8qxp_pixel_link *pl = bridge->driver_private;
171
172
imx8qxp_pixel_link_disable_mst_en(pl);
173
imx8qxp_pixel_link_disable_mst_vld(pl);
174
imx8qxp_pixel_link_disable_sync(pl);
175
}
176
177
static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {
178
MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
179
MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
180
};
181
182
static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)
183
{
184
int i;
185
186
for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {
187
if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)
188
return true;
189
}
190
191
return false;
192
}
193
194
static u32 *
195
imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
196
struct drm_bridge_state *bridge_state,
197
struct drm_crtc_state *crtc_state,
198
struct drm_connector_state *conn_state,
199
u32 output_fmt,
200
unsigned int *num_input_fmts)
201
{
202
u32 *input_fmts;
203
204
if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))
205
return NULL;
206
207
*num_input_fmts = 1;
208
209
input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
210
if (!input_fmts)
211
return NULL;
212
213
input_fmts[0] = output_fmt;
214
215
return input_fmts;
216
}
217
218
static u32 *
219
imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
220
struct drm_bridge_state *bridge_state,
221
struct drm_crtc_state *crtc_state,
222
struct drm_connector_state *conn_state,
223
unsigned int *num_output_fmts)
224
{
225
*num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);
226
return kmemdup(imx8qxp_pixel_link_bus_output_fmts,
227
sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);
228
}
229
230
static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {
231
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
232
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
233
.atomic_reset = drm_atomic_helper_bridge_reset,
234
.attach = imx8qxp_pixel_link_bridge_attach,
235
.mode_set = imx8qxp_pixel_link_bridge_mode_set,
236
.atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable,
237
.atomic_disable = imx8qxp_pixel_link_bridge_atomic_disable,
238
.atomic_get_input_bus_fmts =
239
imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,
240
.atomic_get_output_bus_fmts =
241
imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,
242
};
243
244
static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)
245
{
246
int ret;
247
248
ret = imx8qxp_pixel_link_disable_mst_en(pl);
249
if (ret)
250
return ret;
251
252
ret = imx8qxp_pixel_link_disable_mst_vld(pl);
253
if (ret)
254
return ret;
255
256
return imx8qxp_pixel_link_disable_sync(pl);
257
}
258
259
static struct drm_bridge *
260
imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)
261
{
262
struct device_node *np = pl->dev->of_node;
263
struct device_node *port, *remote;
264
struct drm_bridge *next_bridge[PL_MAX_NEXT_BRIDGES];
265
u32 port_id;
266
bool found_port = false;
267
int reg, ep_cnt = 0;
268
/* select the first next bridge by default */
269
int bridge_sel = 0;
270
271
for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {
272
port = of_graph_get_port_by_id(np, port_id);
273
if (!port)
274
continue;
275
276
if (of_device_is_available(port)) {
277
found_port = true;
278
of_node_put(port);
279
break;
280
}
281
282
of_node_put(port);
283
}
284
285
if (!found_port) {
286
DRM_DEV_ERROR(pl->dev, "no available output port\n");
287
return ERR_PTR(-ENODEV);
288
}
289
290
for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {
291
remote = of_graph_get_remote_node(np, port_id, reg);
292
if (!remote)
293
continue;
294
295
if (!of_device_is_available(remote->parent)) {
296
DRM_DEV_DEBUG(pl->dev,
297
"port%u endpoint%u remote parent is not available\n",
298
port_id, reg);
299
of_node_put(remote);
300
continue;
301
}
302
303
next_bridge[ep_cnt] = of_drm_find_bridge(remote);
304
if (!next_bridge[ep_cnt]) {
305
of_node_put(remote);
306
return ERR_PTR(-EPROBE_DEFER);
307
}
308
309
/* specially select the next bridge with companion PXL2DPI */
310
if (of_property_present(remote, "fsl,companion-pxl2dpi"))
311
bridge_sel = ep_cnt;
312
313
ep_cnt++;
314
315
of_node_put(remote);
316
}
317
318
pl->mst_addr = port_id - 1;
319
320
return next_bridge[bridge_sel];
321
}
322
323
static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
324
{
325
struct imx8qxp_pixel_link *pl;
326
struct device *dev = &pdev->dev;
327
struct device_node *np = dev->of_node;
328
int ret;
329
330
pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge,
331
&imx8qxp_pixel_link_bridge_funcs);
332
if (IS_ERR(pl))
333
return PTR_ERR(pl);
334
335
ret = imx_scu_get_handle(&pl->ipc_handle);
336
if (ret) {
337
if (ret != -EPROBE_DEFER)
338
DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
339
ret);
340
return ret;
341
}
342
343
ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);
344
if (ret) {
345
DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);
346
return ret;
347
}
348
349
ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);
350
if (ret) {
351
DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);
352
return ret;
353
}
354
355
pl->dev = dev;
356
357
pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;
358
359
if (pl->stream_id == 0) {
360
pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;
361
pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST1_ENB;
362
pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST1_VLD;
363
pl->sync_ctrl = IMX_SC_C_SYNC_CTRL0;
364
} else {
365
pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;
366
pl->mst_en_ctrl = IMX_SC_C_PXL_LINK_MST2_ENB;
367
pl->mst_vld_ctrl = IMX_SC_C_PXL_LINK_MST2_VLD;
368
pl->sync_ctrl = IMX_SC_C_SYNC_CTRL1;
369
}
370
371
/* disable all controls to POR default */
372
ret = imx8qxp_pixel_link_disable_all_controls(pl);
373
if (ret)
374
return ret;
375
376
pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);
377
if (IS_ERR(pl->next_bridge)) {
378
ret = PTR_ERR(pl->next_bridge);
379
if (ret != -EPROBE_DEFER)
380
DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
381
ret);
382
return ret;
383
}
384
385
platform_set_drvdata(pdev, pl);
386
387
pl->bridge.driver_private = pl;
388
pl->bridge.of_node = np;
389
390
drm_bridge_add(&pl->bridge);
391
392
return ret;
393
}
394
395
static void imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)
396
{
397
struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);
398
399
drm_bridge_remove(&pl->bridge);
400
}
401
402
static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {
403
{ .compatible = "fsl,imx8qm-dc-pixel-link", },
404
{ .compatible = "fsl,imx8qxp-dc-pixel-link", },
405
{ /* sentinel */ }
406
};
407
MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);
408
409
static struct platform_driver imx8qxp_pixel_link_bridge_driver = {
410
.probe = imx8qxp_pixel_link_bridge_probe,
411
.remove = imx8qxp_pixel_link_bridge_remove,
412
.driver = {
413
.of_match_table = imx8qxp_pixel_link_dt_ids,
414
.name = DRIVER_NAME,
415
},
416
};
417
module_platform_driver(imx8qxp_pixel_link_bridge_driver);
418
419
MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");
420
MODULE_AUTHOR("Liu Ying <[email protected]>");
421
MODULE_LICENSE("GPL v2");
422
MODULE_ALIAS("platform:" DRIVER_NAME);
423
424