xref: /openbmc/linux/drivers/gpu/drm/mediatek/mtk_drm_crtc.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2119f5173SCK Hu /*
3119f5173SCK Hu  * Copyright (c) 2015 MediaTek Inc.
4119f5173SCK Hu  */
5119f5173SCK Hu 
69aef5867SSam Ravnborg #include <linux/clk.h>
71ee07a68SChun-Kuang Hu #include <linux/dma-mapping.h>
81ee07a68SChun-Kuang Hu #include <linux/mailbox_controller.h>
9722d4f06SRob Herring #include <linux/of.h>
109aef5867SSam Ravnborg #include <linux/pm_runtime.h>
112f965be7SBibby Hsieh #include <linux/soc/mediatek/mtk-cmdq.h>
122c758e30SEnric Balletbo i Serra #include <linux/soc/mediatek/mtk-mmsys.h>
13e1e4f7feSCK Hu #include <linux/soc/mediatek/mtk-mutex.h>
149aef5867SSam Ravnborg 
15119f5173SCK Hu #include <asm/barrier.h>
169aef5867SSam Ravnborg 
17253f28b6SMaxime Ripard #include <drm/drm_atomic.h>
18119f5173SCK Hu #include <drm/drm_atomic_helper.h>
19fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
209aef5867SSam Ravnborg #include <drm/drm_vblank.h>
21119f5173SCK Hu 
22119f5173SCK Hu #include "mtk_drm_drv.h"
23119f5173SCK Hu #include "mtk_drm_crtc.h"
24119f5173SCK Hu #include "mtk_drm_ddp_comp.h"
25119f5173SCK Hu #include "mtk_drm_gem.h"
26119f5173SCK Hu #include "mtk_drm_plane.h"
27119f5173SCK Hu 
28eaa4d562SLee Jones /*
29119f5173SCK Hu  * struct mtk_drm_crtc - MediaTek specific crtc structure.
30119f5173SCK Hu  * @base: crtc object.
31119f5173SCK Hu  * @enabled: records whether crtc_enable succeeded
325bfafad8SDaniel Kurtz  * @planes: array of 4 drm_plane structures, one for each overlay plane
33119f5173SCK Hu  * @pending_planes: whether any plane has pending changes to be applied
342c758e30SEnric Balletbo i Serra  * @mmsys_dev: pointer to the mmsys device for configuration registers
35119f5173SCK Hu  * @mutex: handle to one of the ten disp_mutex streams
36119f5173SCK Hu  * @ddp_comp_nr: number of components in ddp_comp
37119f5173SCK Hu  * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
38eaa4d562SLee Jones  *
39eaa4d562SLee Jones  * TODO: Needs update: this header is missing a bunch of member descriptions.
40119f5173SCK Hu  */
41119f5173SCK Hu struct mtk_drm_crtc {
42119f5173SCK Hu 	struct drm_crtc			base;
43119f5173SCK Hu 	bool				enabled;
44119f5173SCK Hu 
45119f5173SCK Hu 	bool				pending_needs_vblank;
46119f5173SCK Hu 	struct drm_pending_vblank_event	*event;
47119f5173SCK Hu 
4866b2cf96SStu Hsieh 	struct drm_plane		*planes;
4966b2cf96SStu Hsieh 	unsigned int			layer_nr;
50119f5173SCK Hu 	bool				pending_planes;
51920fffccSBibby Hsieh 	bool				pending_async_planes;
52119f5173SCK Hu 
532f965be7SBibby Hsieh #if IS_REACHABLE(CONFIG_MTK_CMDQ)
54563c9d4aSChun-Kuang Hu 	struct cmdq_client		cmdq_client;
557627122fSChun-Kuang Hu 	struct cmdq_pkt			cmdq_handle;
562f965be7SBibby Hsieh 	u32				cmdq_event;
57eaf80126SChun-Kuang Hu 	u32				cmdq_vblank_cnt;
58aa2d5f2fSjason-jh.lin 	wait_queue_head_t		cb_blocking_queue;
592f965be7SBibby Hsieh #endif
602f965be7SBibby Hsieh 
612c758e30SEnric Balletbo i Serra 	struct device			*mmsys_dev;
62cb1d6bccSNancy.Lin 	struct device			*dma_dev;
634971593fSCK Hu 	struct mtk_mutex		*mutex;
64119f5173SCK Hu 	unsigned int			ddp_comp_nr;
65119f5173SCK Hu 	struct mtk_ddp_comp		**ddp_comp;
66920fffccSBibby Hsieh 
67920fffccSBibby Hsieh 	/* lock for display hardware access */
68920fffccSBibby Hsieh 	struct mutex			hw_lock;
69368166ecSChun-Kuang Hu 	bool				config_updating;
705b9b8cd2SJason-JH.Lin 	/* lock for config_updating to cmd buffer */
715b9b8cd2SJason-JH.Lin 	spinlock_t			config_lock;
72119f5173SCK Hu };
73119f5173SCK Hu 
74119f5173SCK Hu struct mtk_crtc_state {
75119f5173SCK Hu 	struct drm_crtc_state		base;
76119f5173SCK Hu 
77119f5173SCK Hu 	bool				pending_config;
78119f5173SCK Hu 	unsigned int			pending_width;
79119f5173SCK Hu 	unsigned int			pending_height;
80119f5173SCK Hu 	unsigned int			pending_vrefresh;
81119f5173SCK Hu };
82119f5173SCK Hu 
to_mtk_crtc(struct drm_crtc * c)83119f5173SCK Hu static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c)
84119f5173SCK Hu {
85119f5173SCK Hu 	return container_of(c, struct mtk_drm_crtc, base);
86119f5173SCK Hu }
87119f5173SCK Hu 
to_mtk_crtc_state(struct drm_crtc_state * s)88119f5173SCK Hu static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s)
89119f5173SCK Hu {
90119f5173SCK Hu 	return container_of(s, struct mtk_crtc_state, base);
91119f5173SCK Hu }
92119f5173SCK Hu 
mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc * mtk_crtc)93119f5173SCK Hu static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
94119f5173SCK Hu {
95119f5173SCK Hu 	struct drm_crtc *crtc = &mtk_crtc->base;
96119f5173SCK Hu 	unsigned long flags;
97119f5173SCK Hu 
98a3dd12b6SHsin-Yi Wang 	if (mtk_crtc->event) {
99119f5173SCK Hu 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
100119f5173SCK Hu 		drm_crtc_send_vblank_event(crtc, mtk_crtc->event);
101119f5173SCK Hu 		drm_crtc_vblank_put(crtc);
102119f5173SCK Hu 		mtk_crtc->event = NULL;
103119f5173SCK Hu 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
104119f5173SCK Hu 	}
105a3dd12b6SHsin-Yi Wang }
106119f5173SCK Hu 
mtk_drm_finish_page_flip(struct mtk_drm_crtc * mtk_crtc)107119f5173SCK Hu static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc)
108119f5173SCK Hu {
1095b9b8cd2SJason-JH.Lin 	unsigned long flags;
1105b9b8cd2SJason-JH.Lin 
111119f5173SCK Hu 	drm_crtc_handle_vblank(&mtk_crtc->base);
1125b9b8cd2SJason-JH.Lin 
1135b9b8cd2SJason-JH.Lin 	spin_lock_irqsave(&mtk_crtc->config_lock, flags);
114368166ecSChun-Kuang Hu 	if (!mtk_crtc->config_updating && mtk_crtc->pending_needs_vblank) {
115119f5173SCK Hu 		mtk_drm_crtc_finish_page_flip(mtk_crtc);
116119f5173SCK Hu 		mtk_crtc->pending_needs_vblank = false;
117119f5173SCK Hu 	}
1185b9b8cd2SJason-JH.Lin 	spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
119119f5173SCK Hu }
120119f5173SCK Hu 
1217627122fSChun-Kuang Hu #if IS_REACHABLE(CONFIG_MTK_CMDQ)
mtk_drm_cmdq_pkt_create(struct cmdq_client * client,struct cmdq_pkt * pkt,size_t size)1227627122fSChun-Kuang Hu static int mtk_drm_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt,
1237627122fSChun-Kuang Hu 				   size_t size)
1247627122fSChun-Kuang Hu {
1257627122fSChun-Kuang Hu 	struct device *dev;
1267627122fSChun-Kuang Hu 	dma_addr_t dma_addr;
1277627122fSChun-Kuang Hu 
1287627122fSChun-Kuang Hu 	pkt->va_base = kzalloc(size, GFP_KERNEL);
12927b9e2eaSJason-JH.Lin 	if (!pkt->va_base)
1307627122fSChun-Kuang Hu 		return -ENOMEM;
13127b9e2eaSJason-JH.Lin 
1327627122fSChun-Kuang Hu 	pkt->buf_size = size;
1337627122fSChun-Kuang Hu 	pkt->cl = (void *)client;
1347627122fSChun-Kuang Hu 
1357627122fSChun-Kuang Hu 	dev = client->chan->mbox->dev;
1367627122fSChun-Kuang Hu 	dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
1377627122fSChun-Kuang Hu 				  DMA_TO_DEVICE);
1387627122fSChun-Kuang Hu 	if (dma_mapping_error(dev, dma_addr)) {
1397627122fSChun-Kuang Hu 		dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
1407627122fSChun-Kuang Hu 		kfree(pkt->va_base);
1417627122fSChun-Kuang Hu 		return -ENOMEM;
1427627122fSChun-Kuang Hu 	}
1437627122fSChun-Kuang Hu 
1447627122fSChun-Kuang Hu 	pkt->pa_base = dma_addr;
1457627122fSChun-Kuang Hu 
1467627122fSChun-Kuang Hu 	return 0;
1477627122fSChun-Kuang Hu }
1487627122fSChun-Kuang Hu 
mtk_drm_cmdq_pkt_destroy(struct cmdq_pkt * pkt)1497627122fSChun-Kuang Hu static void mtk_drm_cmdq_pkt_destroy(struct cmdq_pkt *pkt)
1507627122fSChun-Kuang Hu {
1517627122fSChun-Kuang Hu 	struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
1527627122fSChun-Kuang Hu 
1537627122fSChun-Kuang Hu 	dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size,
1547627122fSChun-Kuang Hu 			 DMA_TO_DEVICE);
1557627122fSChun-Kuang Hu 	kfree(pkt->va_base);
1567627122fSChun-Kuang Hu }
1577627122fSChun-Kuang Hu #endif
1587627122fSChun-Kuang Hu 
mtk_drm_crtc_destroy(struct drm_crtc * crtc)159119f5173SCK Hu static void mtk_drm_crtc_destroy(struct drm_crtc *crtc)
160119f5173SCK Hu {
161119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
162b74d921bSRex-BC Chen 	int i;
163119f5173SCK Hu 
1644971593fSCK Hu 	mtk_mutex_put(mtk_crtc->mutex);
1657627122fSChun-Kuang Hu #if IS_REACHABLE(CONFIG_MTK_CMDQ)
1667627122fSChun-Kuang Hu 	mtk_drm_cmdq_pkt_destroy(&mtk_crtc->cmdq_handle);
167593b655fSjason-jh.lin 
168593b655fSjason-jh.lin 	if (mtk_crtc->cmdq_client.chan) {
169593b655fSjason-jh.lin 		mbox_free_channel(mtk_crtc->cmdq_client.chan);
170593b655fSjason-jh.lin 		mtk_crtc->cmdq_client.chan = NULL;
171593b655fSjason-jh.lin 	}
1727627122fSChun-Kuang Hu #endif
173b74d921bSRex-BC Chen 
174b74d921bSRex-BC Chen 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
175b74d921bSRex-BC Chen 		struct mtk_ddp_comp *comp;
176b74d921bSRex-BC Chen 
177b74d921bSRex-BC Chen 		comp = mtk_crtc->ddp_comp[i];
178b74d921bSRex-BC Chen 		mtk_ddp_comp_unregister_vblank_cb(comp);
179b74d921bSRex-BC Chen 	}
180b74d921bSRex-BC Chen 
181119f5173SCK Hu 	drm_crtc_cleanup(crtc);
182119f5173SCK Hu }
183119f5173SCK Hu 
mtk_drm_crtc_reset(struct drm_crtc * crtc)184119f5173SCK Hu static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
185119f5173SCK Hu {
186119f5173SCK Hu 	struct mtk_crtc_state *state;
187119f5173SCK Hu 
1882d267b81SDaniel Vetter 	if (crtc->state)
189903daff6SBibby Hsieh 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
190119f5173SCK Hu 
1912d267b81SDaniel Vetter 	kfree(to_mtk_crtc_state(crtc->state));
1922d267b81SDaniel Vetter 	crtc->state = NULL;
193119f5173SCK Hu 
1942d267b81SDaniel Vetter 	state = kzalloc(sizeof(*state), GFP_KERNEL);
1952d267b81SDaniel Vetter 	if (state)
1962d267b81SDaniel Vetter 		__drm_atomic_helper_crtc_reset(crtc, &state->base);
197119f5173SCK Hu }
198119f5173SCK Hu 
mtk_drm_crtc_duplicate_state(struct drm_crtc * crtc)199119f5173SCK Hu static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
200119f5173SCK Hu {
201119f5173SCK Hu 	struct mtk_crtc_state *state;
202119f5173SCK Hu 
20307fb1e5bSAngeloGioacchino Del Regno 	state = kmalloc(sizeof(*state), GFP_KERNEL);
204119f5173SCK Hu 	if (!state)
205119f5173SCK Hu 		return NULL;
206119f5173SCK Hu 
207119f5173SCK Hu 	__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
208119f5173SCK Hu 
209119f5173SCK Hu 	WARN_ON(state->base.crtc != crtc);
210119f5173SCK Hu 	state->base.crtc = crtc;
21107fb1e5bSAngeloGioacchino Del Regno 	state->pending_config = false;
212119f5173SCK Hu 
213119f5173SCK Hu 	return &state->base;
214119f5173SCK Hu }
215119f5173SCK Hu 
mtk_drm_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)216119f5173SCK Hu static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc,
217119f5173SCK Hu 				       struct drm_crtc_state *state)
218119f5173SCK Hu {
219ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
220119f5173SCK Hu 	kfree(to_mtk_crtc_state(state));
221119f5173SCK Hu }
222119f5173SCK Hu 
mtk_drm_crtc_mode_fixup(struct drm_crtc * crtc,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)223119f5173SCK Hu static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc,
224119f5173SCK Hu 				    const struct drm_display_mode *mode,
225119f5173SCK Hu 				    struct drm_display_mode *adjusted_mode)
226119f5173SCK Hu {
227119f5173SCK Hu 	/* Nothing to do here, but this callback is mandatory. */
228119f5173SCK Hu 	return true;
229119f5173SCK Hu }
230119f5173SCK Hu 
mtk_drm_crtc_mode_set_nofb(struct drm_crtc * crtc)231119f5173SCK Hu static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
232119f5173SCK Hu {
233119f5173SCK Hu 	struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state);
234119f5173SCK Hu 
235119f5173SCK Hu 	state->pending_width = crtc->mode.hdisplay;
236119f5173SCK Hu 	state->pending_height = crtc->mode.vdisplay;
2370425662fSVille Syrjälä 	state->pending_vrefresh = drm_mode_vrefresh(&crtc->mode);
238119f5173SCK Hu 	wmb();	/* Make sure the above parameters are set before update */
239119f5173SCK Hu 	state->pending_config = true;
240119f5173SCK Hu }
241119f5173SCK Hu 
mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc * mtk_crtc)242119f5173SCK Hu static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc)
243119f5173SCK Hu {
244119f5173SCK Hu 	int ret;
245119f5173SCK Hu 	int i;
246119f5173SCK Hu 
247119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
248c0d36de8SCK Hu 		ret = mtk_ddp_comp_clk_enable(mtk_crtc->ddp_comp[i]);
249119f5173SCK Hu 		if (ret) {
250119f5173SCK Hu 			DRM_ERROR("Failed to enable clock %d: %d\n", i, ret);
251119f5173SCK Hu 			goto err;
252119f5173SCK Hu 		}
253119f5173SCK Hu 	}
254119f5173SCK Hu 
255119f5173SCK Hu 	return 0;
256119f5173SCK Hu err:
257119f5173SCK Hu 	while (--i >= 0)
258c0d36de8SCK Hu 		mtk_ddp_comp_clk_disable(mtk_crtc->ddp_comp[i]);
259119f5173SCK Hu 	return ret;
260119f5173SCK Hu }
261119f5173SCK Hu 
mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc * mtk_crtc)262119f5173SCK Hu static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
263119f5173SCK Hu {
264119f5173SCK Hu 	int i;
265119f5173SCK Hu 
266119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
267c0d36de8SCK Hu 		mtk_ddp_comp_clk_disable(mtk_crtc->ddp_comp[i]);
268119f5173SCK Hu }
269119f5173SCK Hu 
270d6b53f68SSean Paul static
mtk_drm_ddp_comp_for_plane(struct drm_crtc * crtc,struct drm_plane * plane,unsigned int * local_layer)271d6b53f68SSean Paul struct mtk_ddp_comp *mtk_drm_ddp_comp_for_plane(struct drm_crtc *crtc,
272d6b53f68SSean Paul 						struct drm_plane *plane,
273d6b53f68SSean Paul 						unsigned int *local_layer)
274d6b53f68SSean Paul {
275d6b53f68SSean Paul 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
276d6b53f68SSean Paul 	struct mtk_ddp_comp *comp;
277d6b53f68SSean Paul 	int i, count = 0;
278138b80cbSYongqiang Niu 	unsigned int local_index = plane - mtk_crtc->planes;
279d6b53f68SSean Paul 
280d6b53f68SSean Paul 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
281d6b53f68SSean Paul 		comp = mtk_crtc->ddp_comp[i];
282138b80cbSYongqiang Niu 		if (local_index < (count + mtk_ddp_comp_layer_nr(comp))) {
283138b80cbSYongqiang Niu 			*local_layer = local_index - count;
284d6b53f68SSean Paul 			return comp;
285d6b53f68SSean Paul 		}
286d6b53f68SSean Paul 		count += mtk_ddp_comp_layer_nr(comp);
287d6b53f68SSean Paul 	}
288d6b53f68SSean Paul 
289d6b53f68SSean Paul 	WARN(1, "Failed to find component for plane %d\n", plane->index);
290d6b53f68SSean Paul 	return NULL;
291d6b53f68SSean Paul }
292d6b53f68SSean Paul 
2932f965be7SBibby Hsieh #if IS_REACHABLE(CONFIG_MTK_CMDQ)
ddp_cmdq_cb(struct mbox_client * cl,void * mssg)2941ee07a68SChun-Kuang Hu static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
2952f965be7SBibby Hsieh {
2967f82d9c4SYongqiang Niu 	struct cmdq_cb_data *data = mssg;
297eaf80126SChun-Kuang Hu 	struct cmdq_client *cmdq_cl = container_of(cl, struct cmdq_client, client);
298eaf80126SChun-Kuang Hu 	struct mtk_drm_crtc *mtk_crtc = container_of(cmdq_cl, struct mtk_drm_crtc, cmdq_client);
2997f82d9c4SYongqiang Niu 	struct mtk_crtc_state *state;
3007f82d9c4SYongqiang Niu 	unsigned int i;
3015b9b8cd2SJason-JH.Lin 	unsigned long flags;
3027f82d9c4SYongqiang Niu 
3037f82d9c4SYongqiang Niu 	if (data->sta < 0)
3047f82d9c4SYongqiang Niu 		return;
3057f82d9c4SYongqiang Niu 
3067f82d9c4SYongqiang Niu 	state = to_mtk_crtc_state(mtk_crtc->base.state);
3077f82d9c4SYongqiang Niu 
3085b9b8cd2SJason-JH.Lin 	spin_lock_irqsave(&mtk_crtc->config_lock, flags);
3095b9b8cd2SJason-JH.Lin 	if (mtk_crtc->config_updating) {
3105b9b8cd2SJason-JH.Lin 		spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
3115b9b8cd2SJason-JH.Lin 		goto ddp_cmdq_cb_out;
3125b9b8cd2SJason-JH.Lin 	}
3135b9b8cd2SJason-JH.Lin 
3147f82d9c4SYongqiang Niu 	state->pending_config = false;
3157f82d9c4SYongqiang Niu 
3167f82d9c4SYongqiang Niu 	if (mtk_crtc->pending_planes) {
3177f82d9c4SYongqiang Niu 		for (i = 0; i < mtk_crtc->layer_nr; i++) {
3187f82d9c4SYongqiang Niu 			struct drm_plane *plane = &mtk_crtc->planes[i];
3197f82d9c4SYongqiang Niu 			struct mtk_plane_state *plane_state;
3207f82d9c4SYongqiang Niu 
3217f82d9c4SYongqiang Niu 			plane_state = to_mtk_plane_state(plane->state);
3227f82d9c4SYongqiang Niu 
3237f82d9c4SYongqiang Niu 			plane_state->pending.config = false;
3247f82d9c4SYongqiang Niu 		}
3257f82d9c4SYongqiang Niu 		mtk_crtc->pending_planes = false;
3267f82d9c4SYongqiang Niu 	}
3277f82d9c4SYongqiang Niu 
3287f82d9c4SYongqiang Niu 	if (mtk_crtc->pending_async_planes) {
3297f82d9c4SYongqiang Niu 		for (i = 0; i < mtk_crtc->layer_nr; i++) {
3307f82d9c4SYongqiang Niu 			struct drm_plane *plane = &mtk_crtc->planes[i];
3317f82d9c4SYongqiang Niu 			struct mtk_plane_state *plane_state;
3327f82d9c4SYongqiang Niu 
3337f82d9c4SYongqiang Niu 			plane_state = to_mtk_plane_state(plane->state);
3347f82d9c4SYongqiang Niu 
3357f82d9c4SYongqiang Niu 			plane_state->pending.async_config = false;
3367f82d9c4SYongqiang Niu 		}
3377f82d9c4SYongqiang Niu 		mtk_crtc->pending_async_planes = false;
3387f82d9c4SYongqiang Niu 	}
3391ee07a68SChun-Kuang Hu 
3405b9b8cd2SJason-JH.Lin 	spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
3415b9b8cd2SJason-JH.Lin 
3425b9b8cd2SJason-JH.Lin ddp_cmdq_cb_out:
3435b9b8cd2SJason-JH.Lin 
344eaf80126SChun-Kuang Hu 	mtk_crtc->cmdq_vblank_cnt = 0;
345aa2d5f2fSjason-jh.lin 	wake_up(&mtk_crtc->cb_blocking_queue);
3462f965be7SBibby Hsieh }
3472f965be7SBibby Hsieh #endif
3482f965be7SBibby Hsieh 
mtk_crtc_ddp_hw_init(struct mtk_drm_crtc * mtk_crtc)349119f5173SCK Hu static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
350119f5173SCK Hu {
351119f5173SCK Hu 	struct drm_crtc *crtc = &mtk_crtc->base;
35272164364SBibby Hsieh 	struct drm_connector *connector;
35372164364SBibby Hsieh 	struct drm_encoder *encoder;
354c8bf8b96SGustavo Padovan 	struct drm_connector_list_iter conn_iter;
35572164364SBibby Hsieh 	unsigned int width, height, vrefresh, bpc = MTK_MAX_BPC;
356119f5173SCK Hu 	int ret;
357119f5173SCK Hu 	int i;
358119f5173SCK Hu 
359119f5173SCK Hu 	if (WARN_ON(!crtc->state))
360119f5173SCK Hu 		return -EINVAL;
361119f5173SCK Hu 
362119f5173SCK Hu 	width = crtc->state->adjusted_mode.hdisplay;
363119f5173SCK Hu 	height = crtc->state->adjusted_mode.vdisplay;
3640425662fSVille Syrjälä 	vrefresh = drm_mode_vrefresh(&crtc->state->adjusted_mode);
365119f5173SCK Hu 
36672164364SBibby Hsieh 	drm_for_each_encoder(encoder, crtc->dev) {
36772164364SBibby Hsieh 		if (encoder->crtc != crtc)
36872164364SBibby Hsieh 			continue;
36972164364SBibby Hsieh 
370c8bf8b96SGustavo Padovan 		drm_connector_list_iter_begin(crtc->dev, &conn_iter);
371c8bf8b96SGustavo Padovan 		drm_for_each_connector_iter(connector, &conn_iter) {
37272164364SBibby Hsieh 			if (connector->encoder != encoder)
37372164364SBibby Hsieh 				continue;
37472164364SBibby Hsieh 			if (connector->display_info.bpc != 0 &&
37572164364SBibby Hsieh 			    bpc > connector->display_info.bpc)
37672164364SBibby Hsieh 				bpc = connector->display_info.bpc;
37772164364SBibby Hsieh 		}
378c8bf8b96SGustavo Padovan 		drm_connector_list_iter_end(&conn_iter);
37972164364SBibby Hsieh 	}
38072164364SBibby Hsieh 
38169777e6cSWang Li 	ret = pm_runtime_resume_and_get(crtc->dev->dev);
382119f5173SCK Hu 	if (ret < 0) {
383119f5173SCK Hu 		DRM_ERROR("Failed to enable power domain: %d\n", ret);
384119f5173SCK Hu 		return ret;
385119f5173SCK Hu 	}
386119f5173SCK Hu 
3874971593fSCK Hu 	ret = mtk_mutex_prepare(mtk_crtc->mutex);
388119f5173SCK Hu 	if (ret < 0) {
389119f5173SCK Hu 		DRM_ERROR("Failed to enable mutex clock: %d\n", ret);
390119f5173SCK Hu 		goto err_pm_runtime_put;
391119f5173SCK Hu 	}
392119f5173SCK Hu 
393119f5173SCK Hu 	ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
394119f5173SCK Hu 	if (ret < 0) {
395119f5173SCK Hu 		DRM_ERROR("Failed to enable component clocks: %d\n", ret);
396119f5173SCK Hu 		goto err_mutex_unprepare;
397119f5173SCK Hu 	}
398119f5173SCK Hu 
399119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
4000d9eee91SNancy.Lin 		if (!mtk_ddp_comp_connect(mtk_crtc->ddp_comp[i], mtk_crtc->mmsys_dev,
4010d9eee91SNancy.Lin 					  mtk_crtc->ddp_comp[i + 1]->id))
4022c758e30SEnric Balletbo i Serra 			mtk_mmsys_ddp_connect(mtk_crtc->mmsys_dev,
403119f5173SCK Hu 					      mtk_crtc->ddp_comp[i]->id,
404119f5173SCK Hu 					      mtk_crtc->ddp_comp[i + 1]->id);
4050d9eee91SNancy.Lin 		if (!mtk_ddp_comp_add(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
4064971593fSCK Hu 			mtk_mutex_add_comp(mtk_crtc->mutex,
407119f5173SCK Hu 					   mtk_crtc->ddp_comp[i]->id);
408119f5173SCK Hu 	}
4090d9eee91SNancy.Lin 	if (!mtk_ddp_comp_add(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
4104971593fSCK Hu 		mtk_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
4114971593fSCK Hu 	mtk_mutex_enable(mtk_crtc->mutex);
412119f5173SCK Hu 
413119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
414119f5173SCK Hu 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
415119f5173SCK Hu 
416412b1e46SYongqiang Niu 		if (i == 1)
417412b1e46SYongqiang Niu 			mtk_ddp_comp_bgclr_in_on(comp);
418412b1e46SYongqiang Niu 
419d0afe37fSBibby Hsieh 		mtk_ddp_comp_config(comp, width, height, vrefresh, bpc, NULL);
420119f5173SCK Hu 		mtk_ddp_comp_start(comp);
421119f5173SCK Hu 	}
422119f5173SCK Hu 
423119f5173SCK Hu 	/* Initially configure all planes */
42466b2cf96SStu Hsieh 	for (i = 0; i < mtk_crtc->layer_nr; i++) {
4255bfafad8SDaniel Kurtz 		struct drm_plane *plane = &mtk_crtc->planes[i];
426119f5173SCK Hu 		struct mtk_plane_state *plane_state;
427d6b53f68SSean Paul 		struct mtk_ddp_comp *comp;
428412b1e46SYongqiang Niu 		unsigned int local_layer;
429119f5173SCK Hu 
430119f5173SCK Hu 		plane_state = to_mtk_plane_state(plane->state);
43112d322fcSJason-JH.Lin 
43212d322fcSJason-JH.Lin 		/* should not enable layer before crtc enabled */
43312d322fcSJason-JH.Lin 		plane_state->pending.enable = false;
434d6b53f68SSean Paul 		comp = mtk_drm_ddp_comp_for_plane(crtc, plane, &local_layer);
4355bbb71cdSPi-Hsun Shih 		if (comp)
4365bbb71cdSPi-Hsun Shih 			mtk_ddp_comp_layer_config(comp, local_layer,
437d0afe37fSBibby Hsieh 						  plane_state, NULL);
438119f5173SCK Hu 	}
439119f5173SCK Hu 
440119f5173SCK Hu 	return 0;
441119f5173SCK Hu 
442119f5173SCK Hu err_mutex_unprepare:
4434971593fSCK Hu 	mtk_mutex_unprepare(mtk_crtc->mutex);
444119f5173SCK Hu err_pm_runtime_put:
445119f5173SCK Hu 	pm_runtime_put(crtc->dev->dev);
446119f5173SCK Hu 	return ret;
447119f5173SCK Hu }
448119f5173SCK Hu 
mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc * mtk_crtc)449119f5173SCK Hu static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
450119f5173SCK Hu {
451119f5173SCK Hu 	struct drm_device *drm = mtk_crtc->base.dev;
452411f5c1eSBibby Hsieh 	struct drm_crtc *crtc = &mtk_crtc->base;
453*415a2c21SFei Shao 	unsigned long flags;
454119f5173SCK Hu 	int i;
455119f5173SCK Hu 
456412b1e46SYongqiang Niu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
457119f5173SCK Hu 		mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
458412b1e46SYongqiang Niu 		if (i == 1)
459412b1e46SYongqiang Niu 			mtk_ddp_comp_bgclr_in_off(mtk_crtc->ddp_comp[i]);
460412b1e46SYongqiang Niu 	}
461412b1e46SYongqiang Niu 
462119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
4630d9eee91SNancy.Lin 		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
4644971593fSCK Hu 			mtk_mutex_remove_comp(mtk_crtc->mutex,
465119f5173SCK Hu 					      mtk_crtc->ddp_comp[i]->id);
4664971593fSCK Hu 	mtk_mutex_disable(mtk_crtc->mutex);
467119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
4680d9eee91SNancy.Lin 		if (!mtk_ddp_comp_disconnect(mtk_crtc->ddp_comp[i], mtk_crtc->mmsys_dev,
4690d9eee91SNancy.Lin 					     mtk_crtc->ddp_comp[i + 1]->id))
4702c758e30SEnric Balletbo i Serra 			mtk_mmsys_ddp_disconnect(mtk_crtc->mmsys_dev,
471119f5173SCK Hu 						 mtk_crtc->ddp_comp[i]->id,
472119f5173SCK Hu 						 mtk_crtc->ddp_comp[i + 1]->id);
4730d9eee91SNancy.Lin 		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
4744971593fSCK Hu 			mtk_mutex_remove_comp(mtk_crtc->mutex,
475119f5173SCK Hu 					      mtk_crtc->ddp_comp[i]->id);
476119f5173SCK Hu 	}
4770d9eee91SNancy.Lin 	if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
4784971593fSCK Hu 		mtk_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id);
479119f5173SCK Hu 	mtk_crtc_ddp_clk_disable(mtk_crtc);
4804971593fSCK Hu 	mtk_mutex_unprepare(mtk_crtc->mutex);
481119f5173SCK Hu 
482119f5173SCK Hu 	pm_runtime_put(drm->dev);
483411f5c1eSBibby Hsieh 
484411f5c1eSBibby Hsieh 	if (crtc->state->event && !crtc->state->active) {
485*415a2c21SFei Shao 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
486411f5c1eSBibby Hsieh 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
487411f5c1eSBibby Hsieh 		crtc->state->event = NULL;
488*415a2c21SFei Shao 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
489411f5c1eSBibby Hsieh 	}
490119f5173SCK Hu }
491119f5173SCK Hu 
mtk_crtc_ddp_config(struct drm_crtc * crtc,struct cmdq_pkt * cmdq_handle)4922f965be7SBibby Hsieh static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
4932f965be7SBibby Hsieh 				struct cmdq_pkt *cmdq_handle)
4949dc84e98Syt.shen@mediatek.com {
4959dc84e98Syt.shen@mediatek.com 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
4969dc84e98Syt.shen@mediatek.com 	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
497f265905cSStu Hsieh 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
4989dc84e98Syt.shen@mediatek.com 	unsigned int i;
499412b1e46SYongqiang Niu 	unsigned int local_layer;
5009dc84e98Syt.shen@mediatek.com 
5019dc84e98Syt.shen@mediatek.com 	/*
5029dc84e98Syt.shen@mediatek.com 	 * TODO: instead of updating the registers here, we should prepare
5039dc84e98Syt.shen@mediatek.com 	 * working registers in atomic_commit and let the hardware command
5049dc84e98Syt.shen@mediatek.com 	 * queue update module registers on vblank.
5059dc84e98Syt.shen@mediatek.com 	 */
5069dc84e98Syt.shen@mediatek.com 	if (state->pending_config) {
507f265905cSStu Hsieh 		mtk_ddp_comp_config(comp, state->pending_width,
5089dc84e98Syt.shen@mediatek.com 				    state->pending_height,
5092f965be7SBibby Hsieh 				    state->pending_vrefresh, 0,
5102f965be7SBibby Hsieh 				    cmdq_handle);
5119dc84e98Syt.shen@mediatek.com 
5127f82d9c4SYongqiang Niu 		if (!cmdq_handle)
5139dc84e98Syt.shen@mediatek.com 			state->pending_config = false;
5149dc84e98Syt.shen@mediatek.com 	}
5159dc84e98Syt.shen@mediatek.com 
5169dc84e98Syt.shen@mediatek.com 	if (mtk_crtc->pending_planes) {
51766b2cf96SStu Hsieh 		for (i = 0; i < mtk_crtc->layer_nr; i++) {
5189dc84e98Syt.shen@mediatek.com 			struct drm_plane *plane = &mtk_crtc->planes[i];
5199dc84e98Syt.shen@mediatek.com 			struct mtk_plane_state *plane_state;
5209dc84e98Syt.shen@mediatek.com 
5219dc84e98Syt.shen@mediatek.com 			plane_state = to_mtk_plane_state(plane->state);
5229dc84e98Syt.shen@mediatek.com 
523d6b53f68SSean Paul 			if (!plane_state->pending.config)
524d6b53f68SSean Paul 				continue;
525d6b53f68SSean Paul 
526d6b53f68SSean Paul 			comp = mtk_drm_ddp_comp_for_plane(crtc, plane,
527d6b53f68SSean Paul 							  &local_layer);
528412b1e46SYongqiang Niu 
5295bbb71cdSPi-Hsun Shih 			if (comp)
530412b1e46SYongqiang Niu 				mtk_ddp_comp_layer_config(comp, local_layer,
5312f965be7SBibby Hsieh 							  plane_state,
5322f965be7SBibby Hsieh 							  cmdq_handle);
5337f82d9c4SYongqiang Niu 			if (!cmdq_handle)
5349dc84e98Syt.shen@mediatek.com 				plane_state->pending.config = false;
5359dc84e98Syt.shen@mediatek.com 		}
5367f82d9c4SYongqiang Niu 
5377f82d9c4SYongqiang Niu 		if (!cmdq_handle)
5389dc84e98Syt.shen@mediatek.com 			mtk_crtc->pending_planes = false;
5399dc84e98Syt.shen@mediatek.com 	}
540920fffccSBibby Hsieh 
541920fffccSBibby Hsieh 	if (mtk_crtc->pending_async_planes) {
542920fffccSBibby Hsieh 		for (i = 0; i < mtk_crtc->layer_nr; i++) {
543920fffccSBibby Hsieh 			struct drm_plane *plane = &mtk_crtc->planes[i];
544920fffccSBibby Hsieh 			struct mtk_plane_state *plane_state;
545920fffccSBibby Hsieh 
546920fffccSBibby Hsieh 			plane_state = to_mtk_plane_state(plane->state);
547920fffccSBibby Hsieh 
548920fffccSBibby Hsieh 			if (!plane_state->pending.async_config)
549920fffccSBibby Hsieh 				continue;
550920fffccSBibby Hsieh 
551920fffccSBibby Hsieh 			comp = mtk_drm_ddp_comp_for_plane(crtc, plane,
552920fffccSBibby Hsieh 							  &local_layer);
553920fffccSBibby Hsieh 
554920fffccSBibby Hsieh 			if (comp)
555920fffccSBibby Hsieh 				mtk_ddp_comp_layer_config(comp, local_layer,
5562f965be7SBibby Hsieh 							  plane_state,
5572f965be7SBibby Hsieh 							  cmdq_handle);
5587f82d9c4SYongqiang Niu 			if (!cmdq_handle)
559920fffccSBibby Hsieh 				plane_state->pending.async_config = false;
560920fffccSBibby Hsieh 		}
5617f82d9c4SYongqiang Niu 
5627f82d9c4SYongqiang Niu 		if (!cmdq_handle)
563920fffccSBibby Hsieh 			mtk_crtc->pending_async_planes = false;
564920fffccSBibby Hsieh 	}
565920fffccSBibby Hsieh }
566920fffccSBibby Hsieh 
mtk_drm_crtc_update_config(struct mtk_drm_crtc * mtk_crtc,bool needs_vblank)567368166ecSChun-Kuang Hu static void mtk_drm_crtc_update_config(struct mtk_drm_crtc *mtk_crtc,
568368166ecSChun-Kuang Hu 				       bool needs_vblank)
569920fffccSBibby Hsieh {
5702f965be7SBibby Hsieh #if IS_REACHABLE(CONFIG_MTK_CMDQ)
5717627122fSChun-Kuang Hu 	struct cmdq_pkt *cmdq_handle = &mtk_crtc->cmdq_handle;
5722f965be7SBibby Hsieh #endif
573920fffccSBibby Hsieh 	struct drm_crtc *crtc = &mtk_crtc->base;
574920fffccSBibby Hsieh 	struct mtk_drm_private *priv = crtc->dev->dev_private;
575920fffccSBibby Hsieh 	unsigned int pending_planes = 0, pending_async_planes = 0;
576920fffccSBibby Hsieh 	int i;
5775b9b8cd2SJason-JH.Lin 	unsigned long flags;
578920fffccSBibby Hsieh 
579920fffccSBibby Hsieh 	mutex_lock(&mtk_crtc->hw_lock);
5805b9b8cd2SJason-JH.Lin 
5815b9b8cd2SJason-JH.Lin 	spin_lock_irqsave(&mtk_crtc->config_lock, flags);
582368166ecSChun-Kuang Hu 	mtk_crtc->config_updating = true;
5835b9b8cd2SJason-JH.Lin 	spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
5845b9b8cd2SJason-JH.Lin 
585368166ecSChun-Kuang Hu 	if (needs_vblank)
586368166ecSChun-Kuang Hu 		mtk_crtc->pending_needs_vblank = true;
587368166ecSChun-Kuang Hu 
588920fffccSBibby Hsieh 	for (i = 0; i < mtk_crtc->layer_nr; i++) {
589920fffccSBibby Hsieh 		struct drm_plane *plane = &mtk_crtc->planes[i];
590920fffccSBibby Hsieh 		struct mtk_plane_state *plane_state;
591920fffccSBibby Hsieh 
592920fffccSBibby Hsieh 		plane_state = to_mtk_plane_state(plane->state);
593920fffccSBibby Hsieh 		if (plane_state->pending.dirty) {
594920fffccSBibby Hsieh 			plane_state->pending.config = true;
595920fffccSBibby Hsieh 			plane_state->pending.dirty = false;
596920fffccSBibby Hsieh 			pending_planes |= BIT(i);
597920fffccSBibby Hsieh 		} else if (plane_state->pending.async_dirty) {
598920fffccSBibby Hsieh 			plane_state->pending.async_config = true;
599920fffccSBibby Hsieh 			plane_state->pending.async_dirty = false;
600920fffccSBibby Hsieh 			pending_async_planes |= BIT(i);
601920fffccSBibby Hsieh 		}
602920fffccSBibby Hsieh 	}
603920fffccSBibby Hsieh 	if (pending_planes)
604920fffccSBibby Hsieh 		mtk_crtc->pending_planes = true;
605920fffccSBibby Hsieh 	if (pending_async_planes)
606920fffccSBibby Hsieh 		mtk_crtc->pending_async_planes = true;
607920fffccSBibby Hsieh 
608920fffccSBibby Hsieh 	if (priv->data->shadow_register) {
6094971593fSCK Hu 		mtk_mutex_acquire(mtk_crtc->mutex);
6102f965be7SBibby Hsieh 		mtk_crtc_ddp_config(crtc, NULL);
6114971593fSCK Hu 		mtk_mutex_release(mtk_crtc->mutex);
612920fffccSBibby Hsieh 	}
6132f965be7SBibby Hsieh #if IS_REACHABLE(CONFIG_MTK_CMDQ)
614563c9d4aSChun-Kuang Hu 	if (mtk_crtc->cmdq_client.chan) {
615563c9d4aSChun-Kuang Hu 		mbox_flush(mtk_crtc->cmdq_client.chan, 2000);
6167627122fSChun-Kuang Hu 		cmdq_handle->cmd_buf_size = 0;
6172f965be7SBibby Hsieh 		cmdq_pkt_clear_event(cmdq_handle, mtk_crtc->cmdq_event);
618bee1abc9SDennis YC Hsieh 		cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event, false);
6192f965be7SBibby Hsieh 		mtk_crtc_ddp_config(crtc, cmdq_handle);
62099581858SDennis YC Hsieh 		cmdq_pkt_finalize(cmdq_handle);
621563c9d4aSChun-Kuang Hu 		dma_sync_single_for_device(mtk_crtc->cmdq_client.chan->mbox->dev,
6221ee07a68SChun-Kuang Hu 					   cmdq_handle->pa_base,
6231ee07a68SChun-Kuang Hu 					   cmdq_handle->cmd_buf_size,
6241ee07a68SChun-Kuang Hu 					   DMA_TO_DEVICE);
625eaf80126SChun-Kuang Hu 		/*
626eaf80126SChun-Kuang Hu 		 * CMDQ command should execute in next 3 vblank.
627eaf80126SChun-Kuang Hu 		 * One vblank interrupt before send message (occasionally)
628eaf80126SChun-Kuang Hu 		 * and one vblank interrupt after cmdq done,
629eaf80126SChun-Kuang Hu 		 * so it's timeout after 3 vblank interrupt.
630eaf80126SChun-Kuang Hu 		 * If it fail to execute in next 3 vblank, timeout happen.
631eaf80126SChun-Kuang Hu 		 */
632eaf80126SChun-Kuang Hu 		mtk_crtc->cmdq_vblank_cnt = 3;
633eaf80126SChun-Kuang Hu 
634563c9d4aSChun-Kuang Hu 		mbox_send_message(mtk_crtc->cmdq_client.chan, cmdq_handle);
635563c9d4aSChun-Kuang Hu 		mbox_client_txdone(mtk_crtc->cmdq_client.chan, 0);
6362f965be7SBibby Hsieh 	}
6372f965be7SBibby Hsieh #endif
6385b9b8cd2SJason-JH.Lin 	spin_lock_irqsave(&mtk_crtc->config_lock, flags);
639368166ecSChun-Kuang Hu 	mtk_crtc->config_updating = false;
6405b9b8cd2SJason-JH.Lin 	spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
6415b9b8cd2SJason-JH.Lin 
642920fffccSBibby Hsieh 	mutex_unlock(&mtk_crtc->hw_lock);
6439dc84e98Syt.shen@mediatek.com }
6449dc84e98Syt.shen@mediatek.com 
mtk_crtc_ddp_irq(void * data)6459b070498SCK Hu static void mtk_crtc_ddp_irq(void *data)
6469b070498SCK Hu {
6479b070498SCK Hu 	struct drm_crtc *crtc = data;
6489b070498SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
6499b070498SCK Hu 	struct mtk_drm_private *priv = crtc->dev->dev_private;
6509b070498SCK Hu 
6519b070498SCK Hu #if IS_REACHABLE(CONFIG_MTK_CMDQ)
652563c9d4aSChun-Kuang Hu 	if (!priv->data->shadow_register && !mtk_crtc->cmdq_client.chan)
653eaf80126SChun-Kuang Hu 		mtk_crtc_ddp_config(crtc, NULL);
654eaf80126SChun-Kuang Hu 	else if (mtk_crtc->cmdq_vblank_cnt > 0 && --mtk_crtc->cmdq_vblank_cnt == 0)
655eaf80126SChun-Kuang Hu 		DRM_ERROR("mtk_crtc %d CMDQ execute command timeout!\n",
656eaf80126SChun-Kuang Hu 			  drm_crtc_index(&mtk_crtc->base));
6579b070498SCK Hu #else
6589b070498SCK Hu 	if (!priv->data->shadow_register)
6590cf54fffSChun-Kuang Hu 		mtk_crtc_ddp_config(crtc, NULL);
660eaf80126SChun-Kuang Hu #endif
6619b070498SCK Hu 	mtk_drm_finish_page_flip(mtk_crtc);
6629b070498SCK Hu }
6639b070498SCK Hu 
mtk_drm_crtc_enable_vblank(struct drm_crtc * crtc)6649b070498SCK Hu static int mtk_drm_crtc_enable_vblank(struct drm_crtc *crtc)
6659b070498SCK Hu {
6669b070498SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
6679b070498SCK Hu 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
6689b070498SCK Hu 
669b74d921bSRex-BC Chen 	mtk_ddp_comp_enable_vblank(comp);
6709b070498SCK Hu 
6719b070498SCK Hu 	return 0;
6729b070498SCK Hu }
6739b070498SCK Hu 
mtk_drm_crtc_disable_vblank(struct drm_crtc * crtc)6749b070498SCK Hu static void mtk_drm_crtc_disable_vblank(struct drm_crtc *crtc)
6759b070498SCK Hu {
6769b070498SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
6779b070498SCK Hu 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
6789b070498SCK Hu 
6799b070498SCK Hu 	mtk_ddp_comp_disable_vblank(comp);
6809b070498SCK Hu }
6819b070498SCK Hu 
mtk_drm_crtc_plane_check(struct drm_crtc * crtc,struct drm_plane * plane,struct mtk_plane_state * state)682f7c710d1SSean Paul int mtk_drm_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
683f7c710d1SSean Paul 			     struct mtk_plane_state *state)
684f7c710d1SSean Paul {
685f7c710d1SSean Paul 	unsigned int local_layer;
686f7c710d1SSean Paul 	struct mtk_ddp_comp *comp;
687f7c710d1SSean Paul 
688f7c710d1SSean Paul 	comp = mtk_drm_ddp_comp_for_plane(crtc, plane, &local_layer);
6895bbb71cdSPi-Hsun Shih 	if (comp)
690f7c710d1SSean Paul 		return mtk_ddp_comp_layer_check(comp, local_layer, state);
6915bbb71cdSPi-Hsun Shih 	return 0;
692f7c710d1SSean Paul }
693f7c710d1SSean Paul 
mtk_drm_crtc_async_update(struct drm_crtc * crtc,struct drm_plane * plane,struct drm_atomic_state * state)694920fffccSBibby Hsieh void mtk_drm_crtc_async_update(struct drm_crtc *crtc, struct drm_plane *plane,
695977697e2SMaxime Ripard 			       struct drm_atomic_state *state)
696920fffccSBibby Hsieh {
697920fffccSBibby Hsieh 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
698920fffccSBibby Hsieh 
699920fffccSBibby Hsieh 	if (!mtk_crtc->enabled)
700920fffccSBibby Hsieh 		return;
701920fffccSBibby Hsieh 
702368166ecSChun-Kuang Hu 	mtk_drm_crtc_update_config(mtk_crtc, false);
703920fffccSBibby Hsieh }
704920fffccSBibby Hsieh 
mtk_drm_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)7050b20a0f8SLaurent Pinchart static void mtk_drm_crtc_atomic_enable(struct drm_crtc *crtc,
706351f950dSMaxime Ripard 				       struct drm_atomic_state *state)
707119f5173SCK Hu {
708119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
709f265905cSStu Hsieh 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
710119f5173SCK Hu 	int ret;
711119f5173SCK Hu 
712119f5173SCK Hu 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
713119f5173SCK Hu 
7145db12f5dSYongqiang Niu 	ret = pm_runtime_resume_and_get(comp->dev);
7155db12f5dSYongqiang Niu 	if (ret < 0) {
7165db12f5dSYongqiang Niu 		DRM_DEV_ERROR(comp->dev, "Failed to enable power domain: %d\n", ret);
7175db12f5dSYongqiang Niu 		return;
7185db12f5dSYongqiang Niu 	}
7195db12f5dSYongqiang Niu 
720119f5173SCK Hu 	ret = mtk_crtc_ddp_hw_init(mtk_crtc);
721119f5173SCK Hu 	if (ret) {
7225db12f5dSYongqiang Niu 		pm_runtime_put(comp->dev);
723119f5173SCK Hu 		return;
724119f5173SCK Hu 	}
725119f5173SCK Hu 
726119f5173SCK Hu 	drm_crtc_vblank_on(crtc);
727119f5173SCK Hu 	mtk_crtc->enabled = true;
728119f5173SCK Hu }
729119f5173SCK Hu 
mtk_drm_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)73064581714SLaurent Pinchart static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
731351f950dSMaxime Ripard 					struct drm_atomic_state *state)
732119f5173SCK Hu {
733119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
734f265905cSStu Hsieh 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
7355db12f5dSYongqiang Niu 	int i, ret;
736119f5173SCK Hu 
737119f5173SCK Hu 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
738119f5173SCK Hu 	if (!mtk_crtc->enabled)
739119f5173SCK Hu 		return;
740119f5173SCK Hu 
741119f5173SCK Hu 	/* Set all pending plane state to disabled */
74266b2cf96SStu Hsieh 	for (i = 0; i < mtk_crtc->layer_nr; i++) {
7435bfafad8SDaniel Kurtz 		struct drm_plane *plane = &mtk_crtc->planes[i];
744119f5173SCK Hu 		struct mtk_plane_state *plane_state;
745119f5173SCK Hu 
746119f5173SCK Hu 		plane_state = to_mtk_plane_state(plane->state);
747119f5173SCK Hu 		plane_state->pending.enable = false;
748119f5173SCK Hu 		plane_state->pending.config = true;
749119f5173SCK Hu 	}
750119f5173SCK Hu 	mtk_crtc->pending_planes = true;
751119f5173SCK Hu 
752368166ecSChun-Kuang Hu 	mtk_drm_crtc_update_config(mtk_crtc, false);
753aa2d5f2fSjason-jh.lin #if IS_REACHABLE(CONFIG_MTK_CMDQ)
754aa2d5f2fSjason-jh.lin 	/* Wait for planes to be disabled by cmdq */
755aa2d5f2fSjason-jh.lin 	if (mtk_crtc->cmdq_client.chan)
756aa2d5f2fSjason-jh.lin 		wait_event_timeout(mtk_crtc->cb_blocking_queue,
757aa2d5f2fSjason-jh.lin 				   mtk_crtc->cmdq_vblank_cnt == 0,
758aa2d5f2fSjason-jh.lin 				   msecs_to_jiffies(500));
759aa2d5f2fSjason-jh.lin #endif
760119f5173SCK Hu 	/* Wait for planes to be disabled */
761119f5173SCK Hu 	drm_crtc_wait_one_vblank(crtc);
762119f5173SCK Hu 
763119f5173SCK Hu 	drm_crtc_vblank_off(crtc);
764119f5173SCK Hu 	mtk_crtc_ddp_hw_fini(mtk_crtc);
7655db12f5dSYongqiang Niu 	ret = pm_runtime_put(comp->dev);
7665db12f5dSYongqiang Niu 	if (ret < 0)
7675db12f5dSYongqiang Niu 		DRM_DEV_ERROR(comp->dev, "Failed to disable power domain: %d\n", ret);
768119f5173SCK Hu 
769119f5173SCK Hu 	mtk_crtc->enabled = false;
770119f5173SCK Hu }
771119f5173SCK Hu 
mtk_drm_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)772119f5173SCK Hu static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
773f6ebe9f9SMaxime Ripard 				      struct drm_atomic_state *state)
774119f5173SCK Hu {
775253f28b6SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
776253f28b6SMaxime Ripard 									  crtc);
777253f28b6SMaxime Ripard 	struct mtk_crtc_state *mtk_crtc_state = to_mtk_crtc_state(crtc_state);
778119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
7797d6e9cb7SJason-JH.Lin 	unsigned long flags;
780119f5173SCK Hu 
781253f28b6SMaxime Ripard 	if (mtk_crtc->event && mtk_crtc_state->base.event)
782119f5173SCK Hu 		DRM_ERROR("new event while there is still a pending event\n");
783119f5173SCK Hu 
784253f28b6SMaxime Ripard 	if (mtk_crtc_state->base.event) {
785253f28b6SMaxime Ripard 		mtk_crtc_state->base.event->pipe = drm_crtc_index(crtc);
786119f5173SCK Hu 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
7877d6e9cb7SJason-JH.Lin 
7887d6e9cb7SJason-JH.Lin 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
789253f28b6SMaxime Ripard 		mtk_crtc->event = mtk_crtc_state->base.event;
7907d6e9cb7SJason-JH.Lin 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
7917d6e9cb7SJason-JH.Lin 
792253f28b6SMaxime Ripard 		mtk_crtc_state->base.event = NULL;
793119f5173SCK Hu 	}
794119f5173SCK Hu }
795119f5173SCK Hu 
mtk_drm_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)796119f5173SCK Hu static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
797f6ebe9f9SMaxime Ripard 				      struct drm_atomic_state *state)
798119f5173SCK Hu {
799119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
800119f5173SCK Hu 	int i;
801119f5173SCK Hu 
8022f3f4ddaSBibby Hsieh 	if (crtc->state->color_mgmt_changed)
80384abcf12SYongqiang Niu 		for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
8042f3f4ddaSBibby Hsieh 			mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], crtc->state);
80584abcf12SYongqiang Niu 			mtk_ddp_ctm_set(mtk_crtc->ddp_comp[i], crtc->state);
80684abcf12SYongqiang Niu 		}
807368166ecSChun-Kuang Hu 	mtk_drm_crtc_update_config(mtk_crtc, !!mtk_crtc->event);
808119f5173SCK Hu }
809119f5173SCK Hu 
810119f5173SCK Hu static const struct drm_crtc_funcs mtk_crtc_funcs = {
811119f5173SCK Hu 	.set_config		= drm_atomic_helper_set_config,
812119f5173SCK Hu 	.page_flip		= drm_atomic_helper_page_flip,
813119f5173SCK Hu 	.destroy		= mtk_drm_crtc_destroy,
814119f5173SCK Hu 	.reset			= mtk_drm_crtc_reset,
815119f5173SCK Hu 	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
816119f5173SCK Hu 	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
817a5073a5bSShawn Guo 	.enable_vblank		= mtk_drm_crtc_enable_vblank,
818a5073a5bSShawn Guo 	.disable_vblank		= mtk_drm_crtc_disable_vblank,
819119f5173SCK Hu };
820119f5173SCK Hu 
821119f5173SCK Hu static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
822119f5173SCK Hu 	.mode_fixup	= mtk_drm_crtc_mode_fixup,
823119f5173SCK Hu 	.mode_set_nofb	= mtk_drm_crtc_mode_set_nofb,
824119f5173SCK Hu 	.atomic_begin	= mtk_drm_crtc_atomic_begin,
825119f5173SCK Hu 	.atomic_flush	= mtk_drm_crtc_atomic_flush,
8260b20a0f8SLaurent Pinchart 	.atomic_enable	= mtk_drm_crtc_atomic_enable,
82764581714SLaurent Pinchart 	.atomic_disable	= mtk_drm_crtc_atomic_disable,
828119f5173SCK Hu };
829119f5173SCK Hu 
mtk_drm_crtc_init(struct drm_device * drm,struct mtk_drm_crtc * mtk_crtc,unsigned int pipe)830119f5173SCK Hu static int mtk_drm_crtc_init(struct drm_device *drm,
831119f5173SCK Hu 			     struct mtk_drm_crtc *mtk_crtc,
832318caac7SEvan Benn 			     unsigned int pipe)
833119f5173SCK Hu {
834318caac7SEvan Benn 	struct drm_plane *primary = NULL;
835318caac7SEvan Benn 	struct drm_plane *cursor = NULL;
836318caac7SEvan Benn 	int i, ret;
837318caac7SEvan Benn 
838318caac7SEvan Benn 	for (i = 0; i < mtk_crtc->layer_nr; i++) {
839318caac7SEvan Benn 		if (mtk_crtc->planes[i].type == DRM_PLANE_TYPE_PRIMARY)
840318caac7SEvan Benn 			primary = &mtk_crtc->planes[i];
841318caac7SEvan Benn 		else if (mtk_crtc->planes[i].type == DRM_PLANE_TYPE_CURSOR)
842318caac7SEvan Benn 			cursor = &mtk_crtc->planes[i];
843318caac7SEvan Benn 	}
844119f5173SCK Hu 
845119f5173SCK Hu 	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
846119f5173SCK Hu 					&mtk_crtc_funcs, NULL);
847119f5173SCK Hu 	if (ret)
848119f5173SCK Hu 		goto err_cleanup_crtc;
849119f5173SCK Hu 
850119f5173SCK Hu 	drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs);
851119f5173SCK Hu 
852119f5173SCK Hu 	return 0;
853119f5173SCK Hu 
854119f5173SCK Hu err_cleanup_crtc:
855119f5173SCK Hu 	drm_crtc_cleanup(&mtk_crtc->base);
856119f5173SCK Hu 	return ret;
857119f5173SCK Hu }
858119f5173SCK Hu 
mtk_drm_crtc_num_comp_planes(struct mtk_drm_crtc * mtk_crtc,int comp_idx)85931c5558dSSean Paul static int mtk_drm_crtc_num_comp_planes(struct mtk_drm_crtc *mtk_crtc,
86031c5558dSSean Paul 					int comp_idx)
86131c5558dSSean Paul {
86231c5558dSSean Paul 	struct mtk_ddp_comp *comp;
86331c5558dSSean Paul 
86431c5558dSSean Paul 	if (comp_idx > 1)
86531c5558dSSean Paul 		return 0;
86631c5558dSSean Paul 
86731c5558dSSean Paul 	comp = mtk_crtc->ddp_comp[comp_idx];
86831c5558dSSean Paul 	if (!comp->funcs)
86931c5558dSSean Paul 		return 0;
87031c5558dSSean Paul 
87131c5558dSSean Paul 	if (comp_idx == 1 && !comp->funcs->bgclr_in_on)
87231c5558dSSean Paul 		return 0;
87331c5558dSSean Paul 
87431c5558dSSean Paul 	return mtk_ddp_comp_layer_nr(comp);
87531c5558dSSean Paul }
87631c5558dSSean Paul 
87731c5558dSSean Paul static inline
mtk_drm_crtc_plane_type(unsigned int plane_idx,unsigned int num_planes)87826d69619SSean Paul enum drm_plane_type mtk_drm_crtc_plane_type(unsigned int plane_idx,
87926d69619SSean Paul 					    unsigned int num_planes)
88031c5558dSSean Paul {
88131c5558dSSean Paul 	if (plane_idx == 0)
88231c5558dSSean Paul 		return DRM_PLANE_TYPE_PRIMARY;
88326d69619SSean Paul 	else if (plane_idx == (num_planes - 1))
88431c5558dSSean Paul 		return DRM_PLANE_TYPE_CURSOR;
88531c5558dSSean Paul 	else
88631c5558dSSean Paul 		return DRM_PLANE_TYPE_OVERLAY;
88731c5558dSSean Paul 
88831c5558dSSean Paul }
88931c5558dSSean Paul 
mtk_drm_crtc_init_comp_planes(struct drm_device * drm_dev,struct mtk_drm_crtc * mtk_crtc,int comp_idx,int pipe)89031c5558dSSean Paul static int mtk_drm_crtc_init_comp_planes(struct drm_device *drm_dev,
89131c5558dSSean Paul 					 struct mtk_drm_crtc *mtk_crtc,
89231c5558dSSean Paul 					 int comp_idx, int pipe)
89331c5558dSSean Paul {
89431c5558dSSean Paul 	int num_planes = mtk_drm_crtc_num_comp_planes(mtk_crtc, comp_idx);
895ef87d3e2SSean Paul 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[comp_idx];
89631c5558dSSean Paul 	int i, ret;
89731c5558dSSean Paul 
89831c5558dSSean Paul 	for (i = 0; i < num_planes; i++) {
89931c5558dSSean Paul 		ret = mtk_plane_init(drm_dev,
90031c5558dSSean Paul 				&mtk_crtc->planes[mtk_crtc->layer_nr],
90131c5558dSSean Paul 				BIT(pipe),
90226d69619SSean Paul 				mtk_drm_crtc_plane_type(mtk_crtc->layer_nr,
90326d69619SSean Paul 							num_planes),
904f287c66aSJustin Green 				mtk_ddp_comp_supported_rotations(comp),
905f287c66aSJustin Green 				mtk_ddp_comp_get_formats(comp),
906f287c66aSJustin Green 				mtk_ddp_comp_get_num_formats(comp));
90731c5558dSSean Paul 		if (ret)
90831c5558dSSean Paul 			return ret;
90931c5558dSSean Paul 
91031c5558dSSean Paul 		mtk_crtc->layer_nr++;
91131c5558dSSean Paul 	}
91231c5558dSSean Paul 	return 0;
91331c5558dSSean Paul }
91431c5558dSSean Paul 
mtk_drm_crtc_dma_dev_get(struct drm_crtc * crtc)915cb1d6bccSNancy.Lin struct device *mtk_drm_crtc_dma_dev_get(struct drm_crtc *crtc)
916cb1d6bccSNancy.Lin {
91703e63e49SStuart Lee 	struct mtk_drm_crtc *mtk_crtc = NULL;
91803e63e49SStuart Lee 
91903e63e49SStuart Lee 	if (!crtc)
92003e63e49SStuart Lee 		return NULL;
92103e63e49SStuart Lee 
92203e63e49SStuart Lee 	mtk_crtc = to_mtk_crtc(crtc);
92303e63e49SStuart Lee 	if (!mtk_crtc)
92403e63e49SStuart Lee 		return NULL;
925cb1d6bccSNancy.Lin 
926cb1d6bccSNancy.Lin 	return mtk_crtc->dma_dev;
927cb1d6bccSNancy.Lin }
928cb1d6bccSNancy.Lin 
mtk_drm_crtc_create(struct drm_device * drm_dev,const unsigned int * path,unsigned int path_len,int priv_data_index)929119f5173SCK Hu int mtk_drm_crtc_create(struct drm_device *drm_dev,
9300d9eee91SNancy.Lin 			const unsigned int *path, unsigned int path_len,
9311ef7ed48SNancy.Lin 			int priv_data_index)
932119f5173SCK Hu {
933119f5173SCK Hu 	struct mtk_drm_private *priv = drm_dev->dev_private;
934119f5173SCK Hu 	struct device *dev = drm_dev->dev;
935119f5173SCK Hu 	struct mtk_drm_crtc *mtk_crtc;
93631c5558dSSean Paul 	unsigned int num_comp_planes = 0;
937119f5173SCK Hu 	int ret;
938119f5173SCK Hu 	int i;
93984abcf12SYongqiang Niu 	bool has_ctm = false;
9404cebc1deSYongqiang Niu 	uint gamma_lut_size = 0;
9411ef7ed48SNancy.Lin 	struct drm_crtc *tmp;
9421ef7ed48SNancy.Lin 	int crtc_i = 0;
943119f5173SCK Hu 
944561fad31Sstu.hsieh@mediatek.com 	if (!path)
945561fad31Sstu.hsieh@mediatek.com 		return 0;
946561fad31Sstu.hsieh@mediatek.com 
9471ef7ed48SNancy.Lin 	priv = priv->all_drm_private[priv_data_index];
9481ef7ed48SNancy.Lin 
9491ef7ed48SNancy.Lin 	drm_for_each_crtc(tmp, drm_dev)
9501ef7ed48SNancy.Lin 		crtc_i++;
9511ef7ed48SNancy.Lin 
952119f5173SCK Hu 	for (i = 0; i < path_len; i++) {
953119f5173SCK Hu 		enum mtk_ddp_comp_id comp_id = path[i];
954119f5173SCK Hu 		struct device_node *node;
9555b0ef98eSDafna Hirschfeld 		struct mtk_ddp_comp *comp;
956119f5173SCK Hu 
957119f5173SCK Hu 		node = priv->comp_node[comp_id];
9585b0ef98eSDafna Hirschfeld 		comp = &priv->ddp_comp[comp_id];
9595b0ef98eSDafna Hirschfeld 
9600d9eee91SNancy.Lin 		/* Not all drm components have a DTS device node, such as ovl_adaptor,
9610d9eee91SNancy.Lin 		 * which is the drm bring up sub driver
9620d9eee91SNancy.Lin 		 */
9630d9eee91SNancy.Lin 		if (!node && comp_id != DDP_COMPONENT_DRM_OVL_ADAPTOR) {
964119f5173SCK Hu 			dev_info(dev,
965119f5173SCK Hu 				"Not creating crtc %d because component %d is disabled or missing\n",
9661ef7ed48SNancy.Lin 				crtc_i, comp_id);
967119f5173SCK Hu 			return 0;
968119f5173SCK Hu 		}
9695b0ef98eSDafna Hirschfeld 
9705b0ef98eSDafna Hirschfeld 		if (!comp->dev) {
9715b0ef98eSDafna Hirschfeld 			dev_err(dev, "Component %pOF not initialized\n", node);
9725b0ef98eSDafna Hirschfeld 			return -ENODEV;
9735b0ef98eSDafna Hirschfeld 		}
974119f5173SCK Hu 	}
975119f5173SCK Hu 
976119f5173SCK Hu 	mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
977119f5173SCK Hu 	if (!mtk_crtc)
978119f5173SCK Hu 		return -ENOMEM;
979119f5173SCK Hu 
9802c758e30SEnric Balletbo i Serra 	mtk_crtc->mmsys_dev = priv->mmsys_dev;
981119f5173SCK Hu 	mtk_crtc->ddp_comp_nr = path_len;
982119f5173SCK Hu 	mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr,
983119f5173SCK Hu 						sizeof(*mtk_crtc->ddp_comp),
984119f5173SCK Hu 						GFP_KERNEL);
9854a3bfb5cSChristophe Jaillet 	if (!mtk_crtc->ddp_comp)
9864a3bfb5cSChristophe Jaillet 		return -ENOMEM;
987119f5173SCK Hu 
98842a090b8SCK Hu 	mtk_crtc->mutex = mtk_mutex_get(priv->mutex_dev);
989119f5173SCK Hu 	if (IS_ERR(mtk_crtc->mutex)) {
990119f5173SCK Hu 		ret = PTR_ERR(mtk_crtc->mutex);
991119f5173SCK Hu 		dev_err(dev, "Failed to get mutex: %d\n", ret);
992119f5173SCK Hu 		return ret;
993119f5173SCK Hu 	}
994119f5173SCK Hu 
995119f5173SCK Hu 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
9960d9eee91SNancy.Lin 		unsigned int comp_id = path[i];
997119f5173SCK Hu 		struct mtk_ddp_comp *comp;
998119f5173SCK Hu 
999ff139560SCK Hu 		comp = &priv->ddp_comp[comp_id];
1000119f5173SCK Hu 		mtk_crtc->ddp_comp[i] = comp;
10014cebc1deSYongqiang Niu 
100284abcf12SYongqiang Niu 		if (comp->funcs) {
100384abcf12SYongqiang Niu 			if (comp->funcs->gamma_set)
10044cebc1deSYongqiang Niu 				gamma_lut_size = MTK_LUT_SIZE;
100584abcf12SYongqiang Niu 
100684abcf12SYongqiang Niu 			if (comp->funcs->ctm_set)
100784abcf12SYongqiang Niu 				has_ctm = true;
100884abcf12SYongqiang Niu 		}
1009b74d921bSRex-BC Chen 
1010b74d921bSRex-BC Chen 		mtk_ddp_comp_register_vblank_cb(comp, mtk_crtc_ddp_irq,
1011b74d921bSRex-BC Chen 						&mtk_crtc->base);
1012119f5173SCK Hu 	}
1013119f5173SCK Hu 
101431c5558dSSean Paul 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
101531c5558dSSean Paul 		num_comp_planes += mtk_drm_crtc_num_comp_planes(mtk_crtc, i);
1016412b1e46SYongqiang Niu 
101731c5558dSSean Paul 	mtk_crtc->planes = devm_kcalloc(dev, num_comp_planes,
101831c5558dSSean Paul 					sizeof(struct drm_plane), GFP_KERNEL);
10195bf1e3bdSruanjinjie 	if (!mtk_crtc->planes)
10205bf1e3bdSruanjinjie 		return -ENOMEM;
102166b2cf96SStu Hsieh 
102231c5558dSSean Paul 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
102331c5558dSSean Paul 		ret = mtk_drm_crtc_init_comp_planes(drm_dev, mtk_crtc, i,
10241ef7ed48SNancy.Lin 						    crtc_i);
1025119f5173SCK Hu 		if (ret)
1026937f861dSYongqiang Niu 			return ret;
1027119f5173SCK Hu 	}
1028119f5173SCK Hu 
1029cb1d6bccSNancy.Lin 	/*
1030cb1d6bccSNancy.Lin 	 * Default to use the first component as the dma dev.
1031cb1d6bccSNancy.Lin 	 * In the case of ovl_adaptor sub driver, it needs to use the
1032cb1d6bccSNancy.Lin 	 * dma_dev_get function to get representative dma dev.
1033cb1d6bccSNancy.Lin 	 */
1034cb1d6bccSNancy.Lin 	mtk_crtc->dma_dev = mtk_ddp_comp_dma_dev_get(&priv->ddp_comp[path[0]]);
1035cb1d6bccSNancy.Lin 
10361ef7ed48SNancy.Lin 	ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, crtc_i);
1037119f5173SCK Hu 	if (ret < 0)
1038937f861dSYongqiang Niu 		return ret;
10394cebc1deSYongqiang Niu 
10404cebc1deSYongqiang Niu 	if (gamma_lut_size)
10414cebc1deSYongqiang Niu 		drm_mode_crtc_set_gamma_size(&mtk_crtc->base, gamma_lut_size);
104284abcf12SYongqiang Niu 	drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, has_ctm, gamma_lut_size);
1043920fffccSBibby Hsieh 	mutex_init(&mtk_crtc->hw_lock);
10445b9b8cd2SJason-JH.Lin 	spin_lock_init(&mtk_crtc->config_lock);
1045119f5173SCK Hu 
10462f965be7SBibby Hsieh #if IS_REACHABLE(CONFIG_MTK_CMDQ)
10471ef7ed48SNancy.Lin 	i = priv->mbox_index++;
1048563c9d4aSChun-Kuang Hu 	mtk_crtc->cmdq_client.client.dev = mtk_crtc->mmsys_dev;
1049563c9d4aSChun-Kuang Hu 	mtk_crtc->cmdq_client.client.tx_block = false;
1050563c9d4aSChun-Kuang Hu 	mtk_crtc->cmdq_client.client.knows_txdone = true;
1051563c9d4aSChun-Kuang Hu 	mtk_crtc->cmdq_client.client.rx_callback = ddp_cmdq_cb;
1052563c9d4aSChun-Kuang Hu 	mtk_crtc->cmdq_client.chan =
10531ef7ed48SNancy.Lin 			mbox_request_channel(&mtk_crtc->cmdq_client.client, i);
1054563c9d4aSChun-Kuang Hu 	if (IS_ERR(mtk_crtc->cmdq_client.chan)) {
10552f965be7SBibby Hsieh 		dev_dbg(dev, "mtk_crtc %d failed to create mailbox client, writing register by CPU now\n",
10562f965be7SBibby Hsieh 			drm_crtc_index(&mtk_crtc->base));
1057563c9d4aSChun-Kuang Hu 		mtk_crtc->cmdq_client.chan = NULL;
10582f965be7SBibby Hsieh 	}
1059f85acdadSChun-Kuang Hu 
1060563c9d4aSChun-Kuang Hu 	if (mtk_crtc->cmdq_client.chan) {
106160fa8c13SBibby Hsieh 		ret = of_property_read_u32_index(priv->mutex_node,
106260fa8c13SBibby Hsieh 						 "mediatek,gce-events",
10631ef7ed48SNancy.Lin 						 i,
10642f965be7SBibby Hsieh 						 &mtk_crtc->cmdq_event);
1065f85acdadSChun-Kuang Hu 		if (ret) {
10662f965be7SBibby Hsieh 			dev_dbg(dev, "mtk_crtc %d failed to get mediatek,gce-events property\n",
10672f965be7SBibby Hsieh 				drm_crtc_index(&mtk_crtc->base));
1068563c9d4aSChun-Kuang Hu 			mbox_free_channel(mtk_crtc->cmdq_client.chan);
1069563c9d4aSChun-Kuang Hu 			mtk_crtc->cmdq_client.chan = NULL;
10707627122fSChun-Kuang Hu 		} else {
10717627122fSChun-Kuang Hu 			ret = mtk_drm_cmdq_pkt_create(&mtk_crtc->cmdq_client,
10727627122fSChun-Kuang Hu 						      &mtk_crtc->cmdq_handle,
10737627122fSChun-Kuang Hu 						      PAGE_SIZE);
10747627122fSChun-Kuang Hu 			if (ret) {
10757627122fSChun-Kuang Hu 				dev_dbg(dev, "mtk_crtc %d failed to create cmdq packet\n",
10767627122fSChun-Kuang Hu 					drm_crtc_index(&mtk_crtc->base));
10777627122fSChun-Kuang Hu 				mbox_free_channel(mtk_crtc->cmdq_client.chan);
10787627122fSChun-Kuang Hu 				mtk_crtc->cmdq_client.chan = NULL;
10797627122fSChun-Kuang Hu 			}
1080f85acdadSChun-Kuang Hu 		}
1081aa2d5f2fSjason-jh.lin 
1082aa2d5f2fSjason-jh.lin 		/* for sending blocking cmd in crtc disable */
1083aa2d5f2fSjason-jh.lin 		init_waitqueue_head(&mtk_crtc->cb_blocking_queue);
1084f85acdadSChun-Kuang Hu 	}
10852f965be7SBibby Hsieh #endif
1086119f5173SCK Hu 	return 0;
1087119f5173SCK Hu }
1088