]> Git Repo - linux.git/blob - drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
Linux 6.14-rc3
[linux.git] / drivers / gpu / drm / bridge / imx / imx8qxp-pixel-combiner.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 /*
4  * Copyright 2020 NXP
5  */
6
7 #include <linux/bitfield.h>
8 #include <linux/clk.h>
9 #include <linux/delay.h>
10 #include <linux/io.h>
11 #include <linux/media-bus-format.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_graph.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
17
18 #include <drm/drm_atomic_state_helper.h>
19 #include <drm/drm_bridge.h>
20 #include <drm/drm_print.h>
21
22 #define PC_CTRL_REG                     0x0
23 #define  PC_COMBINE_ENABLE              BIT(0)
24 #define  PC_DISP_BYPASS(n)              BIT(1 + 21 * (n))
25 #define  PC_DISP_HSYNC_POLARITY(n)      BIT(2 + 11 * (n))
26 #define  PC_DISP_HSYNC_POLARITY_POS(n)  DISP_HSYNC_POLARITY(n)
27 #define  PC_DISP_VSYNC_POLARITY(n)      BIT(3 + 11 * (n))
28 #define  PC_DISP_VSYNC_POLARITY_POS(n)  DISP_VSYNC_POLARITY(n)
29 #define  PC_DISP_DVALID_POLARITY(n)     BIT(4 + 11 * (n))
30 #define  PC_DISP_DVALID_POLARITY_POS(n) DISP_DVALID_POLARITY(n)
31 #define  PC_VSYNC_MASK_ENABLE           BIT(5)
32 #define  PC_SKIP_MODE                   BIT(6)
33 #define  PC_SKIP_NUMBER_MASK            GENMASK(12, 7)
34 #define  PC_SKIP_NUMBER(n)              FIELD_PREP(PC_SKIP_NUMBER_MASK, (n))
35 #define  PC_DISP0_PIX_DATA_FORMAT_MASK  GENMASK(18, 16)
36 #define  PC_DISP0_PIX_DATA_FORMAT(fmt)  \
37                                 FIELD_PREP(PC_DISP0_PIX_DATA_FORMAT_MASK, (fmt))
38 #define  PC_DISP1_PIX_DATA_FORMAT_MASK  GENMASK(21, 19)
39 #define  PC_DISP1_PIX_DATA_FORMAT(fmt)  \
40                                 FIELD_PREP(PC_DISP1_PIX_DATA_FORMAT_MASK, (fmt))
41
42 #define PC_SW_RESET_REG                 0x20
43 #define  PC_SW_RESET_N                  BIT(0)
44 #define  PC_DISP_SW_RESET_N(n)          BIT(1 + (n))
45 #define  PC_FULL_RESET_N                (PC_SW_RESET_N |                \
46                                          PC_DISP_SW_RESET_N(0) |        \
47                                          PC_DISP_SW_RESET_N(1))
48
49 #define PC_REG_SET                      0x4
50 #define PC_REG_CLR                      0x8
51
52 #define DRIVER_NAME                     "imx8qxp-pixel-combiner"
53
54 enum imx8qxp_pc_pix_data_format {
55         RGB,
56         YUV444,
57         YUV422,
58         SPLIT_RGB,
59 };
60
61 struct imx8qxp_pc_channel {
62         struct drm_bridge bridge;
63         struct drm_bridge *next_bridge;
64         struct imx8qxp_pc *pc;
65         unsigned int stream_id;
66         bool is_available;
67 };
68
69 struct imx8qxp_pc {
70         struct device *dev;
71         struct imx8qxp_pc_channel ch[2];
72         struct clk *clk_apb;
73         void __iomem *base;
74 };
75
76 static inline u32 imx8qxp_pc_read(struct imx8qxp_pc *pc, unsigned int offset)
77 {
78         return readl(pc->base + offset);
79 }
80
81 static inline void
82 imx8qxp_pc_write(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
83 {
84         writel(value, pc->base + offset);
85 }
86
87 static inline void
88 imx8qxp_pc_write_set(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
89 {
90         imx8qxp_pc_write(pc, offset + PC_REG_SET, value);
91 }
92
93 static inline void
94 imx8qxp_pc_write_clr(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
95 {
96         imx8qxp_pc_write(pc, offset + PC_REG_CLR, value);
97 }
98
99 static enum drm_mode_status
100 imx8qxp_pc_bridge_mode_valid(struct drm_bridge *bridge,
101                              const struct drm_display_info *info,
102                              const struct drm_display_mode *mode)
103 {
104         if (mode->hdisplay > 2560)
105                 return MODE_BAD_HVALUE;
106
107         return MODE_OK;
108 }
109
110 static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,
111                                     enum drm_bridge_attach_flags flags)
112 {
113         struct imx8qxp_pc_channel *ch = bridge->driver_private;
114         struct imx8qxp_pc *pc = ch->pc;
115
116         if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
117                 DRM_DEV_ERROR(pc->dev,
118                               "do not support creating a drm_connector\n");
119                 return -EINVAL;
120         }
121
122         return drm_bridge_attach(bridge->encoder,
123                                  ch->next_bridge, bridge,
124                                  DRM_BRIDGE_ATTACH_NO_CONNECTOR);
125 }
126
127 static void
128 imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,
129                            const struct drm_display_mode *mode,
130                            const struct drm_display_mode *adjusted_mode)
131 {
132         struct imx8qxp_pc_channel *ch = bridge->driver_private;
133         struct imx8qxp_pc *pc = ch->pc;
134         u32 val;
135         int ret;
136
137         ret = pm_runtime_get_sync(pc->dev);
138         if (ret < 0)
139                 DRM_DEV_ERROR(pc->dev,
140                               "failed to get runtime PM sync: %d\n", ret);
141
142         ret = clk_prepare_enable(pc->clk_apb);
143         if (ret)
144                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
145                               __func__,  ret);
146
147         /* HSYNC to pixel link is active low. */
148         imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
149                              PC_DISP_HSYNC_POLARITY(ch->stream_id));
150
151         /* VSYNC to pixel link is active low. */
152         imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
153                              PC_DISP_VSYNC_POLARITY(ch->stream_id));
154
155         /* Data enable to pixel link is active high. */
156         imx8qxp_pc_write_set(pc, PC_CTRL_REG,
157                              PC_DISP_DVALID_POLARITY(ch->stream_id));
158
159         /* Mask the first frame output which may be incomplete. */
160         imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);
161
162         /* Only support RGB currently. */
163         val = imx8qxp_pc_read(pc, PC_CTRL_REG);
164         if (ch->stream_id == 0) {
165                 val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;
166                 val |= PC_DISP0_PIX_DATA_FORMAT(RGB);
167         } else {
168                 val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;
169                 val |= PC_DISP1_PIX_DATA_FORMAT(RGB);
170         }
171         imx8qxp_pc_write(pc, PC_CTRL_REG, val);
172
173         /* Only support bypass mode currently. */
174         imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));
175
176         clk_disable_unprepare(pc->clk_apb);
177 }
178
179 static void
180 imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,
181                                  struct drm_bridge_state *old_bridge_state)
182 {
183         struct imx8qxp_pc_channel *ch = bridge->driver_private;
184         struct imx8qxp_pc *pc = ch->pc;
185         int ret;
186
187         ret = pm_runtime_put(pc->dev);
188         if (ret < 0)
189                 DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);
190 }
191
192 static const u32 imx8qxp_pc_bus_output_fmts[] = {
193         MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
194         MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
195 };
196
197 static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)
198 {
199         int i;
200
201         for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {
202                 if (imx8qxp_pc_bus_output_fmts[i] == fmt)
203                         return true;
204         }
205
206         return false;
207 }
208
209 static u32 *
210 imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
211                                             struct drm_bridge_state *bridge_state,
212                                             struct drm_crtc_state *crtc_state,
213                                             struct drm_connector_state *conn_state,
214                                             u32 output_fmt,
215                                             unsigned int *num_input_fmts)
216 {
217         u32 *input_fmts;
218
219         if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))
220                 return NULL;
221
222         *num_input_fmts = 1;
223
224         input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
225         if (!input_fmts)
226                 return NULL;
227
228         switch (output_fmt) {
229         case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:
230                 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;
231                 break;
232         case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:
233                 input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;
234                 break;
235         default:
236                 kfree(input_fmts);
237                 input_fmts = NULL;
238                 break;
239         }
240
241         return input_fmts;
242 }
243
244 static u32 *
245 imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
246                                              struct drm_bridge_state *bridge_state,
247                                              struct drm_crtc_state *crtc_state,
248                                              struct drm_connector_state *conn_state,
249                                              unsigned int *num_output_fmts)
250 {
251         *num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);
252         return kmemdup(imx8qxp_pc_bus_output_fmts,
253                         sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);
254 }
255
256 static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
257         .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
258         .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
259         .atomic_reset           = drm_atomic_helper_bridge_reset,
260         .mode_valid             = imx8qxp_pc_bridge_mode_valid,
261         .attach                 = imx8qxp_pc_bridge_attach,
262         .mode_set               = imx8qxp_pc_bridge_mode_set,
263         .atomic_disable         = imx8qxp_pc_bridge_atomic_disable,
264         .atomic_get_input_bus_fmts =
265                                 imx8qxp_pc_bridge_atomic_get_input_bus_fmts,
266         .atomic_get_output_bus_fmts =
267                                 imx8qxp_pc_bridge_atomic_get_output_bus_fmts,
268 };
269
270 static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
271 {
272         struct imx8qxp_pc *pc;
273         struct imx8qxp_pc_channel *ch;
274         struct device *dev = &pdev->dev;
275         struct device_node *np = dev->of_node;
276         struct device_node *child, *remote;
277         u32 i;
278         int ret;
279
280         pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
281         if (!pc)
282                 return -ENOMEM;
283
284         pc->base = devm_platform_ioremap_resource(pdev, 0);
285         if (IS_ERR(pc->base))
286                 return PTR_ERR(pc->base);
287
288         pc->dev = dev;
289
290         pc->clk_apb = devm_clk_get(dev, "apb");
291         if (IS_ERR(pc->clk_apb)) {
292                 ret = PTR_ERR(pc->clk_apb);
293                 if (ret != -EPROBE_DEFER)
294                         DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);
295                 return ret;
296         }
297
298         platform_set_drvdata(pdev, pc);
299         pm_runtime_enable(dev);
300
301         for_each_available_child_of_node(np, child) {
302                 ret = of_property_read_u32(child, "reg", &i);
303                 if (ret || i > 1) {
304                         ret = -EINVAL;
305                         DRM_DEV_ERROR(dev,
306                                       "invalid channel(%u) node address\n", i);
307                         goto free_child;
308                 }
309
310                 ch = &pc->ch[i];
311                 ch->pc = pc;
312                 ch->stream_id = i;
313
314                 remote = of_graph_get_remote_node(child, 1, 0);
315                 if (!remote) {
316                         ret = -ENODEV;
317                         DRM_DEV_ERROR(dev,
318                                       "channel%u failed to get port1's remote node: %d\n",
319                                       i, ret);
320                         goto free_child;
321                 }
322
323                 ch->next_bridge = of_drm_find_bridge(remote);
324                 if (!ch->next_bridge) {
325                         of_node_put(remote);
326                         ret = -EPROBE_DEFER;
327                         DRM_DEV_DEBUG_DRIVER(dev,
328                                              "channel%u failed to find next bridge: %d\n",
329                                              i, ret);
330                         goto free_child;
331                 }
332
333                 of_node_put(remote);
334
335                 ch->bridge.driver_private = ch;
336                 ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;
337                 ch->bridge.of_node = child;
338                 ch->is_available = true;
339
340                 drm_bridge_add(&ch->bridge);
341         }
342
343         return 0;
344
345 free_child:
346         of_node_put(child);
347
348         if (i == 1 && pc->ch[0].next_bridge)
349                 drm_bridge_remove(&pc->ch[0].bridge);
350
351         pm_runtime_disable(dev);
352         return ret;
353 }
354
355 static void imx8qxp_pc_bridge_remove(struct platform_device *pdev)
356 {
357         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
358         struct imx8qxp_pc_channel *ch;
359         int i;
360
361         for (i = 0; i < 2; i++) {
362                 ch = &pc->ch[i];
363
364                 if (!ch->is_available)
365                         continue;
366
367                 drm_bridge_remove(&ch->bridge);
368                 ch->is_available = false;
369         }
370
371         pm_runtime_disable(&pdev->dev);
372 }
373
374 static int imx8qxp_pc_runtime_suspend(struct device *dev)
375 {
376         struct platform_device *pdev = to_platform_device(dev);
377         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
378         int ret;
379
380         ret = clk_prepare_enable(pc->clk_apb);
381         if (ret)
382                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
383                               __func__,  ret);
384
385         /* Disable pixel combiner by full reset. */
386         imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
387
388         clk_disable_unprepare(pc->clk_apb);
389
390         /* Ensure the reset takes effect. */
391         usleep_range(10, 20);
392
393         return ret;
394 }
395
396 static int imx8qxp_pc_runtime_resume(struct device *dev)
397 {
398         struct platform_device *pdev = to_platform_device(dev);
399         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
400         int ret;
401
402         ret = clk_prepare_enable(pc->clk_apb);
403         if (ret) {
404                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
405                               __func__, ret);
406                 return ret;
407         }
408
409         /* out of reset */
410         imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
411
412         clk_disable_unprepare(pc->clk_apb);
413
414         return ret;
415 }
416
417 static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
418         RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend, imx8qxp_pc_runtime_resume, NULL)
419 };
420
421 static const struct of_device_id imx8qxp_pc_dt_ids[] = {
422         { .compatible = "fsl,imx8qm-pixel-combiner", },
423         { .compatible = "fsl,imx8qxp-pixel-combiner", },
424         { /* sentinel */ }
425 };
426 MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
427
428 static struct platform_driver imx8qxp_pc_bridge_driver = {
429         .probe  = imx8qxp_pc_bridge_probe,
430         .remove = imx8qxp_pc_bridge_remove,
431         .driver = {
432                 .pm = pm_ptr(&imx8qxp_pc_pm_ops),
433                 .name = DRIVER_NAME,
434                 .of_match_table = imx8qxp_pc_dt_ids,
435         },
436 };
437 module_platform_driver(imx8qxp_pc_bridge_driver);
438
439 MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
440 MODULE_AUTHOR("Liu Ying <[email protected]>");
441 MODULE_LICENSE("GPL v2");
442 MODULE_ALIAS("platform:" DRIVER_NAME);
This page took 0.056714 seconds and 4 git commands to generate.