xref: /linux/drivers/gpu/drm/adp/adp_drv.c (revision 6de6674c66bce543c6ae62f49eb35a1ab9bb7425)
1332122ebSSasha Finkelstein // SPDX-License-Identifier: GPL-2.0-only
2332122ebSSasha Finkelstein 
3332122ebSSasha Finkelstein #include <linux/component.h>
4332122ebSSasha Finkelstein #include <linux/iopoll.h>
5332122ebSSasha Finkelstein #include <linux/of.h>
6332122ebSSasha Finkelstein #include <linux/platform_device.h>
7332122ebSSasha Finkelstein 
8332122ebSSasha Finkelstein #include <drm/drm_atomic.h>
9332122ebSSasha Finkelstein #include <drm/drm_atomic_helper.h>
10332122ebSSasha Finkelstein #include <drm/drm_bridge.h>
11332122ebSSasha Finkelstein #include <drm/drm_bridge_connector.h>
12332122ebSSasha Finkelstein #include <drm/drm_drv.h>
13332122ebSSasha Finkelstein #include <drm/drm_fb_dma_helper.h>
14332122ebSSasha Finkelstein #include <drm/drm_framebuffer.h>
15332122ebSSasha Finkelstein #include <drm/drm_gem_atomic_helper.h>
16332122ebSSasha Finkelstein #include <drm/drm_gem_dma_helper.h>
17332122ebSSasha Finkelstein #include <drm/drm_gem_framebuffer_helper.h>
18332122ebSSasha Finkelstein #include <drm/drm_of.h>
19332122ebSSasha Finkelstein #include <drm/drm_probe_helper.h>
20332122ebSSasha Finkelstein #include <drm/drm_vblank.h>
21332122ebSSasha Finkelstein 
22332122ebSSasha Finkelstein #define ADP_INT_STATUS 0x34
23332122ebSSasha Finkelstein #define ADP_INT_STATUS_INT_MASK 0x7
24332122ebSSasha Finkelstein #define ADP_INT_STATUS_VBLANK 0x1
25332122ebSSasha Finkelstein #define ADP_CTRL 0x100
26332122ebSSasha Finkelstein #define ADP_CTRL_VBLANK_ON 0x12
27332122ebSSasha Finkelstein #define ADP_CTRL_FIFO_ON 0x601
28332122ebSSasha Finkelstein #define ADP_SCREEN_SIZE 0x0c
29332122ebSSasha Finkelstein #define ADP_SCREEN_HSIZE GENMASK(15, 0)
30332122ebSSasha Finkelstein #define ADP_SCREEN_VSIZE GENMASK(31, 16)
31332122ebSSasha Finkelstein 
32332122ebSSasha Finkelstein #define ADBE_FIFO 0x10c0
33332122ebSSasha Finkelstein #define ADBE_FIFO_SYNC 0xc0000000
34332122ebSSasha Finkelstein 
35332122ebSSasha Finkelstein #define ADBE_BLEND_BYPASS 0x2020
36332122ebSSasha Finkelstein #define ADBE_BLEND_EN1 0x2028
37332122ebSSasha Finkelstein #define ADBE_BLEND_EN2 0x2074
38332122ebSSasha Finkelstein #define ADBE_BLEND_EN3 0x202c
39332122ebSSasha Finkelstein #define ADBE_BLEND_EN4 0x2034
40332122ebSSasha Finkelstein #define ADBE_MASK_BUF 0x2200
41332122ebSSasha Finkelstein 
42332122ebSSasha Finkelstein #define ADBE_SRC_START 0x4040
43332122ebSSasha Finkelstein #define ADBE_SRC_SIZE 0x4048
44332122ebSSasha Finkelstein #define ADBE_DST_START 0x4050
45332122ebSSasha Finkelstein #define ADBE_DST_SIZE 0x4054
46332122ebSSasha Finkelstein #define ADBE_STRIDE 0x4038
47332122ebSSasha Finkelstein #define ADBE_FB_BASE 0x4030
48332122ebSSasha Finkelstein 
49332122ebSSasha Finkelstein #define ADBE_LAYER_EN1 0x4020
50332122ebSSasha Finkelstein #define ADBE_LAYER_EN2 0x4068
51332122ebSSasha Finkelstein #define ADBE_LAYER_EN3 0x40b4
52332122ebSSasha Finkelstein #define ADBE_LAYER_EN4 0x40f4
53332122ebSSasha Finkelstein #define ADBE_SCALE_CTL 0x40ac
54332122ebSSasha Finkelstein #define ADBE_SCALE_CTL_BYPASS 0x100000
55332122ebSSasha Finkelstein 
56332122ebSSasha Finkelstein #define ADBE_LAYER_CTL 0x1038
57332122ebSSasha Finkelstein #define ADBE_LAYER_CTL_ENABLE 0x10000
58332122ebSSasha Finkelstein 
59332122ebSSasha Finkelstein #define ADBE_PIX_FMT 0x402c
60332122ebSSasha Finkelstein #define ADBE_PIX_FMT_XRGB32 0x53e4001
61332122ebSSasha Finkelstein 
adp_open(struct inode * inode,struct file * filp)62332122ebSSasha Finkelstein static int adp_open(struct inode *inode, struct file *filp)
63332122ebSSasha Finkelstein {
64332122ebSSasha Finkelstein 	/*
65332122ebSSasha Finkelstein 	 * The modesetting driver does not check the non-desktop connector
66332122ebSSasha Finkelstein 	 * property and keeps the device open and locked. If the touchbar daemon
67332122ebSSasha Finkelstein 	 * opens the device first, modesetting breaks the whole X session.
68332122ebSSasha Finkelstein 	 * Simply refuse to open the device for X11 server processes as
69332122ebSSasha Finkelstein 	 * workaround.
70332122ebSSasha Finkelstein 	 */
71332122ebSSasha Finkelstein 	if (current->comm[0] == 'X')
72332122ebSSasha Finkelstein 		return -EBUSY;
73332122ebSSasha Finkelstein 
74332122ebSSasha Finkelstein 	return drm_open(inode, filp);
75332122ebSSasha Finkelstein }
76332122ebSSasha Finkelstein 
77332122ebSSasha Finkelstein static const struct file_operations adp_fops = {
78332122ebSSasha Finkelstein 	.owner          = THIS_MODULE,
79332122ebSSasha Finkelstein 	.open           = adp_open,
80332122ebSSasha Finkelstein 	.release        = drm_release,
81332122ebSSasha Finkelstein 	.unlocked_ioctl = drm_ioctl,
82332122ebSSasha Finkelstein 	.compat_ioctl   = drm_compat_ioctl,
83332122ebSSasha Finkelstein 	.poll           = drm_poll,
84332122ebSSasha Finkelstein 	.read           = drm_read,
85332122ebSSasha Finkelstein 	.llseek         = noop_llseek,
86332122ebSSasha Finkelstein 	.mmap           = drm_gem_mmap,
87332122ebSSasha Finkelstein 	.fop_flags      = FOP_UNSIGNED_OFFSET,
88332122ebSSasha Finkelstein 	DRM_GEM_DMA_UNMAPPED_AREA_FOPS
89332122ebSSasha Finkelstein };
90332122ebSSasha Finkelstein 
adp_drm_gem_dumb_create(struct drm_file * file_priv,struct drm_device * drm,struct drm_mode_create_dumb * args)91332122ebSSasha Finkelstein static int adp_drm_gem_dumb_create(struct drm_file *file_priv,
92332122ebSSasha Finkelstein 					struct drm_device *drm,
93332122ebSSasha Finkelstein 					struct drm_mode_create_dumb *args)
94332122ebSSasha Finkelstein {
95332122ebSSasha Finkelstein 	args->height = ALIGN(args->height, 64);
96332122ebSSasha Finkelstein 	args->size = args->pitch * args->height;
97332122ebSSasha Finkelstein 
98332122ebSSasha Finkelstein 	return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
99332122ebSSasha Finkelstein }
100332122ebSSasha Finkelstein 
101332122ebSSasha Finkelstein static const struct drm_driver adp_driver = {
102332122ebSSasha Finkelstein 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
103332122ebSSasha Finkelstein 	.fops = &adp_fops,
104332122ebSSasha Finkelstein 	DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(adp_drm_gem_dumb_create),
105332122ebSSasha Finkelstein 	.name = "adp",
106332122ebSSasha Finkelstein 	.desc = "Apple Display Pipe DRM Driver",
107332122ebSSasha Finkelstein 	.major = 0,
108332122ebSSasha Finkelstein 	.minor = 1,
109332122ebSSasha Finkelstein };
110332122ebSSasha Finkelstein 
111332122ebSSasha Finkelstein struct adp_drv_private {
112332122ebSSasha Finkelstein 	struct drm_device drm;
113332122ebSSasha Finkelstein 	struct drm_crtc crtc;
114332122ebSSasha Finkelstein 	struct drm_encoder *encoder;
115332122ebSSasha Finkelstein 	struct drm_connector *connector;
116332122ebSSasha Finkelstein 	struct drm_bridge *next_bridge;
117332122ebSSasha Finkelstein 	void __iomem *be;
118332122ebSSasha Finkelstein 	void __iomem *fe;
119332122ebSSasha Finkelstein 	u32 *mask_buf;
120332122ebSSasha Finkelstein 	u64 mask_buf_size;
121332122ebSSasha Finkelstein 	dma_addr_t mask_iova;
122332122ebSSasha Finkelstein 	int be_irq;
123332122ebSSasha Finkelstein 	int fe_irq;
124332122ebSSasha Finkelstein 	struct drm_pending_vblank_event *event;
125332122ebSSasha Finkelstein };
126332122ebSSasha Finkelstein 
127332122ebSSasha Finkelstein #define to_adp(x) container_of(x, struct adp_drv_private, drm)
128332122ebSSasha Finkelstein #define crtc_to_adp(x) container_of(x, struct adp_drv_private, crtc)
129332122ebSSasha Finkelstein 
adp_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)130332122ebSSasha Finkelstein static int adp_plane_atomic_check(struct drm_plane *plane,
131332122ebSSasha Finkelstein 				    struct drm_atomic_state *state)
132332122ebSSasha Finkelstein {
133332122ebSSasha Finkelstein 	struct drm_plane_state *new_plane_state;
134332122ebSSasha Finkelstein 	struct drm_crtc_state *crtc_state;
135332122ebSSasha Finkelstein 
136332122ebSSasha Finkelstein 	new_plane_state = drm_atomic_get_new_plane_state(state, plane);
137332122ebSSasha Finkelstein 
138332122ebSSasha Finkelstein 	if (!new_plane_state->crtc)
139332122ebSSasha Finkelstein 		return 0;
140332122ebSSasha Finkelstein 
141332122ebSSasha Finkelstein 	crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
142332122ebSSasha Finkelstein 	if (IS_ERR(crtc_state))
143332122ebSSasha Finkelstein 		return PTR_ERR(crtc_state);
144332122ebSSasha Finkelstein 
145332122ebSSasha Finkelstein 	return drm_atomic_helper_check_plane_state(new_plane_state,
146332122ebSSasha Finkelstein 						   crtc_state,
147332122ebSSasha Finkelstein 						   DRM_PLANE_NO_SCALING,
148332122ebSSasha Finkelstein 						   DRM_PLANE_NO_SCALING,
149332122ebSSasha Finkelstein 						   true, true);
150332122ebSSasha Finkelstein }
151332122ebSSasha Finkelstein 
adp_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)152332122ebSSasha Finkelstein static void adp_plane_atomic_update(struct drm_plane *plane,
153332122ebSSasha Finkelstein 				    struct drm_atomic_state *state)
154332122ebSSasha Finkelstein {
155332122ebSSasha Finkelstein 	struct adp_drv_private *adp;
156332122ebSSasha Finkelstein 	struct drm_rect src_rect;
157332122ebSSasha Finkelstein 	struct drm_gem_dma_object *obj;
158332122ebSSasha Finkelstein 	struct drm_framebuffer *fb;
159332122ebSSasha Finkelstein 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
160332122ebSSasha Finkelstein 	u32 src_pos, src_size, dst_pos, dst_size;
161332122ebSSasha Finkelstein 
162332122ebSSasha Finkelstein 	if (!plane || !new_state)
163332122ebSSasha Finkelstein 		return;
164332122ebSSasha Finkelstein 
165332122ebSSasha Finkelstein 	fb = new_state->fb;
166332122ebSSasha Finkelstein 	if (!fb)
167332122ebSSasha Finkelstein 		return;
168332122ebSSasha Finkelstein 	adp = to_adp(plane->dev);
169332122ebSSasha Finkelstein 
170332122ebSSasha Finkelstein 	drm_rect_fp_to_int(&src_rect, &new_state->src);
171332122ebSSasha Finkelstein 	src_pos = src_rect.x1 << 16 | src_rect.y1;
172332122ebSSasha Finkelstein 	dst_pos = new_state->dst.x1 << 16 | new_state->dst.y1;
173332122ebSSasha Finkelstein 	src_size = drm_rect_width(&src_rect) << 16 | drm_rect_height(&src_rect);
174332122ebSSasha Finkelstein 	dst_size = drm_rect_width(&new_state->dst) << 16 |
175332122ebSSasha Finkelstein 		drm_rect_height(&new_state->dst);
176332122ebSSasha Finkelstein 	writel(src_pos, adp->be + ADBE_SRC_START);
177332122ebSSasha Finkelstein 	writel(src_size, adp->be + ADBE_SRC_SIZE);
178332122ebSSasha Finkelstein 	writel(dst_pos, adp->be + ADBE_DST_START);
179332122ebSSasha Finkelstein 	writel(dst_size, adp->be + ADBE_DST_SIZE);
180332122ebSSasha Finkelstein 	writel(fb->pitches[0], adp->be + ADBE_STRIDE);
181332122ebSSasha Finkelstein 	obj = drm_fb_dma_get_gem_obj(fb, 0);
182332122ebSSasha Finkelstein 	if (obj)
183332122ebSSasha Finkelstein 		writel(obj->dma_addr + fb->offsets[0], adp->be + ADBE_FB_BASE);
184332122ebSSasha Finkelstein 
185332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_LAYER_EN1);
186332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_LAYER_EN2);
187332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_LAYER_EN3);
188332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_LAYER_EN4);
189332122ebSSasha Finkelstein 	writel(ADBE_SCALE_CTL_BYPASS, adp->be + ADBE_SCALE_CTL);
190332122ebSSasha Finkelstein 	writel(ADBE_LAYER_CTL_ENABLE | BIT(0), adp->be + ADBE_LAYER_CTL);
191332122ebSSasha Finkelstein 	writel(ADBE_PIX_FMT_XRGB32, adp->be + ADBE_PIX_FMT);
192332122ebSSasha Finkelstein }
193332122ebSSasha Finkelstein 
adp_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)194332122ebSSasha Finkelstein static void adp_plane_atomic_disable(struct drm_plane *plane,
195332122ebSSasha Finkelstein 				     struct drm_atomic_state *state)
196332122ebSSasha Finkelstein {
197332122ebSSasha Finkelstein 	struct adp_drv_private *adp = to_adp(plane->dev);
198332122ebSSasha Finkelstein 
199332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_LAYER_EN1);
200332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_LAYER_EN2);
201332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_LAYER_EN3);
202332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_LAYER_EN4);
203332122ebSSasha Finkelstein 	writel(ADBE_LAYER_CTL_ENABLE, adp->be + ADBE_LAYER_CTL);
204332122ebSSasha Finkelstein }
205332122ebSSasha Finkelstein 
206332122ebSSasha Finkelstein static const struct drm_plane_helper_funcs adp_plane_helper_funcs = {
207332122ebSSasha Finkelstein 	.atomic_check = adp_plane_atomic_check,
208332122ebSSasha Finkelstein 	.atomic_update = adp_plane_atomic_update,
209332122ebSSasha Finkelstein 	.atomic_disable = adp_plane_atomic_disable,
210332122ebSSasha Finkelstein 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS
211332122ebSSasha Finkelstein };
212332122ebSSasha Finkelstein 
213332122ebSSasha Finkelstein static const struct drm_plane_funcs adp_plane_funcs = {
214332122ebSSasha Finkelstein 	.update_plane = drm_atomic_helper_update_plane,
215332122ebSSasha Finkelstein 	.disable_plane = drm_atomic_helper_disable_plane,
216332122ebSSasha Finkelstein 	DRM_GEM_SHADOW_PLANE_FUNCS
217332122ebSSasha Finkelstein };
218332122ebSSasha Finkelstein 
219332122ebSSasha Finkelstein static const u32 plane_formats[] = {
220332122ebSSasha Finkelstein 	DRM_FORMAT_XRGB8888,
221332122ebSSasha Finkelstein };
222332122ebSSasha Finkelstein 
223332122ebSSasha Finkelstein #define ALL_CRTCS 1
224332122ebSSasha Finkelstein 
adp_plane_new(struct adp_drv_private * adp)225332122ebSSasha Finkelstein static struct drm_plane *adp_plane_new(struct adp_drv_private *adp)
226332122ebSSasha Finkelstein {
227332122ebSSasha Finkelstein 	struct drm_device *drm = &adp->drm;
228332122ebSSasha Finkelstein 	struct drm_plane *plane;
229332122ebSSasha Finkelstein 
230332122ebSSasha Finkelstein 	plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0,
231332122ebSSasha Finkelstein 					     ALL_CRTCS, &adp_plane_funcs,
232332122ebSSasha Finkelstein 					     plane_formats, ARRAY_SIZE(plane_formats),
233332122ebSSasha Finkelstein 					     NULL, DRM_PLANE_TYPE_PRIMARY, "plane");
234ee20c69cSDan Carpenter 	if (IS_ERR(plane)) {
235332122ebSSasha Finkelstein 		drm_err(drm, "failed to allocate plane");
236ee20c69cSDan Carpenter 		return plane;
237332122ebSSasha Finkelstein 	}
238332122ebSSasha Finkelstein 
239332122ebSSasha Finkelstein 	drm_plane_helper_add(plane, &adp_plane_helper_funcs);
240332122ebSSasha Finkelstein 	return plane;
241332122ebSSasha Finkelstein }
242332122ebSSasha Finkelstein 
adp_enable_vblank(struct adp_drv_private * adp)243332122ebSSasha Finkelstein static void adp_enable_vblank(struct adp_drv_private *adp)
244332122ebSSasha Finkelstein {
245332122ebSSasha Finkelstein 	u32 cur_ctrl;
246332122ebSSasha Finkelstein 
247332122ebSSasha Finkelstein 	writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
248332122ebSSasha Finkelstein 
249332122ebSSasha Finkelstein 	cur_ctrl = readl(adp->fe + ADP_CTRL);
250332122ebSSasha Finkelstein 	writel(cur_ctrl | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
251332122ebSSasha Finkelstein }
252332122ebSSasha Finkelstein 
adp_crtc_enable_vblank(struct drm_crtc * crtc)253332122ebSSasha Finkelstein static int adp_crtc_enable_vblank(struct drm_crtc *crtc)
254332122ebSSasha Finkelstein {
255332122ebSSasha Finkelstein 	struct drm_device *dev = crtc->dev;
256332122ebSSasha Finkelstein 	struct adp_drv_private *adp = to_adp(dev);
257332122ebSSasha Finkelstein 
258332122ebSSasha Finkelstein 	adp_enable_vblank(adp);
259332122ebSSasha Finkelstein 
260332122ebSSasha Finkelstein 	return 0;
261332122ebSSasha Finkelstein }
262332122ebSSasha Finkelstein 
adp_disable_vblank(struct adp_drv_private * adp)263332122ebSSasha Finkelstein static void adp_disable_vblank(struct adp_drv_private *adp)
264332122ebSSasha Finkelstein {
265332122ebSSasha Finkelstein 	u32 cur_ctrl;
266332122ebSSasha Finkelstein 
267332122ebSSasha Finkelstein 	cur_ctrl = readl(adp->fe + ADP_CTRL);
268332122ebSSasha Finkelstein 	writel(cur_ctrl & ~ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
269332122ebSSasha Finkelstein 	writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
270332122ebSSasha Finkelstein }
271332122ebSSasha Finkelstein 
adp_crtc_disable_vblank(struct drm_crtc * crtc)272332122ebSSasha Finkelstein static void adp_crtc_disable_vblank(struct drm_crtc *crtc)
273332122ebSSasha Finkelstein {
274332122ebSSasha Finkelstein 	struct drm_device *dev = crtc->dev;
275332122ebSSasha Finkelstein 	struct adp_drv_private *adp = to_adp(dev);
276332122ebSSasha Finkelstein 
277332122ebSSasha Finkelstein 	adp_disable_vblank(adp);
278332122ebSSasha Finkelstein }
279332122ebSSasha Finkelstein 
adp_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)280332122ebSSasha Finkelstein static void adp_crtc_atomic_enable(struct drm_crtc *crtc,
281332122ebSSasha Finkelstein 				   struct drm_atomic_state *state)
282332122ebSSasha Finkelstein {
283332122ebSSasha Finkelstein 	struct adp_drv_private *adp = crtc_to_adp(crtc);
284332122ebSSasha Finkelstein 
285332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_BLEND_EN2);
286332122ebSSasha Finkelstein 	writel(BIT(4), adp->be + ADBE_BLEND_EN1);
287332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_BLEND_EN3);
288332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_BLEND_BYPASS);
289332122ebSSasha Finkelstein 	writel(BIT(0), adp->be + ADBE_BLEND_EN4);
290*c082a521SJanne Grunau 	drm_crtc_vblank_on(crtc);
291332122ebSSasha Finkelstein }
292332122ebSSasha Finkelstein 
adp_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)293332122ebSSasha Finkelstein static void adp_crtc_atomic_disable(struct drm_crtc *crtc,
294332122ebSSasha Finkelstein 				    struct drm_atomic_state *state)
295332122ebSSasha Finkelstein {
296332122ebSSasha Finkelstein 	struct adp_drv_private *adp = crtc_to_adp(crtc);
297332122ebSSasha Finkelstein 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
298332122ebSSasha Finkelstein 
299332122ebSSasha Finkelstein 	drm_atomic_helper_disable_planes_on_crtc(old_state, false);
300332122ebSSasha Finkelstein 
301332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_BLEND_EN2);
302332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_BLEND_EN1);
303332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_BLEND_EN3);
304332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_BLEND_BYPASS);
305332122ebSSasha Finkelstein 	writel(0x0, adp->be + ADBE_BLEND_EN4);
306332122ebSSasha Finkelstein 	drm_crtc_vblank_off(crtc);
307332122ebSSasha Finkelstein }
308332122ebSSasha Finkelstein 
adp_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)309332122ebSSasha Finkelstein static void adp_crtc_atomic_flush(struct drm_crtc *crtc,
310332122ebSSasha Finkelstein 				  struct drm_atomic_state *state)
311332122ebSSasha Finkelstein {
312332122ebSSasha Finkelstein 	u32 frame_num = 1;
313bc43f711SJanne Grunau 	unsigned long flags;
314332122ebSSasha Finkelstein 	struct adp_drv_private *adp = crtc_to_adp(crtc);
315332122ebSSasha Finkelstein 	struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc);
316332122ebSSasha Finkelstein 	u64 new_size = ALIGN(new_state->mode.hdisplay *
317332122ebSSasha Finkelstein 			     new_state->mode.vdisplay * 4, PAGE_SIZE);
318332122ebSSasha Finkelstein 
319332122ebSSasha Finkelstein 	if (new_size != adp->mask_buf_size) {
320332122ebSSasha Finkelstein 		if (adp->mask_buf)
321332122ebSSasha Finkelstein 			dma_free_coherent(crtc->dev->dev, adp->mask_buf_size,
322332122ebSSasha Finkelstein 					  adp->mask_buf, adp->mask_iova);
323332122ebSSasha Finkelstein 		adp->mask_buf = NULL;
324332122ebSSasha Finkelstein 		if (new_size != 0) {
325332122ebSSasha Finkelstein 			adp->mask_buf = dma_alloc_coherent(crtc->dev->dev, new_size,
326332122ebSSasha Finkelstein 							   &adp->mask_iova, GFP_KERNEL);
327332122ebSSasha Finkelstein 			memset(adp->mask_buf, 0xFF, new_size);
328332122ebSSasha Finkelstein 			writel(adp->mask_iova, adp->be + ADBE_MASK_BUF);
329332122ebSSasha Finkelstein 		}
330332122ebSSasha Finkelstein 		adp->mask_buf_size = new_size;
331332122ebSSasha Finkelstein 	}
332332122ebSSasha Finkelstein 	writel(ADBE_FIFO_SYNC | frame_num, adp->be + ADBE_FIFO);
333332122ebSSasha Finkelstein 	//FIXME: use adbe flush interrupt
334332122ebSSasha Finkelstein 	if (crtc->state->event) {
3357a7d6681SJanne Grunau 		struct drm_pending_vblank_event *event = crtc->state->event;
3367a7d6681SJanne Grunau 
337332122ebSSasha Finkelstein 		crtc->state->event = NULL;
3387a7d6681SJanne Grunau 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
3397a7d6681SJanne Grunau 
3407a7d6681SJanne Grunau 		if (drm_crtc_vblank_get(crtc) != 0)
3417a7d6681SJanne Grunau 			drm_crtc_send_vblank_event(crtc, event);
3427a7d6681SJanne Grunau 		else
3437a7d6681SJanne Grunau 			adp->event = event;
3447a7d6681SJanne Grunau 
345bc43f711SJanne Grunau 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
346332122ebSSasha Finkelstein 	}
3477a7d6681SJanne Grunau }
348332122ebSSasha Finkelstein 
349332122ebSSasha Finkelstein static const struct drm_crtc_funcs adp_crtc_funcs = {
350332122ebSSasha Finkelstein 	.destroy = drm_crtc_cleanup,
351332122ebSSasha Finkelstein 	.set_config = drm_atomic_helper_set_config,
352332122ebSSasha Finkelstein 	.page_flip = drm_atomic_helper_page_flip,
353332122ebSSasha Finkelstein 	.reset = drm_atomic_helper_crtc_reset,
354332122ebSSasha Finkelstein 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
355332122ebSSasha Finkelstein 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
356332122ebSSasha Finkelstein 	.enable_vblank = adp_crtc_enable_vblank,
357332122ebSSasha Finkelstein 	.disable_vblank = adp_crtc_disable_vblank,
358332122ebSSasha Finkelstein };
359332122ebSSasha Finkelstein 
360332122ebSSasha Finkelstein 
361332122ebSSasha Finkelstein static const struct drm_crtc_helper_funcs adp_crtc_helper_funcs = {
362332122ebSSasha Finkelstein 	.atomic_enable = adp_crtc_atomic_enable,
363332122ebSSasha Finkelstein 	.atomic_disable = adp_crtc_atomic_disable,
364332122ebSSasha Finkelstein 	.atomic_flush = adp_crtc_atomic_flush,
365332122ebSSasha Finkelstein };
366332122ebSSasha Finkelstein 
adp_setup_crtc(struct adp_drv_private * adp)367332122ebSSasha Finkelstein static int adp_setup_crtc(struct adp_drv_private *adp)
368332122ebSSasha Finkelstein {
369332122ebSSasha Finkelstein 	struct drm_device *drm = &adp->drm;
370332122ebSSasha Finkelstein 	struct drm_plane *primary;
371332122ebSSasha Finkelstein 	int ret;
372332122ebSSasha Finkelstein 
373332122ebSSasha Finkelstein 	primary = adp_plane_new(adp);
374332122ebSSasha Finkelstein 	if (IS_ERR(primary))
375332122ebSSasha Finkelstein 		return PTR_ERR(primary);
376332122ebSSasha Finkelstein 
377332122ebSSasha Finkelstein 	ret = drm_crtc_init_with_planes(drm, &adp->crtc, primary,
378332122ebSSasha Finkelstein 					NULL, &adp_crtc_funcs, NULL);
379332122ebSSasha Finkelstein 	if (ret)
380332122ebSSasha Finkelstein 		return ret;
381332122ebSSasha Finkelstein 
382332122ebSSasha Finkelstein 	drm_crtc_helper_add(&adp->crtc, &adp_crtc_helper_funcs);
383332122ebSSasha Finkelstein 	return 0;
384332122ebSSasha Finkelstein }
385332122ebSSasha Finkelstein 
386332122ebSSasha Finkelstein static const struct drm_mode_config_funcs adp_mode_config_funcs = {
387332122ebSSasha Finkelstein 	.fb_create = drm_gem_fb_create_with_dirty,
388332122ebSSasha Finkelstein 	.atomic_check = drm_atomic_helper_check,
389332122ebSSasha Finkelstein 	.atomic_commit = drm_atomic_helper_commit,
390332122ebSSasha Finkelstein };
391332122ebSSasha Finkelstein 
adp_setup_mode_config(struct adp_drv_private * adp)392332122ebSSasha Finkelstein static int adp_setup_mode_config(struct adp_drv_private *adp)
393332122ebSSasha Finkelstein {
394332122ebSSasha Finkelstein 	struct drm_device *drm = &adp->drm;
395332122ebSSasha Finkelstein 	int ret;
396332122ebSSasha Finkelstein 	u32 size;
397332122ebSSasha Finkelstein 
398332122ebSSasha Finkelstein 	ret = drmm_mode_config_init(drm);
399332122ebSSasha Finkelstein 	if (ret)
400332122ebSSasha Finkelstein 		return ret;
401332122ebSSasha Finkelstein 
402332122ebSSasha Finkelstein 	/*
403332122ebSSasha Finkelstein 	 * Query screen size restrict the frame buffer size to the screen size
404332122ebSSasha Finkelstein 	 * aligned to the next multiple of 64. This is not necessary but can be
405332122ebSSasha Finkelstein 	 * used as simple check for non-desktop devices.
406332122ebSSasha Finkelstein 	 * Xorg's modesetting driver does not care about the connector
407332122ebSSasha Finkelstein 	 * "non-desktop" property. The max frame buffer width or height can be
408332122ebSSasha Finkelstein 	 * easily checked and a device can be reject if the max width/height is
409332122ebSSasha Finkelstein 	 * smaller than 120 for example.
410332122ebSSasha Finkelstein 	 * Any touchbar daemon is not limited by this small framebuffer size.
411332122ebSSasha Finkelstein 	 */
412332122ebSSasha Finkelstein 	size = readl(adp->fe + ADP_SCREEN_SIZE);
413332122ebSSasha Finkelstein 
414332122ebSSasha Finkelstein 	drm->mode_config.min_width = 32;
415332122ebSSasha Finkelstein 	drm->mode_config.min_height = 32;
416332122ebSSasha Finkelstein 	drm->mode_config.max_width = ALIGN(FIELD_GET(ADP_SCREEN_HSIZE, size), 64);
417332122ebSSasha Finkelstein 	drm->mode_config.max_height = ALIGN(FIELD_GET(ADP_SCREEN_VSIZE, size), 64);
418332122ebSSasha Finkelstein 	drm->mode_config.preferred_depth = 24;
419332122ebSSasha Finkelstein 	drm->mode_config.prefer_shadow = 0;
420332122ebSSasha Finkelstein 	drm->mode_config.funcs = &adp_mode_config_funcs;
421332122ebSSasha Finkelstein 
422332122ebSSasha Finkelstein 	ret = adp_setup_crtc(adp);
423332122ebSSasha Finkelstein 	if (ret) {
424332122ebSSasha Finkelstein 		drm_err(drm, "failed to create crtc");
425332122ebSSasha Finkelstein 		return ret;
426332122ebSSasha Finkelstein 	}
427332122ebSSasha Finkelstein 
428332122ebSSasha Finkelstein 	adp->encoder = drmm_plain_encoder_alloc(drm, NULL, DRM_MODE_ENCODER_DSI, NULL);
429332122ebSSasha Finkelstein 	if (IS_ERR(adp->encoder)) {
430332122ebSSasha Finkelstein 		drm_err(drm, "failed to init encoder");
431332122ebSSasha Finkelstein 		return PTR_ERR(adp->encoder);
432332122ebSSasha Finkelstein 	}
433332122ebSSasha Finkelstein 	adp->encoder->possible_crtcs = ALL_CRTCS;
434332122ebSSasha Finkelstein 
435332122ebSSasha Finkelstein 	ret = drm_bridge_attach(adp->encoder, adp->next_bridge, NULL,
436332122ebSSasha Finkelstein 				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
437332122ebSSasha Finkelstein 	if (ret) {
438332122ebSSasha Finkelstein 		drm_err(drm, "failed to init bridge chain");
439332122ebSSasha Finkelstein 		return ret;
440332122ebSSasha Finkelstein 	}
441332122ebSSasha Finkelstein 
442332122ebSSasha Finkelstein 	adp->connector = drm_bridge_connector_init(drm, adp->encoder);
443332122ebSSasha Finkelstein 	if (IS_ERR(adp->connector))
444332122ebSSasha Finkelstein 		return PTR_ERR(adp->connector);
445332122ebSSasha Finkelstein 
446332122ebSSasha Finkelstein 	drm_connector_attach_encoder(adp->connector, adp->encoder);
447332122ebSSasha Finkelstein 
448332122ebSSasha Finkelstein 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
449332122ebSSasha Finkelstein 	if (ret < 0) {
450332122ebSSasha Finkelstein 		drm_err(drm, "failed to initialize vblank");
451332122ebSSasha Finkelstein 		return ret;
452332122ebSSasha Finkelstein 	}
453332122ebSSasha Finkelstein 
454332122ebSSasha Finkelstein 	drm_mode_config_reset(drm);
455332122ebSSasha Finkelstein 
456332122ebSSasha Finkelstein 	return 0;
457332122ebSSasha Finkelstein }
458332122ebSSasha Finkelstein 
adp_parse_of(struct platform_device * pdev,struct adp_drv_private * adp)459332122ebSSasha Finkelstein static int adp_parse_of(struct platform_device *pdev, struct adp_drv_private *adp)
460332122ebSSasha Finkelstein {
461332122ebSSasha Finkelstein 	struct device *dev = &pdev->dev;
462332122ebSSasha Finkelstein 
463332122ebSSasha Finkelstein 	adp->be = devm_platform_ioremap_resource_byname(pdev, "be");
464332122ebSSasha Finkelstein 	if (IS_ERR(adp->be)) {
465332122ebSSasha Finkelstein 		dev_err(dev, "failed to map display backend mmio");
466332122ebSSasha Finkelstein 		return PTR_ERR(adp->be);
467332122ebSSasha Finkelstein 	}
468332122ebSSasha Finkelstein 
469332122ebSSasha Finkelstein 	adp->fe = devm_platform_ioremap_resource_byname(pdev, "fe");
470332122ebSSasha Finkelstein 	if (IS_ERR(adp->fe)) {
471332122ebSSasha Finkelstein 		dev_err(dev, "failed to map display pipe mmio");
472332122ebSSasha Finkelstein 		return PTR_ERR(adp->fe);
473332122ebSSasha Finkelstein 	}
474332122ebSSasha Finkelstein 
475332122ebSSasha Finkelstein 	adp->be_irq = platform_get_irq_byname(pdev, "be");
476e4c0fd3fSJiapeng Chong 	if (adp->be_irq < 0)
477332122ebSSasha Finkelstein 		return adp->be_irq;
478332122ebSSasha Finkelstein 
479332122ebSSasha Finkelstein 	adp->fe_irq = platform_get_irq_byname(pdev, "fe");
480e4c0fd3fSJiapeng Chong 	if (adp->fe_irq < 0)
481332122ebSSasha Finkelstein 		return adp->fe_irq;
482332122ebSSasha Finkelstein 
483332122ebSSasha Finkelstein 	return 0;
484332122ebSSasha Finkelstein }
485332122ebSSasha Finkelstein 
adp_fe_irq(int irq,void * arg)486332122ebSSasha Finkelstein static irqreturn_t adp_fe_irq(int irq, void *arg)
487332122ebSSasha Finkelstein {
488332122ebSSasha Finkelstein 	struct adp_drv_private *adp = (struct adp_drv_private *)arg;
489332122ebSSasha Finkelstein 	u32 int_status;
490332122ebSSasha Finkelstein 	u32 int_ctl;
491332122ebSSasha Finkelstein 
492332122ebSSasha Finkelstein 	int_status = readl(adp->fe + ADP_INT_STATUS);
493332122ebSSasha Finkelstein 	if (int_status & ADP_INT_STATUS_VBLANK) {
494332122ebSSasha Finkelstein 		drm_crtc_handle_vblank(&adp->crtc);
495332122ebSSasha Finkelstein 		spin_lock(&adp->crtc.dev->event_lock);
496332122ebSSasha Finkelstein 		if (adp->event) {
497332122ebSSasha Finkelstein 			int_ctl = readl(adp->fe + ADP_CTRL);
498332122ebSSasha Finkelstein 			if ((int_ctl & 0xF00) == 0x600) {
499332122ebSSasha Finkelstein 				drm_crtc_send_vblank_event(&adp->crtc, adp->event);
500332122ebSSasha Finkelstein 				adp->event = NULL;
501332122ebSSasha Finkelstein 				drm_crtc_vblank_put(&adp->crtc);
502332122ebSSasha Finkelstein 			}
503332122ebSSasha Finkelstein 		}
504332122ebSSasha Finkelstein 		spin_unlock(&adp->crtc.dev->event_lock);
505332122ebSSasha Finkelstein 	}
506332122ebSSasha Finkelstein 
507332122ebSSasha Finkelstein 	writel(int_status, adp->fe + ADP_INT_STATUS);
508332122ebSSasha Finkelstein 
509332122ebSSasha Finkelstein 
510332122ebSSasha Finkelstein 	return IRQ_HANDLED;
511332122ebSSasha Finkelstein }
512332122ebSSasha Finkelstein 
adp_drm_bind(struct device * dev)513332122ebSSasha Finkelstein static int adp_drm_bind(struct device *dev)
514332122ebSSasha Finkelstein {
515332122ebSSasha Finkelstein 	struct drm_device *drm = dev_get_drvdata(dev);
516332122ebSSasha Finkelstein 	struct adp_drv_private *adp = to_adp(drm);
517332122ebSSasha Finkelstein 	int err;
518332122ebSSasha Finkelstein 
519*c082a521SJanne Grunau 	writel(ADP_CTRL_FIFO_ON, adp->fe + ADP_CTRL);
520332122ebSSasha Finkelstein 
521332122ebSSasha Finkelstein 	adp->next_bridge = drmm_of_get_bridge(&adp->drm, dev->of_node, 0, 0);
522332122ebSSasha Finkelstein 	if (IS_ERR(adp->next_bridge)) {
523332122ebSSasha Finkelstein 		dev_err(dev, "failed to find next bridge");
524332122ebSSasha Finkelstein 		return PTR_ERR(adp->next_bridge);
525332122ebSSasha Finkelstein 	}
526332122ebSSasha Finkelstein 
527332122ebSSasha Finkelstein 	err = adp_setup_mode_config(adp);
528332122ebSSasha Finkelstein 	if (err < 0)
529332122ebSSasha Finkelstein 		return err;
530332122ebSSasha Finkelstein 
531332122ebSSasha Finkelstein 	err = request_irq(adp->fe_irq, adp_fe_irq, 0, "adp-fe", adp);
532332122ebSSasha Finkelstein 	if (err)
533332122ebSSasha Finkelstein 		return err;
534332122ebSSasha Finkelstein 
535332122ebSSasha Finkelstein 	err = drm_dev_register(&adp->drm, 0);
536332122ebSSasha Finkelstein 	if (err)
537332122ebSSasha Finkelstein 		return err;
538332122ebSSasha Finkelstein 
539332122ebSSasha Finkelstein 	return 0;
540332122ebSSasha Finkelstein }
541332122ebSSasha Finkelstein 
adp_drm_unbind(struct device * dev)542332122ebSSasha Finkelstein static void adp_drm_unbind(struct device *dev)
543332122ebSSasha Finkelstein {
544332122ebSSasha Finkelstein 	struct drm_device *drm = dev_get_drvdata(dev);
545332122ebSSasha Finkelstein 	struct adp_drv_private *adp = to_adp(drm);
546332122ebSSasha Finkelstein 
547332122ebSSasha Finkelstein 	drm_dev_unregister(drm);
548332122ebSSasha Finkelstein 	drm_atomic_helper_shutdown(drm);
549332122ebSSasha Finkelstein 	free_irq(adp->fe_irq, adp);
550332122ebSSasha Finkelstein }
551332122ebSSasha Finkelstein 
552332122ebSSasha Finkelstein static const struct component_master_ops adp_master_ops = {
553332122ebSSasha Finkelstein 	.bind	= adp_drm_bind,
554332122ebSSasha Finkelstein 	.unbind = adp_drm_unbind,
555332122ebSSasha Finkelstein };
556332122ebSSasha Finkelstein 
compare_dev(struct device * dev,void * data)557332122ebSSasha Finkelstein static int compare_dev(struct device *dev, void *data)
558332122ebSSasha Finkelstein {
559332122ebSSasha Finkelstein 	return dev->of_node == data;
560332122ebSSasha Finkelstein }
561332122ebSSasha Finkelstein 
adp_probe(struct platform_device * pdev)562332122ebSSasha Finkelstein static int adp_probe(struct platform_device *pdev)
563332122ebSSasha Finkelstein {
564332122ebSSasha Finkelstein 	struct device_node *port;
565332122ebSSasha Finkelstein 	struct component_match *match = NULL;
566332122ebSSasha Finkelstein 	struct adp_drv_private *adp;
567332122ebSSasha Finkelstein 	int err;
568332122ebSSasha Finkelstein 
569332122ebSSasha Finkelstein 	adp = devm_drm_dev_alloc(&pdev->dev, &adp_driver, struct adp_drv_private, drm);
570332122ebSSasha Finkelstein 	if (IS_ERR(adp))
571332122ebSSasha Finkelstein 		return PTR_ERR(adp);
572332122ebSSasha Finkelstein 
573332122ebSSasha Finkelstein 	dev_set_drvdata(&pdev->dev, &adp->drm);
574332122ebSSasha Finkelstein 
575332122ebSSasha Finkelstein 	err = adp_parse_of(pdev, adp);
576332122ebSSasha Finkelstein 	if (err < 0)
577332122ebSSasha Finkelstein 		return err;
578332122ebSSasha Finkelstein 
579332122ebSSasha Finkelstein 	port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
580332122ebSSasha Finkelstein 	if (!port)
581332122ebSSasha Finkelstein 		return -ENODEV;
582332122ebSSasha Finkelstein 
583332122ebSSasha Finkelstein 	drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
584332122ebSSasha Finkelstein 	of_node_put(port);
585332122ebSSasha Finkelstein 
586332122ebSSasha Finkelstein 	return component_master_add_with_match(&pdev->dev, &adp_master_ops, match);
587332122ebSSasha Finkelstein }
588332122ebSSasha Finkelstein 
adp_remove(struct platform_device * pdev)589332122ebSSasha Finkelstein static void adp_remove(struct platform_device *pdev)
590332122ebSSasha Finkelstein {
591332122ebSSasha Finkelstein 	component_master_del(&pdev->dev, &adp_master_ops);
592332122ebSSasha Finkelstein 	dev_set_drvdata(&pdev->dev, NULL);
593332122ebSSasha Finkelstein }
594332122ebSSasha Finkelstein 
595332122ebSSasha Finkelstein static const struct of_device_id adp_of_match[] = {
596332122ebSSasha Finkelstein 	{ .compatible = "apple,h7-display-pipe", },
597332122ebSSasha Finkelstein 	{ },
598332122ebSSasha Finkelstein };
599332122ebSSasha Finkelstein MODULE_DEVICE_TABLE(of, adp_of_match);
600332122ebSSasha Finkelstein 
601332122ebSSasha Finkelstein static struct platform_driver adp_platform_driver = {
602332122ebSSasha Finkelstein 	.driver = {
603332122ebSSasha Finkelstein 		.name = "adp",
604332122ebSSasha Finkelstein 		.of_match_table = adp_of_match,
605332122ebSSasha Finkelstein 	},
606332122ebSSasha Finkelstein 	.probe = adp_probe,
607332122ebSSasha Finkelstein 	.remove = adp_remove,
608332122ebSSasha Finkelstein };
609332122ebSSasha Finkelstein 
610332122ebSSasha Finkelstein module_platform_driver(adp_platform_driver);
611332122ebSSasha Finkelstein 
612332122ebSSasha Finkelstein MODULE_DESCRIPTION("Apple Display Pipe DRM driver");
613332122ebSSasha Finkelstein MODULE_LICENSE("GPL");
614