]> Git Repo - linux.git/blob - drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
Merge tag 'devicetree-fixes-for-6.4-3' of git://git.kernel.org/pub/scm/linux/kernel...
[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         if (!bridge->encoder) {
123                 DRM_DEV_ERROR(pc->dev, "missing encoder\n");
124                 return -ENODEV;
125         }
126
127         return drm_bridge_attach(bridge->encoder,
128                                  ch->next_bridge, bridge,
129                                  DRM_BRIDGE_ATTACH_NO_CONNECTOR);
130 }
131
132 static void
133 imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,
134                            const struct drm_display_mode *mode,
135                            const struct drm_display_mode *adjusted_mode)
136 {
137         struct imx8qxp_pc_channel *ch = bridge->driver_private;
138         struct imx8qxp_pc *pc = ch->pc;
139         u32 val;
140         int ret;
141
142         ret = pm_runtime_get_sync(pc->dev);
143         if (ret < 0)
144                 DRM_DEV_ERROR(pc->dev,
145                               "failed to get runtime PM sync: %d\n", ret);
146
147         ret = clk_prepare_enable(pc->clk_apb);
148         if (ret)
149                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
150                               __func__,  ret);
151
152         /* HSYNC to pixel link is active low. */
153         imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
154                              PC_DISP_HSYNC_POLARITY(ch->stream_id));
155
156         /* VSYNC to pixel link is active low. */
157         imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
158                              PC_DISP_VSYNC_POLARITY(ch->stream_id));
159
160         /* Data enable to pixel link is active high. */
161         imx8qxp_pc_write_set(pc, PC_CTRL_REG,
162                              PC_DISP_DVALID_POLARITY(ch->stream_id));
163
164         /* Mask the first frame output which may be incomplete. */
165         imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);
166
167         /* Only support RGB currently. */
168         val = imx8qxp_pc_read(pc, PC_CTRL_REG);
169         if (ch->stream_id == 0) {
170                 val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;
171                 val |= PC_DISP0_PIX_DATA_FORMAT(RGB);
172         } else {
173                 val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;
174                 val |= PC_DISP1_PIX_DATA_FORMAT(RGB);
175         }
176         imx8qxp_pc_write(pc, PC_CTRL_REG, val);
177
178         /* Only support bypass mode currently. */
179         imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));
180
181         clk_disable_unprepare(pc->clk_apb);
182 }
183
184 static void
185 imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,
186                                  struct drm_bridge_state *old_bridge_state)
187 {
188         struct imx8qxp_pc_channel *ch = bridge->driver_private;
189         struct imx8qxp_pc *pc = ch->pc;
190         int ret;
191
192         ret = pm_runtime_put(pc->dev);
193         if (ret < 0)
194                 DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);
195 }
196
197 static const u32 imx8qxp_pc_bus_output_fmts[] = {
198         MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
199         MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
200 };
201
202 static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)
203 {
204         int i;
205
206         for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {
207                 if (imx8qxp_pc_bus_output_fmts[i] == fmt)
208                         return true;
209         }
210
211         return false;
212 }
213
214 static u32 *
215 imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
216                                             struct drm_bridge_state *bridge_state,
217                                             struct drm_crtc_state *crtc_state,
218                                             struct drm_connector_state *conn_state,
219                                             u32 output_fmt,
220                                             unsigned int *num_input_fmts)
221 {
222         u32 *input_fmts;
223
224         if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))
225                 return NULL;
226
227         *num_input_fmts = 1;
228
229         input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
230         if (!input_fmts)
231                 return NULL;
232
233         switch (output_fmt) {
234         case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:
235                 input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;
236                 break;
237         case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:
238                 input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;
239                 break;
240         default:
241                 kfree(input_fmts);
242                 input_fmts = NULL;
243                 break;
244         }
245
246         return input_fmts;
247 }
248
249 static u32 *
250 imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
251                                              struct drm_bridge_state *bridge_state,
252                                              struct drm_crtc_state *crtc_state,
253                                              struct drm_connector_state *conn_state,
254                                              unsigned int *num_output_fmts)
255 {
256         *num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);
257         return kmemdup(imx8qxp_pc_bus_output_fmts,
258                         sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);
259 }
260
261 static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
262         .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
263         .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
264         .atomic_reset           = drm_atomic_helper_bridge_reset,
265         .mode_valid             = imx8qxp_pc_bridge_mode_valid,
266         .attach                 = imx8qxp_pc_bridge_attach,
267         .mode_set               = imx8qxp_pc_bridge_mode_set,
268         .atomic_disable         = imx8qxp_pc_bridge_atomic_disable,
269         .atomic_get_input_bus_fmts =
270                                 imx8qxp_pc_bridge_atomic_get_input_bus_fmts,
271         .atomic_get_output_bus_fmts =
272                                 imx8qxp_pc_bridge_atomic_get_output_bus_fmts,
273 };
274
275 static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
276 {
277         struct imx8qxp_pc *pc;
278         struct imx8qxp_pc_channel *ch;
279         struct device *dev = &pdev->dev;
280         struct device_node *np = dev->of_node;
281         struct device_node *child, *remote;
282         u32 i;
283         int ret;
284
285         pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
286         if (!pc)
287                 return -ENOMEM;
288
289         pc->base = devm_platform_ioremap_resource(pdev, 0);
290         if (IS_ERR(pc->base))
291                 return PTR_ERR(pc->base);
292
293         pc->dev = dev;
294
295         pc->clk_apb = devm_clk_get(dev, "apb");
296         if (IS_ERR(pc->clk_apb)) {
297                 ret = PTR_ERR(pc->clk_apb);
298                 if (ret != -EPROBE_DEFER)
299                         DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);
300                 return ret;
301         }
302
303         platform_set_drvdata(pdev, pc);
304         pm_runtime_enable(dev);
305
306         for_each_available_child_of_node(np, child) {
307                 ret = of_property_read_u32(child, "reg", &i);
308                 if (ret || i > 1) {
309                         ret = -EINVAL;
310                         DRM_DEV_ERROR(dev,
311                                       "invalid channel(%u) node address\n", i);
312                         goto free_child;
313                 }
314
315                 ch = &pc->ch[i];
316                 ch->pc = pc;
317                 ch->stream_id = i;
318
319                 remote = of_graph_get_remote_node(child, 1, 0);
320                 if (!remote) {
321                         ret = -ENODEV;
322                         DRM_DEV_ERROR(dev,
323                                       "channel%u failed to get port1's remote node: %d\n",
324                                       i, ret);
325                         goto free_child;
326                 }
327
328                 ch->next_bridge = of_drm_find_bridge(remote);
329                 if (!ch->next_bridge) {
330                         of_node_put(remote);
331                         ret = -EPROBE_DEFER;
332                         DRM_DEV_DEBUG_DRIVER(dev,
333                                              "channel%u failed to find next bridge: %d\n",
334                                              i, ret);
335                         goto free_child;
336                 }
337
338                 of_node_put(remote);
339
340                 ch->bridge.driver_private = ch;
341                 ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;
342                 ch->bridge.of_node = child;
343                 ch->is_available = true;
344
345                 drm_bridge_add(&ch->bridge);
346         }
347
348         return 0;
349
350 free_child:
351         of_node_put(child);
352
353         if (i == 1 && pc->ch[0].next_bridge)
354                 drm_bridge_remove(&pc->ch[0].bridge);
355
356         pm_runtime_disable(dev);
357         return ret;
358 }
359
360 static void imx8qxp_pc_bridge_remove(struct platform_device *pdev)
361 {
362         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
363         struct imx8qxp_pc_channel *ch;
364         int i;
365
366         for (i = 0; i < 2; i++) {
367                 ch = &pc->ch[i];
368
369                 if (!ch->is_available)
370                         continue;
371
372                 drm_bridge_remove(&ch->bridge);
373                 ch->is_available = false;
374         }
375
376         pm_runtime_disable(&pdev->dev);
377 }
378
379 static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
380 {
381         struct platform_device *pdev = to_platform_device(dev);
382         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
383         int ret;
384
385         ret = clk_prepare_enable(pc->clk_apb);
386         if (ret)
387                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
388                               __func__,  ret);
389
390         /* Disable pixel combiner by full reset. */
391         imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
392
393         clk_disable_unprepare(pc->clk_apb);
394
395         /* Ensure the reset takes effect. */
396         usleep_range(10, 20);
397
398         return ret;
399 }
400
401 static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
402 {
403         struct platform_device *pdev = to_platform_device(dev);
404         struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
405         int ret;
406
407         ret = clk_prepare_enable(pc->clk_apb);
408         if (ret) {
409                 DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
410                               __func__, ret);
411                 return ret;
412         }
413
414         /* out of reset */
415         imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
416
417         clk_disable_unprepare(pc->clk_apb);
418
419         return ret;
420 }
421
422 static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
423         SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,
424                            imx8qxp_pc_runtime_resume, NULL)
425 };
426
427 static const struct of_device_id imx8qxp_pc_dt_ids[] = {
428         { .compatible = "fsl,imx8qm-pixel-combiner", },
429         { .compatible = "fsl,imx8qxp-pixel-combiner", },
430         { /* sentinel */ }
431 };
432 MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
433
434 static struct platform_driver imx8qxp_pc_bridge_driver = {
435         .probe  = imx8qxp_pc_bridge_probe,
436         .remove_new = imx8qxp_pc_bridge_remove,
437         .driver = {
438                 .pm = &imx8qxp_pc_pm_ops,
439                 .name = DRIVER_NAME,
440                 .of_match_table = imx8qxp_pc_dt_ids,
441         },
442 };
443 module_platform_driver(imx8qxp_pc_bridge_driver);
444
445 MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
446 MODULE_AUTHOR("Liu Ying <[email protected]>");
447 MODULE_LICENSE("GPL v2");
448 MODULE_ALIAS("platform:" DRIVER_NAME);
This page took 0.062247 seconds and 4 git commands to generate.