1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2021 MediaTek Inc. |
4 | */ |
5 | |
6 | #include <drm/drm_fourcc.h> |
7 | #include <drm/drm_of.h> |
8 | #include <linux/clk.h> |
9 | #include <linux/component.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/of_platform.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <linux/reset.h> |
16 | #include <linux/soc/mediatek/mtk-cmdq.h> |
17 | #include <linux/soc/mediatek/mtk-mmsys.h> |
18 | #include <linux/soc/mediatek/mtk-mutex.h> |
19 | |
20 | #include "mtk_disp_drv.h" |
21 | #include "mtk_drm_crtc.h" |
22 | #include "mtk_drm_ddp_comp.h" |
23 | #include "mtk_drm_drv.h" |
24 | #include "mtk_ethdr.h" |
25 | |
26 | #define MTK_OVL_ADAPTOR_RDMA_MAX_WIDTH 1920 |
27 | #define MTK_OVL_ADAPTOR_LAYER_NUM 4 |
28 | |
29 | enum mtk_ovl_adaptor_comp_type { |
30 | OVL_ADAPTOR_TYPE_ETHDR, |
31 | OVL_ADAPTOR_TYPE_MDP_RDMA, |
32 | OVL_ADAPTOR_TYPE_MERGE, |
33 | OVL_ADAPTOR_TYPE_PADDING, |
34 | OVL_ADAPTOR_TYPE_NUM, |
35 | }; |
36 | |
37 | enum mtk_ovl_adaptor_comp_id { |
38 | OVL_ADAPTOR_ETHDR0, |
39 | OVL_ADAPTOR_MDP_RDMA0, |
40 | OVL_ADAPTOR_MDP_RDMA1, |
41 | OVL_ADAPTOR_MDP_RDMA2, |
42 | OVL_ADAPTOR_MDP_RDMA3, |
43 | OVL_ADAPTOR_MDP_RDMA4, |
44 | OVL_ADAPTOR_MDP_RDMA5, |
45 | OVL_ADAPTOR_MDP_RDMA6, |
46 | OVL_ADAPTOR_MDP_RDMA7, |
47 | OVL_ADAPTOR_MERGE0, |
48 | OVL_ADAPTOR_MERGE1, |
49 | OVL_ADAPTOR_MERGE2, |
50 | OVL_ADAPTOR_MERGE3, |
51 | OVL_ADAPTOR_PADDING0, |
52 | OVL_ADAPTOR_PADDING1, |
53 | OVL_ADAPTOR_PADDING2, |
54 | OVL_ADAPTOR_PADDING3, |
55 | OVL_ADAPTOR_PADDING4, |
56 | OVL_ADAPTOR_PADDING5, |
57 | OVL_ADAPTOR_PADDING6, |
58 | OVL_ADAPTOR_PADDING7, |
59 | OVL_ADAPTOR_ID_MAX |
60 | }; |
61 | |
62 | struct ovl_adaptor_comp_match { |
63 | enum mtk_ovl_adaptor_comp_type type; |
64 | enum mtk_ddp_comp_id comp_id; |
65 | int alias_id; |
66 | const struct mtk_ddp_comp_funcs *funcs; |
67 | }; |
68 | |
69 | struct mtk_disp_ovl_adaptor { |
70 | struct device *ovl_adaptor_comp[OVL_ADAPTOR_ID_MAX]; |
71 | struct device *mmsys_dev; |
72 | bool children_bound; |
73 | }; |
74 | |
75 | static const char * const private_comp_stem[OVL_ADAPTOR_TYPE_NUM] = { |
76 | [OVL_ADAPTOR_TYPE_ETHDR] = "ethdr" , |
77 | [OVL_ADAPTOR_TYPE_MDP_RDMA] = "vdo1-rdma" , |
78 | [OVL_ADAPTOR_TYPE_MERGE] = "merge" , |
79 | [OVL_ADAPTOR_TYPE_PADDING] = "padding" , |
80 | }; |
81 | |
82 | static const struct mtk_ddp_comp_funcs ethdr = { |
83 | .clk_enable = mtk_ethdr_clk_enable, |
84 | .clk_disable = mtk_ethdr_clk_disable, |
85 | .start = mtk_ethdr_start, |
86 | .stop = mtk_ethdr_stop, |
87 | }; |
88 | |
89 | static const struct mtk_ddp_comp_funcs merge = { |
90 | .clk_enable = mtk_merge_clk_enable, |
91 | .clk_disable = mtk_merge_clk_disable, |
92 | .mode_valid = mtk_merge_mode_valid, |
93 | }; |
94 | |
95 | static const struct mtk_ddp_comp_funcs padding = { |
96 | .clk_enable = mtk_padding_clk_enable, |
97 | .clk_disable = mtk_padding_clk_disable, |
98 | .start = mtk_padding_start, |
99 | .stop = mtk_padding_stop, |
100 | }; |
101 | |
102 | static const struct mtk_ddp_comp_funcs rdma = { |
103 | .power_on = mtk_mdp_rdma_power_on, |
104 | .power_off = mtk_mdp_rdma_power_off, |
105 | .clk_enable = mtk_mdp_rdma_clk_enable, |
106 | .clk_disable = mtk_mdp_rdma_clk_disable, |
107 | }; |
108 | |
109 | static const struct ovl_adaptor_comp_match comp_matches[OVL_ADAPTOR_ID_MAX] = { |
110 | [OVL_ADAPTOR_ETHDR0] = { .type: OVL_ADAPTOR_TYPE_ETHDR, .comp_id: DDP_COMPONENT_ETHDR_MIXER, .alias_id: 0, .funcs: ðdr }, |
111 | [OVL_ADAPTOR_MDP_RDMA0] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA0, .alias_id: 0, .funcs: &rdma }, |
112 | [OVL_ADAPTOR_MDP_RDMA1] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA1, .alias_id: 1, .funcs: &rdma }, |
113 | [OVL_ADAPTOR_MDP_RDMA2] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA2, .alias_id: 2, .funcs: &rdma }, |
114 | [OVL_ADAPTOR_MDP_RDMA3] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA3, .alias_id: 3, .funcs: &rdma }, |
115 | [OVL_ADAPTOR_MDP_RDMA4] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA4, .alias_id: 4, .funcs: &rdma }, |
116 | [OVL_ADAPTOR_MDP_RDMA5] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA5, .alias_id: 5, .funcs: &rdma }, |
117 | [OVL_ADAPTOR_MDP_RDMA6] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA6, .alias_id: 6, .funcs: &rdma }, |
118 | [OVL_ADAPTOR_MDP_RDMA7] = { .type: OVL_ADAPTOR_TYPE_MDP_RDMA, .comp_id: DDP_COMPONENT_MDP_RDMA7, .alias_id: 7, .funcs: &rdma }, |
119 | [OVL_ADAPTOR_MERGE0] = { .type: OVL_ADAPTOR_TYPE_MERGE, .comp_id: DDP_COMPONENT_MERGE1, .alias_id: 1, .funcs: &merge }, |
120 | [OVL_ADAPTOR_MERGE1] = { .type: OVL_ADAPTOR_TYPE_MERGE, .comp_id: DDP_COMPONENT_MERGE2, .alias_id: 2, .funcs: &merge }, |
121 | [OVL_ADAPTOR_MERGE2] = { .type: OVL_ADAPTOR_TYPE_MERGE, .comp_id: DDP_COMPONENT_MERGE3, .alias_id: 3, .funcs: &merge }, |
122 | [OVL_ADAPTOR_MERGE3] = { .type: OVL_ADAPTOR_TYPE_MERGE, .comp_id: DDP_COMPONENT_MERGE4, .alias_id: 4, .funcs: &merge }, |
123 | [OVL_ADAPTOR_PADDING0] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING0, .alias_id: 0, .funcs: &padding }, |
124 | [OVL_ADAPTOR_PADDING1] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING1, .alias_id: 1, .funcs: &padding }, |
125 | [OVL_ADAPTOR_PADDING2] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING2, .alias_id: 2, .funcs: &padding }, |
126 | [OVL_ADAPTOR_PADDING3] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING3, .alias_id: 3, .funcs: &padding }, |
127 | [OVL_ADAPTOR_PADDING4] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING4, .alias_id: 4, .funcs: &padding }, |
128 | [OVL_ADAPTOR_PADDING5] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING5, .alias_id: 5, .funcs: &padding }, |
129 | [OVL_ADAPTOR_PADDING6] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING6, .alias_id: 6, .funcs: &padding }, |
130 | [OVL_ADAPTOR_PADDING7] = { .type: OVL_ADAPTOR_TYPE_PADDING, .comp_id: DDP_COMPONENT_PADDING7, .alias_id: 7, .funcs: &padding }, |
131 | }; |
132 | |
133 | void mtk_ovl_adaptor_layer_config(struct device *dev, unsigned int idx, |
134 | struct mtk_plane_state *state, |
135 | struct cmdq_pkt *cmdq_pkt) |
136 | { |
137 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
138 | struct mtk_plane_pending_state *pending = &state->pending; |
139 | struct mtk_mdp_rdma_cfg rdma_config = {0}; |
140 | struct device *rdma_l; |
141 | struct device *rdma_r; |
142 | struct device *merge; |
143 | struct device *ethdr; |
144 | const struct drm_format_info *fmt_info = drm_format_info(format: pending->format); |
145 | bool use_dual_pipe = false; |
146 | unsigned int align_width; |
147 | unsigned int l_w = 0; |
148 | unsigned int r_w = 0; |
149 | |
150 | dev_dbg(dev, "%s+ idx:%d, enable:%d, fmt:0x%x\n" , __func__, idx, |
151 | pending->enable, pending->format); |
152 | dev_dbg(dev, "addr 0x%pad, fb w:%d, {%d,%d,%d,%d}\n" , |
153 | &pending->addr, (pending->pitch / fmt_info->cpp[0]), |
154 | pending->x, pending->y, pending->width, pending->height); |
155 | |
156 | rdma_l = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0 + 2 * idx]; |
157 | rdma_r = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0 + 2 * idx + 1]; |
158 | merge = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MERGE0 + idx]; |
159 | ethdr = ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]; |
160 | |
161 | if (!pending->enable) { |
162 | mtk_merge_stop_cmdq(dev: merge, cmdq_pkt); |
163 | mtk_mdp_rdma_stop(dev: rdma_l, cmdq_pkt); |
164 | mtk_mdp_rdma_stop(dev: rdma_r, cmdq_pkt); |
165 | mtk_ethdr_layer_config(dev: ethdr, idx, state, cmdq_pkt); |
166 | return; |
167 | } |
168 | |
169 | /* ETHDR is in 1T2P domain, width needs to be 2 pixels align */ |
170 | align_width = ALIGN_DOWN(pending->width, 2); |
171 | |
172 | if (align_width > MTK_OVL_ADAPTOR_RDMA_MAX_WIDTH) |
173 | use_dual_pipe = true; |
174 | |
175 | if (use_dual_pipe) { |
176 | l_w = (align_width / 2) + ((pending->width / 2) % 2); |
177 | r_w = align_width - l_w; |
178 | } else { |
179 | l_w = align_width; |
180 | } |
181 | mtk_merge_advance_config(dev: merge, l_w, r_w, h: pending->height, vrefresh: 0, bpc: 0, cmdq_pkt); |
182 | mtk_mmsys_merge_async_config(dev: ovl_adaptor->mmsys_dev, idx, width: align_width / 2, |
183 | height: pending->height, cmdq_pkt); |
184 | |
185 | rdma_config.width = l_w; |
186 | rdma_config.height = pending->height; |
187 | rdma_config.addr0 = pending->addr; |
188 | rdma_config.pitch = pending->pitch; |
189 | rdma_config.fmt = pending->format; |
190 | rdma_config.color_encoding = pending->color_encoding; |
191 | mtk_mdp_rdma_config(dev: rdma_l, cfg: &rdma_config, cmdq_pkt); |
192 | |
193 | if (use_dual_pipe) { |
194 | rdma_config.x_left = l_w; |
195 | rdma_config.width = r_w; |
196 | mtk_mdp_rdma_config(dev: rdma_r, cfg: &rdma_config, cmdq_pkt); |
197 | } |
198 | |
199 | mtk_merge_start_cmdq(dev: merge, cmdq_pkt); |
200 | |
201 | mtk_mdp_rdma_start(dev: rdma_l, cmdq_pkt); |
202 | if (use_dual_pipe) |
203 | mtk_mdp_rdma_start(dev: rdma_r, cmdq_pkt); |
204 | else |
205 | mtk_mdp_rdma_stop(dev: rdma_r, cmdq_pkt); |
206 | |
207 | mtk_ethdr_layer_config(dev: ethdr, idx, state, cmdq_pkt); |
208 | } |
209 | |
210 | void mtk_ovl_adaptor_config(struct device *dev, unsigned int w, |
211 | unsigned int h, unsigned int vrefresh, |
212 | unsigned int bpc, struct cmdq_pkt *cmdq_pkt) |
213 | { |
214 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
215 | |
216 | mtk_ethdr_config(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0], w, h, |
217 | vrefresh, bpc, cmdq_pkt); |
218 | } |
219 | |
220 | void mtk_ovl_adaptor_start(struct device *dev) |
221 | { |
222 | int i; |
223 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
224 | |
225 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
226 | if (!ovl_adaptor->ovl_adaptor_comp[i] || |
227 | !comp_matches[i].funcs->start) |
228 | continue; |
229 | |
230 | comp_matches[i].funcs->start(ovl_adaptor->ovl_adaptor_comp[i]); |
231 | } |
232 | } |
233 | |
234 | void mtk_ovl_adaptor_stop(struct device *dev) |
235 | { |
236 | int i; |
237 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
238 | |
239 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
240 | if (!ovl_adaptor->ovl_adaptor_comp[i] || |
241 | !comp_matches[i].funcs->stop) |
242 | continue; |
243 | |
244 | comp_matches[i].funcs->stop(ovl_adaptor->ovl_adaptor_comp[i]); |
245 | } |
246 | } |
247 | |
248 | /** |
249 | * power_off - Power off the devices in OVL adaptor |
250 | * @dev: Device to be powered off |
251 | * @num: Number of the devices to be powered off |
252 | * |
253 | * Calls the .power_off() ovl_adaptor component callback if it is present. |
254 | */ |
255 | static inline void power_off(struct device *dev, int num) |
256 | { |
257 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
258 | int i; |
259 | |
260 | if (num > OVL_ADAPTOR_ID_MAX) |
261 | num = OVL_ADAPTOR_ID_MAX; |
262 | |
263 | for (i = num - 1; i >= 0; i--) { |
264 | if (!ovl_adaptor->ovl_adaptor_comp[i] || |
265 | !comp_matches[i].funcs->power_off) |
266 | continue; |
267 | |
268 | comp_matches[i].funcs->power_off(ovl_adaptor->ovl_adaptor_comp[i]); |
269 | } |
270 | } |
271 | |
272 | /** |
273 | * mtk_ovl_adaptor_power_on - Power on the devices in OVL adaptor |
274 | * @dev: Device to be powered on |
275 | * |
276 | * Different from OVL, OVL adaptor is a pseudo device so |
277 | * we didn't define it in the device tree, pm_runtime_resume_and_get() |
278 | * called by .atomic_enable() power on no device in OVL adaptor, |
279 | * we have to implement a function to do the job instead. |
280 | * |
281 | * Return: Zero for success or negative number for failure. |
282 | */ |
283 | int mtk_ovl_adaptor_power_on(struct device *dev) |
284 | { |
285 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
286 | int i, ret; |
287 | |
288 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
289 | if (!ovl_adaptor->ovl_adaptor_comp[i] || |
290 | !comp_matches[i].funcs->power_on) |
291 | continue; |
292 | |
293 | ret = comp_matches[i].funcs->power_on(ovl_adaptor->ovl_adaptor_comp[i]); |
294 | if (ret < 0) { |
295 | dev_err(dev, "Failed to enable power domain %d, err %d\n" , i, ret); |
296 | power_off(dev, num: i); |
297 | return ret; |
298 | } |
299 | } |
300 | return 0; |
301 | } |
302 | |
303 | void mtk_ovl_adaptor_power_off(struct device *dev) |
304 | { |
305 | power_off(dev, num: OVL_ADAPTOR_ID_MAX); |
306 | } |
307 | |
308 | int mtk_ovl_adaptor_clk_enable(struct device *dev) |
309 | { |
310 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
311 | struct device *comp; |
312 | int ret; |
313 | int i; |
314 | |
315 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
316 | comp = ovl_adaptor->ovl_adaptor_comp[i]; |
317 | if (!comp || !comp_matches[i].funcs->clk_enable) |
318 | continue; |
319 | ret = comp_matches[i].funcs->clk_enable(comp); |
320 | if (ret) { |
321 | dev_err(dev, "Failed to enable clock %d, err %d\n" , i, ret); |
322 | while (--i >= 0) |
323 | comp_matches[i].funcs->clk_disable(comp); |
324 | return ret; |
325 | } |
326 | } |
327 | return 0; |
328 | } |
329 | |
330 | void mtk_ovl_adaptor_clk_disable(struct device *dev) |
331 | { |
332 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
333 | struct device *comp; |
334 | int i; |
335 | |
336 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
337 | comp = ovl_adaptor->ovl_adaptor_comp[i]; |
338 | if (!comp || !comp_matches[i].funcs->clk_disable) |
339 | continue; |
340 | comp_matches[i].funcs->clk_disable(comp); |
341 | if (i < OVL_ADAPTOR_MERGE0) |
342 | pm_runtime_put(dev: comp); |
343 | } |
344 | } |
345 | |
346 | enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev, |
347 | const struct drm_display_mode *mode) |
348 | |
349 | { |
350 | int i; |
351 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
352 | |
353 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
354 | dev = ovl_adaptor->ovl_adaptor_comp[i]; |
355 | if (!dev || !comp_matches[i].funcs->mode_valid) |
356 | continue; |
357 | return comp_matches[i].funcs->mode_valid(dev, mode); |
358 | } |
359 | return MODE_OK; |
360 | } |
361 | |
362 | unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev) |
363 | { |
364 | return MTK_OVL_ADAPTOR_LAYER_NUM; |
365 | } |
366 | |
367 | struct device *mtk_ovl_adaptor_dma_dev_get(struct device *dev) |
368 | { |
369 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
370 | |
371 | return ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]; |
372 | } |
373 | |
374 | void mtk_ovl_adaptor_register_vblank_cb(struct device *dev, void (*vblank_cb)(void *), |
375 | void *vblank_cb_data) |
376 | { |
377 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
378 | |
379 | mtk_ethdr_register_vblank_cb(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0], |
380 | vblank_cb, vblank_cb_data); |
381 | } |
382 | |
383 | void mtk_ovl_adaptor_unregister_vblank_cb(struct device *dev) |
384 | { |
385 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
386 | |
387 | mtk_ethdr_unregister_vblank_cb(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]); |
388 | } |
389 | |
390 | void mtk_ovl_adaptor_enable_vblank(struct device *dev) |
391 | { |
392 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
393 | |
394 | mtk_ethdr_enable_vblank(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]); |
395 | } |
396 | |
397 | void mtk_ovl_adaptor_disable_vblank(struct device *dev) |
398 | { |
399 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
400 | |
401 | mtk_ethdr_disable_vblank(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0]); |
402 | } |
403 | |
404 | const u32 *mtk_ovl_adaptor_get_formats(struct device *dev) |
405 | { |
406 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
407 | |
408 | return mtk_mdp_rdma_get_formats(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]); |
409 | } |
410 | |
411 | size_t mtk_ovl_adaptor_get_num_formats(struct device *dev) |
412 | { |
413 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
414 | |
415 | return mtk_mdp_rdma_get_num_formats(dev: ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]); |
416 | } |
417 | |
418 | void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex) |
419 | { |
420 | int i; |
421 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
422 | |
423 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
424 | if (!ovl_adaptor->ovl_adaptor_comp[i]) |
425 | continue; |
426 | mtk_mutex_add_comp(mutex, id: comp_matches[i].comp_id); |
427 | } |
428 | } |
429 | |
430 | void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex) |
431 | { |
432 | int i; |
433 | struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev); |
434 | |
435 | for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) { |
436 | if (!ovl_adaptor->ovl_adaptor_comp[i]) |
437 | continue; |
438 | mtk_mutex_remove_comp(mutex, id: comp_matches[i].comp_id); |
439 | } |
440 | } |
441 | |
442 | void mtk_ovl_adaptor_connect(struct device *dev, struct device *mmsys_dev, unsigned int next) |
443 | { |
444 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_ETHDR_MIXER, next); |
445 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA0, next: DDP_COMPONENT_MERGE1); |
446 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA1, next: DDP_COMPONENT_MERGE1); |
447 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA2, next: DDP_COMPONENT_MERGE2); |
448 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE1, next: DDP_COMPONENT_ETHDR_MIXER); |
449 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE2, next: DDP_COMPONENT_ETHDR_MIXER); |
450 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE3, next: DDP_COMPONENT_ETHDR_MIXER); |
451 | mtk_mmsys_ddp_connect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE4, next: DDP_COMPONENT_ETHDR_MIXER); |
452 | } |
453 | |
454 | void mtk_ovl_adaptor_disconnect(struct device *dev, struct device *mmsys_dev, unsigned int next) |
455 | { |
456 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_ETHDR_MIXER, next); |
457 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA0, next: DDP_COMPONENT_MERGE1); |
458 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA1, next: DDP_COMPONENT_MERGE1); |
459 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MDP_RDMA2, next: DDP_COMPONENT_MERGE2); |
460 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE1, next: DDP_COMPONENT_ETHDR_MIXER); |
461 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE2, next: DDP_COMPONENT_ETHDR_MIXER); |
462 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE3, next: DDP_COMPONENT_ETHDR_MIXER); |
463 | mtk_mmsys_ddp_disconnect(dev: mmsys_dev, cur: DDP_COMPONENT_MERGE4, next: DDP_COMPONENT_ETHDR_MIXER); |
464 | } |
465 | |
466 | static int ovl_adaptor_comp_get_id(struct device *dev, struct device_node *node, |
467 | enum mtk_ovl_adaptor_comp_type type) |
468 | { |
469 | int alias_id = of_alias_get_id(np: node, stem: private_comp_stem[type]); |
470 | int i; |
471 | |
472 | for (i = 0; i < ARRAY_SIZE(comp_matches); i++) |
473 | if (comp_matches[i].type == type && |
474 | comp_matches[i].alias_id == alias_id) |
475 | return i; |
476 | |
477 | dev_warn(dev, "Failed to get id. type: %d, alias: %d\n" , type, alias_id); |
478 | return -EINVAL; |
479 | } |
480 | |
481 | static const struct of_device_id mtk_ovl_adaptor_comp_dt_ids[] = { |
482 | { .compatible = "mediatek,mt8188-disp-padding" , .data = (void *)OVL_ADAPTOR_TYPE_PADDING }, |
483 | { .compatible = "mediatek,mt8195-disp-ethdr" , .data = (void *)OVL_ADAPTOR_TYPE_ETHDR }, |
484 | { .compatible = "mediatek,mt8195-disp-merge" , .data = (void *)OVL_ADAPTOR_TYPE_MERGE }, |
485 | { .compatible = "mediatek,mt8195-vdo1-rdma" , .data = (void *)OVL_ADAPTOR_TYPE_MDP_RDMA }, |
486 | { /* sentinel */ } |
487 | }; |
488 | |
489 | static int compare_of(struct device *dev, void *data) |
490 | { |
491 | return dev->of_node == data; |
492 | } |
493 | |
494 | static int ovl_adaptor_comp_init(struct device *dev, struct component_match **match) |
495 | { |
496 | struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); |
497 | struct device_node *node, *parent; |
498 | struct platform_device *comp_pdev; |
499 | |
500 | parent = dev->parent->parent->of_node->parent; |
501 | |
502 | for_each_child_of_node(parent, node) { |
503 | const struct of_device_id *of_id; |
504 | enum mtk_ovl_adaptor_comp_type type; |
505 | int id; |
506 | |
507 | of_id = of_match_node(matches: mtk_ovl_adaptor_comp_dt_ids, node); |
508 | if (!of_id) |
509 | continue; |
510 | |
511 | if (!of_device_is_available(device: node)) { |
512 | dev_dbg(dev, "Skipping disabled component %pOF\n" , |
513 | node); |
514 | continue; |
515 | } |
516 | |
517 | type = (enum mtk_ovl_adaptor_comp_type)(uintptr_t)of_id->data; |
518 | id = ovl_adaptor_comp_get_id(dev, node, type); |
519 | if (id < 0) { |
520 | dev_warn(dev, "Skipping unknown component %pOF\n" , |
521 | node); |
522 | continue; |
523 | } |
524 | |
525 | comp_pdev = of_find_device_by_node(np: node); |
526 | if (!comp_pdev) |
527 | return -EPROBE_DEFER; |
528 | |
529 | priv->ovl_adaptor_comp[id] = &comp_pdev->dev; |
530 | |
531 | drm_of_component_match_add(master: dev, matchptr: match, compare: compare_of, node); |
532 | dev_dbg(dev, "Adding component match for %pOF\n" , node); |
533 | } |
534 | |
535 | if (!*match) { |
536 | dev_err(dev, "No match device for ovl_adaptor\n" ); |
537 | return -ENODEV; |
538 | } |
539 | |
540 | return 0; |
541 | } |
542 | |
543 | static int mtk_disp_ovl_adaptor_comp_bind(struct device *dev, struct device *master, |
544 | void *data) |
545 | { |
546 | struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); |
547 | |
548 | if (!priv->children_bound) |
549 | return -EPROBE_DEFER; |
550 | |
551 | return 0; |
552 | } |
553 | |
554 | static void mtk_disp_ovl_adaptor_comp_unbind(struct device *dev, struct device *master, |
555 | void *data) |
556 | { |
557 | } |
558 | |
559 | static const struct component_ops mtk_disp_ovl_adaptor_comp_ops = { |
560 | .bind = mtk_disp_ovl_adaptor_comp_bind, |
561 | .unbind = mtk_disp_ovl_adaptor_comp_unbind, |
562 | }; |
563 | |
564 | static int mtk_disp_ovl_adaptor_master_bind(struct device *dev) |
565 | { |
566 | struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); |
567 | int ret; |
568 | |
569 | ret = component_bind_all(parent: dev, data: priv->mmsys_dev); |
570 | if (ret) |
571 | return dev_err_probe(dev, err: ret, fmt: "component_bind_all failed!\n" ); |
572 | |
573 | priv->children_bound = true; |
574 | return 0; |
575 | } |
576 | |
577 | static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev) |
578 | { |
579 | struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); |
580 | |
581 | priv->children_bound = false; |
582 | } |
583 | |
584 | static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = { |
585 | .bind = mtk_disp_ovl_adaptor_master_bind, |
586 | .unbind = mtk_disp_ovl_adaptor_master_unbind, |
587 | }; |
588 | |
589 | static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) |
590 | { |
591 | struct mtk_disp_ovl_adaptor *priv; |
592 | struct device *dev = &pdev->dev; |
593 | struct component_match *match = NULL; |
594 | int ret; |
595 | |
596 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
597 | if (!priv) |
598 | return -ENOMEM; |
599 | |
600 | platform_set_drvdata(pdev, data: priv); |
601 | |
602 | ret = ovl_adaptor_comp_init(dev, match: &match); |
603 | if (ret < 0) |
604 | return ret; |
605 | |
606 | priv->mmsys_dev = pdev->dev.platform_data; |
607 | |
608 | component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); |
609 | |
610 | pm_runtime_enable(dev); |
611 | |
612 | ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops); |
613 | if (ret != 0) { |
614 | pm_runtime_disable(dev); |
615 | dev_err(dev, "Failed to add component: %d\n" , ret); |
616 | } |
617 | |
618 | return ret; |
619 | } |
620 | |
621 | static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) |
622 | { |
623 | component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops); |
624 | pm_runtime_disable(dev: &pdev->dev); |
625 | } |
626 | |
627 | struct platform_driver mtk_disp_ovl_adaptor_driver = { |
628 | .probe = mtk_disp_ovl_adaptor_probe, |
629 | .remove_new = mtk_disp_ovl_adaptor_remove, |
630 | .driver = { |
631 | .name = "mediatek-disp-ovl-adaptor" , |
632 | .owner = THIS_MODULE, |
633 | }, |
634 | }; |
635 | |