xref: /openbmc/linux/drivers/gpu/drm/tiny/ofdrm.c (revision c8a17756c42581ba1a567d1dd3b69e8f5619a7d8)
1*c8a17756SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
2*c8a17756SThomas Zimmermann 
3*c8a17756SThomas Zimmermann #include <linux/of_address.h>
4*c8a17756SThomas Zimmermann #include <linux/pci.h>
5*c8a17756SThomas Zimmermann #include <linux/platform_device.h>
6*c8a17756SThomas Zimmermann 
7*c8a17756SThomas Zimmermann #include <drm/drm_aperture.h>
8*c8a17756SThomas Zimmermann #include <drm/drm_atomic.h>
9*c8a17756SThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
10*c8a17756SThomas Zimmermann #include <drm/drm_connector.h>
11*c8a17756SThomas Zimmermann #include <drm/drm_damage_helper.h>
12*c8a17756SThomas Zimmermann #include <drm/drm_device.h>
13*c8a17756SThomas Zimmermann #include <drm/drm_drv.h>
14*c8a17756SThomas Zimmermann #include <drm/drm_fb_helper.h>
15*c8a17756SThomas Zimmermann #include <drm/drm_format_helper.h>
16*c8a17756SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
17*c8a17756SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
18*c8a17756SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
19*c8a17756SThomas Zimmermann #include <drm/drm_managed.h>
20*c8a17756SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
21*c8a17756SThomas Zimmermann #include <drm/drm_plane_helper.h>
22*c8a17756SThomas Zimmermann #include <drm/drm_probe_helper.h>
23*c8a17756SThomas Zimmermann #include <drm/drm_simple_kms_helper.h>
24*c8a17756SThomas Zimmermann 
25*c8a17756SThomas Zimmermann #define DRIVER_NAME	"ofdrm"
26*c8a17756SThomas Zimmermann #define DRIVER_DESC	"DRM driver for OF platform devices"
27*c8a17756SThomas Zimmermann #define DRIVER_DATE	"20220501"
28*c8a17756SThomas Zimmermann #define DRIVER_MAJOR	1
29*c8a17756SThomas Zimmermann #define DRIVER_MINOR	0
30*c8a17756SThomas Zimmermann 
31*c8a17756SThomas Zimmermann /*
32*c8a17756SThomas Zimmermann  * Helpers for display nodes
33*c8a17756SThomas Zimmermann  */
34*c8a17756SThomas Zimmermann 
35*c8a17756SThomas Zimmermann static int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value)
36*c8a17756SThomas Zimmermann {
37*c8a17756SThomas Zimmermann 	if (value > INT_MAX) {
38*c8a17756SThomas Zimmermann 		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
39*c8a17756SThomas Zimmermann 		return -EINVAL;
40*c8a17756SThomas Zimmermann 	}
41*c8a17756SThomas Zimmermann 	return (int)value;
42*c8a17756SThomas Zimmermann }
43*c8a17756SThomas Zimmermann 
44*c8a17756SThomas Zimmermann static int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value)
45*c8a17756SThomas Zimmermann {
46*c8a17756SThomas Zimmermann 	if (!value) {
47*c8a17756SThomas Zimmermann 		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
48*c8a17756SThomas Zimmermann 		return -EINVAL;
49*c8a17756SThomas Zimmermann 	}
50*c8a17756SThomas Zimmermann 	return display_get_validated_int(dev, name, value);
51*c8a17756SThomas Zimmermann }
52*c8a17756SThomas Zimmermann 
53*c8a17756SThomas Zimmermann static const struct drm_format_info *display_get_validated_format(struct drm_device *dev,
54*c8a17756SThomas Zimmermann 								  u32 depth)
55*c8a17756SThomas Zimmermann {
56*c8a17756SThomas Zimmermann 	const struct drm_format_info *info;
57*c8a17756SThomas Zimmermann 	u32 format;
58*c8a17756SThomas Zimmermann 
59*c8a17756SThomas Zimmermann 	switch (depth) {
60*c8a17756SThomas Zimmermann 	case 8:
61*c8a17756SThomas Zimmermann 		format = drm_mode_legacy_fb_format(8, 8);
62*c8a17756SThomas Zimmermann 		break;
63*c8a17756SThomas Zimmermann 	case 15:
64*c8a17756SThomas Zimmermann 	case 16:
65*c8a17756SThomas Zimmermann 		format = drm_mode_legacy_fb_format(16, depth);
66*c8a17756SThomas Zimmermann 		break;
67*c8a17756SThomas Zimmermann 	case 32:
68*c8a17756SThomas Zimmermann 		format = drm_mode_legacy_fb_format(32, 24);
69*c8a17756SThomas Zimmermann 		break;
70*c8a17756SThomas Zimmermann 	default:
71*c8a17756SThomas Zimmermann 		drm_err(dev, "unsupported framebuffer depth %u\n", depth);
72*c8a17756SThomas Zimmermann 		return ERR_PTR(-EINVAL);
73*c8a17756SThomas Zimmermann 	}
74*c8a17756SThomas Zimmermann 
75*c8a17756SThomas Zimmermann 	info = drm_format_info(format);
76*c8a17756SThomas Zimmermann 	if (!info) {
77*c8a17756SThomas Zimmermann 		drm_err(dev, "cannot find framebuffer format for depth %u\n", depth);
78*c8a17756SThomas Zimmermann 		return ERR_PTR(-EINVAL);
79*c8a17756SThomas Zimmermann 	}
80*c8a17756SThomas Zimmermann 
81*c8a17756SThomas Zimmermann 	return info;
82*c8a17756SThomas Zimmermann }
83*c8a17756SThomas Zimmermann 
84*c8a17756SThomas Zimmermann static int display_read_u32_of(struct drm_device *dev, struct device_node *of_node,
85*c8a17756SThomas Zimmermann 			       const char *name, u32 *value)
86*c8a17756SThomas Zimmermann {
87*c8a17756SThomas Zimmermann 	int ret = of_property_read_u32(of_node, name, value);
88*c8a17756SThomas Zimmermann 
89*c8a17756SThomas Zimmermann 	if (ret)
90*c8a17756SThomas Zimmermann 		drm_err(dev, "cannot parse framebuffer %s: error %d\n", name, ret);
91*c8a17756SThomas Zimmermann 	return ret;
92*c8a17756SThomas Zimmermann }
93*c8a17756SThomas Zimmermann 
94*c8a17756SThomas Zimmermann static int display_get_width_of(struct drm_device *dev, struct device_node *of_node)
95*c8a17756SThomas Zimmermann {
96*c8a17756SThomas Zimmermann 	u32 width;
97*c8a17756SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "width", &width);
98*c8a17756SThomas Zimmermann 
99*c8a17756SThomas Zimmermann 	if (ret)
100*c8a17756SThomas Zimmermann 		return ret;
101*c8a17756SThomas Zimmermann 	return display_get_validated_int0(dev, "width", width);
102*c8a17756SThomas Zimmermann }
103*c8a17756SThomas Zimmermann 
104*c8a17756SThomas Zimmermann static int display_get_height_of(struct drm_device *dev, struct device_node *of_node)
105*c8a17756SThomas Zimmermann {
106*c8a17756SThomas Zimmermann 	u32 height;
107*c8a17756SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "height", &height);
108*c8a17756SThomas Zimmermann 
109*c8a17756SThomas Zimmermann 	if (ret)
110*c8a17756SThomas Zimmermann 		return ret;
111*c8a17756SThomas Zimmermann 	return display_get_validated_int0(dev, "height", height);
112*c8a17756SThomas Zimmermann }
113*c8a17756SThomas Zimmermann 
114*c8a17756SThomas Zimmermann static int display_get_depth_of(struct drm_device *dev, struct device_node *of_node)
115*c8a17756SThomas Zimmermann {
116*c8a17756SThomas Zimmermann 	u32 depth;
117*c8a17756SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "depth", &depth);
118*c8a17756SThomas Zimmermann 
119*c8a17756SThomas Zimmermann 	if (ret)
120*c8a17756SThomas Zimmermann 		return ret;
121*c8a17756SThomas Zimmermann 	return display_get_validated_int0(dev, "depth", depth);
122*c8a17756SThomas Zimmermann }
123*c8a17756SThomas Zimmermann 
124*c8a17756SThomas Zimmermann static int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node)
125*c8a17756SThomas Zimmermann {
126*c8a17756SThomas Zimmermann 	u32 linebytes;
127*c8a17756SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "linebytes", &linebytes);
128*c8a17756SThomas Zimmermann 
129*c8a17756SThomas Zimmermann 	if (ret)
130*c8a17756SThomas Zimmermann 		return ret;
131*c8a17756SThomas Zimmermann 	return display_get_validated_int(dev, "linebytes", linebytes);
132*c8a17756SThomas Zimmermann }
133*c8a17756SThomas Zimmermann 
134*c8a17756SThomas Zimmermann static u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node)
135*c8a17756SThomas Zimmermann {
136*c8a17756SThomas Zimmermann 	u32 address;
137*c8a17756SThomas Zimmermann 	int ret;
138*c8a17756SThomas Zimmermann 
139*c8a17756SThomas Zimmermann 	/*
140*c8a17756SThomas Zimmermann 	 * Not all devices provide an address property, it's not
141*c8a17756SThomas Zimmermann 	 * a bug if this fails. The driver will try to find the
142*c8a17756SThomas Zimmermann 	 * framebuffer base address from the device's memory regions.
143*c8a17756SThomas Zimmermann 	 */
144*c8a17756SThomas Zimmermann 	ret = of_property_read_u32(of_node, "address", &address);
145*c8a17756SThomas Zimmermann 	if (ret)
146*c8a17756SThomas Zimmermann 		return OF_BAD_ADDR;
147*c8a17756SThomas Zimmermann 
148*c8a17756SThomas Zimmermann 	return address;
149*c8a17756SThomas Zimmermann }
150*c8a17756SThomas Zimmermann 
151*c8a17756SThomas Zimmermann /*
152*c8a17756SThomas Zimmermann  * Open Firmware display device
153*c8a17756SThomas Zimmermann  */
154*c8a17756SThomas Zimmermann 
155*c8a17756SThomas Zimmermann struct ofdrm_device {
156*c8a17756SThomas Zimmermann 	struct drm_device dev;
157*c8a17756SThomas Zimmermann 	struct platform_device *pdev;
158*c8a17756SThomas Zimmermann 
159*c8a17756SThomas Zimmermann 	/* firmware-buffer settings */
160*c8a17756SThomas Zimmermann 	struct iosys_map screen_base;
161*c8a17756SThomas Zimmermann 	struct drm_display_mode mode;
162*c8a17756SThomas Zimmermann 	const struct drm_format_info *format;
163*c8a17756SThomas Zimmermann 	unsigned int pitch;
164*c8a17756SThomas Zimmermann 
165*c8a17756SThomas Zimmermann 	/* modesetting */
166*c8a17756SThomas Zimmermann 	uint32_t formats[8];
167*c8a17756SThomas Zimmermann 	struct drm_plane primary_plane;
168*c8a17756SThomas Zimmermann 	struct drm_crtc crtc;
169*c8a17756SThomas Zimmermann 	struct drm_encoder encoder;
170*c8a17756SThomas Zimmermann 	struct drm_connector connector;
171*c8a17756SThomas Zimmermann };
172*c8a17756SThomas Zimmermann 
173*c8a17756SThomas Zimmermann static struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev)
174*c8a17756SThomas Zimmermann {
175*c8a17756SThomas Zimmermann 	return container_of(dev, struct ofdrm_device, dev);
176*c8a17756SThomas Zimmermann }
177*c8a17756SThomas Zimmermann 
178*c8a17756SThomas Zimmermann /*
179*c8a17756SThomas Zimmermann  * Hardware
180*c8a17756SThomas Zimmermann  */
181*c8a17756SThomas Zimmermann 
182*c8a17756SThomas Zimmermann #if defined(CONFIG_PCI)
183*c8a17756SThomas Zimmermann static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node)
184*c8a17756SThomas Zimmermann {
185*c8a17756SThomas Zimmermann 	const __be32 *vendor_p, *device_p;
186*c8a17756SThomas Zimmermann 	u32 vendor, device;
187*c8a17756SThomas Zimmermann 	struct pci_dev *pcidev;
188*c8a17756SThomas Zimmermann 
189*c8a17756SThomas Zimmermann 	vendor_p = of_get_property(of_node, "vendor-id", NULL);
190*c8a17756SThomas Zimmermann 	if (!vendor_p)
191*c8a17756SThomas Zimmermann 		return ERR_PTR(-ENODEV);
192*c8a17756SThomas Zimmermann 	vendor = be32_to_cpup(vendor_p);
193*c8a17756SThomas Zimmermann 
194*c8a17756SThomas Zimmermann 	device_p = of_get_property(of_node, "device-id", NULL);
195*c8a17756SThomas Zimmermann 	if (!device_p)
196*c8a17756SThomas Zimmermann 		return ERR_PTR(-ENODEV);
197*c8a17756SThomas Zimmermann 	device = be32_to_cpup(device_p);
198*c8a17756SThomas Zimmermann 
199*c8a17756SThomas Zimmermann 	pcidev = pci_get_device(vendor, device, NULL);
200*c8a17756SThomas Zimmermann 	if (!pcidev)
201*c8a17756SThomas Zimmermann 		return ERR_PTR(-ENODEV);
202*c8a17756SThomas Zimmermann 
203*c8a17756SThomas Zimmermann 	return pcidev;
204*c8a17756SThomas Zimmermann }
205*c8a17756SThomas Zimmermann 
206*c8a17756SThomas Zimmermann static void ofdrm_pci_release(void *data)
207*c8a17756SThomas Zimmermann {
208*c8a17756SThomas Zimmermann 	struct pci_dev *pcidev = data;
209*c8a17756SThomas Zimmermann 
210*c8a17756SThomas Zimmermann 	pci_disable_device(pcidev);
211*c8a17756SThomas Zimmermann }
212*c8a17756SThomas Zimmermann 
213*c8a17756SThomas Zimmermann static int ofdrm_device_init_pci(struct ofdrm_device *odev)
214*c8a17756SThomas Zimmermann {
215*c8a17756SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
216*c8a17756SThomas Zimmermann 	struct platform_device *pdev = to_platform_device(dev->dev);
217*c8a17756SThomas Zimmermann 	struct device_node *of_node = pdev->dev.of_node;
218*c8a17756SThomas Zimmermann 	struct pci_dev *pcidev;
219*c8a17756SThomas Zimmermann 	int ret;
220*c8a17756SThomas Zimmermann 
221*c8a17756SThomas Zimmermann 	/*
222*c8a17756SThomas Zimmermann 	 * Never use pcim_ or other managed helpers on the returned PCI
223*c8a17756SThomas Zimmermann 	 * device. Otherwise, probing the native driver will fail for
224*c8a17756SThomas Zimmermann 	 * resource conflicts. PCI-device management has to be tied to
225*c8a17756SThomas Zimmermann 	 * the lifetime of the platform device until the native driver
226*c8a17756SThomas Zimmermann 	 * takes over.
227*c8a17756SThomas Zimmermann 	 */
228*c8a17756SThomas Zimmermann 	pcidev = display_get_pci_dev_of(dev, of_node);
229*c8a17756SThomas Zimmermann 	if (IS_ERR(pcidev))
230*c8a17756SThomas Zimmermann 		return 0; /* no PCI device found; ignore the error */
231*c8a17756SThomas Zimmermann 
232*c8a17756SThomas Zimmermann 	ret = pci_enable_device(pcidev);
233*c8a17756SThomas Zimmermann 	if (ret) {
234*c8a17756SThomas Zimmermann 		drm_err(dev, "pci_enable_device(%s) failed: %d\n",
235*c8a17756SThomas Zimmermann 			dev_name(&pcidev->dev), ret);
236*c8a17756SThomas Zimmermann 		return ret;
237*c8a17756SThomas Zimmermann 	}
238*c8a17756SThomas Zimmermann 	ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev);
239*c8a17756SThomas Zimmermann 	if (ret)
240*c8a17756SThomas Zimmermann 		return ret;
241*c8a17756SThomas Zimmermann 
242*c8a17756SThomas Zimmermann 	return 0;
243*c8a17756SThomas Zimmermann }
244*c8a17756SThomas Zimmermann #else
245*c8a17756SThomas Zimmermann static int ofdrm_device_init_pci(struct ofdrm_device *odev)
246*c8a17756SThomas Zimmermann {
247*c8a17756SThomas Zimmermann 	return 0;
248*c8a17756SThomas Zimmermann }
249*c8a17756SThomas Zimmermann #endif
250*c8a17756SThomas Zimmermann 
251*c8a17756SThomas Zimmermann /*
252*c8a17756SThomas Zimmermann  *  OF display settings
253*c8a17756SThomas Zimmermann  */
254*c8a17756SThomas Zimmermann 
255*c8a17756SThomas Zimmermann static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev,
256*c8a17756SThomas Zimmermann 					       struct resource *fb_res)
257*c8a17756SThomas Zimmermann {
258*c8a17756SThomas Zimmermann 	struct platform_device *pdev = to_platform_device(odev->dev.dev);
259*c8a17756SThomas Zimmermann 	struct resource *res, *max_res = NULL;
260*c8a17756SThomas Zimmermann 	u32 i;
261*c8a17756SThomas Zimmermann 
262*c8a17756SThomas Zimmermann 	for (i = 0; pdev->num_resources; ++i) {
263*c8a17756SThomas Zimmermann 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
264*c8a17756SThomas Zimmermann 		if (!res)
265*c8a17756SThomas Zimmermann 			break; /* all resources processed */
266*c8a17756SThomas Zimmermann 		if (resource_size(res) < resource_size(fb_res))
267*c8a17756SThomas Zimmermann 			continue; /* resource too small */
268*c8a17756SThomas Zimmermann 		if (fb_res->start && resource_contains(res, fb_res))
269*c8a17756SThomas Zimmermann 			return res; /* resource contains framebuffer */
270*c8a17756SThomas Zimmermann 		if (!max_res || resource_size(res) > resource_size(max_res))
271*c8a17756SThomas Zimmermann 			max_res = res; /* store largest resource as fallback */
272*c8a17756SThomas Zimmermann 	}
273*c8a17756SThomas Zimmermann 
274*c8a17756SThomas Zimmermann 	return max_res;
275*c8a17756SThomas Zimmermann }
276*c8a17756SThomas Zimmermann 
277*c8a17756SThomas Zimmermann /*
278*c8a17756SThomas Zimmermann  * Modesetting
279*c8a17756SThomas Zimmermann  */
280*c8a17756SThomas Zimmermann 
281*c8a17756SThomas Zimmermann /*
282*c8a17756SThomas Zimmermann  * Support all formats of OF display and maybe more; in order
283*c8a17756SThomas Zimmermann  * of preference. The display's update function will do any
284*c8a17756SThomas Zimmermann  * conversion necessary.
285*c8a17756SThomas Zimmermann  *
286*c8a17756SThomas Zimmermann  * TODO: Add blit helpers for remaining formats and uncomment
287*c8a17756SThomas Zimmermann  *       constants.
288*c8a17756SThomas Zimmermann  */
289*c8a17756SThomas Zimmermann static const uint32_t ofdrm_primary_plane_formats[] = {
290*c8a17756SThomas Zimmermann 	DRM_FORMAT_XRGB8888,
291*c8a17756SThomas Zimmermann 	DRM_FORMAT_RGB565,
292*c8a17756SThomas Zimmermann 	//DRM_FORMAT_XRGB1555,
293*c8a17756SThomas Zimmermann 	//DRM_FORMAT_C8,
294*c8a17756SThomas Zimmermann };
295*c8a17756SThomas Zimmermann 
296*c8a17756SThomas Zimmermann static const uint64_t ofdrm_primary_plane_format_modifiers[] = {
297*c8a17756SThomas Zimmermann 	DRM_FORMAT_MOD_LINEAR,
298*c8a17756SThomas Zimmermann 	DRM_FORMAT_MOD_INVALID
299*c8a17756SThomas Zimmermann };
300*c8a17756SThomas Zimmermann 
301*c8a17756SThomas Zimmermann static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
302*c8a17756SThomas Zimmermann 						   struct drm_atomic_state *new_state)
303*c8a17756SThomas Zimmermann {
304*c8a17756SThomas Zimmermann 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
305*c8a17756SThomas Zimmermann 	struct drm_crtc *new_crtc = new_plane_state->crtc;
306*c8a17756SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = NULL;
307*c8a17756SThomas Zimmermann 
308*c8a17756SThomas Zimmermann 	if (new_crtc)
309*c8a17756SThomas Zimmermann 		new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
310*c8a17756SThomas Zimmermann 
311*c8a17756SThomas Zimmermann 	return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
312*c8a17756SThomas Zimmermann 						   DRM_PLANE_NO_SCALING,
313*c8a17756SThomas Zimmermann 						   DRM_PLANE_NO_SCALING,
314*c8a17756SThomas Zimmermann 						   false, false);
315*c8a17756SThomas Zimmermann }
316*c8a17756SThomas Zimmermann 
317*c8a17756SThomas Zimmermann static void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
318*c8a17756SThomas Zimmermann 						     struct drm_atomic_state *state)
319*c8a17756SThomas Zimmermann {
320*c8a17756SThomas Zimmermann 	struct drm_device *dev = plane->dev;
321*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(dev);
322*c8a17756SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
323*c8a17756SThomas Zimmermann 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
324*c8a17756SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
325*c8a17756SThomas Zimmermann 	struct drm_framebuffer *fb = plane_state->fb;
326*c8a17756SThomas Zimmermann 	unsigned int dst_pitch = odev->pitch;
327*c8a17756SThomas Zimmermann 	const struct drm_format_info *dst_format = odev->format;
328*c8a17756SThomas Zimmermann 	struct drm_atomic_helper_damage_iter iter;
329*c8a17756SThomas Zimmermann 	struct drm_rect damage;
330*c8a17756SThomas Zimmermann 	int ret, idx;
331*c8a17756SThomas Zimmermann 
332*c8a17756SThomas Zimmermann 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
333*c8a17756SThomas Zimmermann 	if (ret)
334*c8a17756SThomas Zimmermann 		return;
335*c8a17756SThomas Zimmermann 
336*c8a17756SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
337*c8a17756SThomas Zimmermann 		goto out_drm_gem_fb_end_cpu_access;
338*c8a17756SThomas Zimmermann 
339*c8a17756SThomas Zimmermann 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
340*c8a17756SThomas Zimmermann 	drm_atomic_for_each_plane_damage(&iter, &damage) {
341*c8a17756SThomas Zimmermann 		struct iosys_map dst = odev->screen_base;
342*c8a17756SThomas Zimmermann 		struct drm_rect dst_clip = plane_state->dst;
343*c8a17756SThomas Zimmermann 
344*c8a17756SThomas Zimmermann 		if (!drm_rect_intersect(&dst_clip, &damage))
345*c8a17756SThomas Zimmermann 			continue;
346*c8a17756SThomas Zimmermann 
347*c8a17756SThomas Zimmermann 		iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
348*c8a17756SThomas Zimmermann 		drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
349*c8a17756SThomas Zimmermann 			    &damage);
350*c8a17756SThomas Zimmermann 	}
351*c8a17756SThomas Zimmermann 
352*c8a17756SThomas Zimmermann 	drm_dev_exit(idx);
353*c8a17756SThomas Zimmermann out_drm_gem_fb_end_cpu_access:
354*c8a17756SThomas Zimmermann 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
355*c8a17756SThomas Zimmermann }
356*c8a17756SThomas Zimmermann 
357*c8a17756SThomas Zimmermann static void ofdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane,
358*c8a17756SThomas Zimmermann 						      struct drm_atomic_state *state)
359*c8a17756SThomas Zimmermann {
360*c8a17756SThomas Zimmermann 	struct drm_device *dev = plane->dev;
361*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(dev);
362*c8a17756SThomas Zimmermann 	struct iosys_map dst = odev->screen_base;
363*c8a17756SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
364*c8a17756SThomas Zimmermann 	void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
365*c8a17756SThomas Zimmermann 	unsigned int dst_pitch = odev->pitch;
366*c8a17756SThomas Zimmermann 	const struct drm_format_info *dst_format = odev->format;
367*c8a17756SThomas Zimmermann 	struct drm_rect dst_clip;
368*c8a17756SThomas Zimmermann 	unsigned long lines, linepixels, i;
369*c8a17756SThomas Zimmermann 	int idx;
370*c8a17756SThomas Zimmermann 
371*c8a17756SThomas Zimmermann 	drm_rect_init(&dst_clip,
372*c8a17756SThomas Zimmermann 		      plane_state->src_x >> 16, plane_state->src_y >> 16,
373*c8a17756SThomas Zimmermann 		      plane_state->src_w >> 16, plane_state->src_h >> 16);
374*c8a17756SThomas Zimmermann 
375*c8a17756SThomas Zimmermann 	lines = drm_rect_height(&dst_clip);
376*c8a17756SThomas Zimmermann 	linepixels = drm_rect_width(&dst_clip);
377*c8a17756SThomas Zimmermann 
378*c8a17756SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
379*c8a17756SThomas Zimmermann 		return;
380*c8a17756SThomas Zimmermann 
381*c8a17756SThomas Zimmermann 	/* Clear buffer to black if disabled */
382*c8a17756SThomas Zimmermann 	dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
383*c8a17756SThomas Zimmermann 	for (i = 0; i < lines; ++i) {
384*c8a17756SThomas Zimmermann 		memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
385*c8a17756SThomas Zimmermann 		dst_vmap += dst_pitch;
386*c8a17756SThomas Zimmermann 	}
387*c8a17756SThomas Zimmermann 
388*c8a17756SThomas Zimmermann 	drm_dev_exit(idx);
389*c8a17756SThomas Zimmermann }
390*c8a17756SThomas Zimmermann 
391*c8a17756SThomas Zimmermann static const struct drm_plane_helper_funcs ofdrm_primary_plane_helper_funcs = {
392*c8a17756SThomas Zimmermann 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
393*c8a17756SThomas Zimmermann 	.atomic_check = ofdrm_primary_plane_helper_atomic_check,
394*c8a17756SThomas Zimmermann 	.atomic_update = ofdrm_primary_plane_helper_atomic_update,
395*c8a17756SThomas Zimmermann 	.atomic_disable = ofdrm_primary_plane_helper_atomic_disable,
396*c8a17756SThomas Zimmermann };
397*c8a17756SThomas Zimmermann 
398*c8a17756SThomas Zimmermann static const struct drm_plane_funcs ofdrm_primary_plane_funcs = {
399*c8a17756SThomas Zimmermann 	.update_plane = drm_atomic_helper_update_plane,
400*c8a17756SThomas Zimmermann 	.disable_plane = drm_atomic_helper_disable_plane,
401*c8a17756SThomas Zimmermann 	.destroy = drm_plane_cleanup,
402*c8a17756SThomas Zimmermann 	DRM_GEM_SHADOW_PLANE_FUNCS,
403*c8a17756SThomas Zimmermann };
404*c8a17756SThomas Zimmermann 
405*c8a17756SThomas Zimmermann static enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc,
406*c8a17756SThomas Zimmermann 							 const struct drm_display_mode *mode)
407*c8a17756SThomas Zimmermann {
408*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev);
409*c8a17756SThomas Zimmermann 
410*c8a17756SThomas Zimmermann 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &odev->mode);
411*c8a17756SThomas Zimmermann }
412*c8a17756SThomas Zimmermann 
413*c8a17756SThomas Zimmermann static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc,
414*c8a17756SThomas Zimmermann 					  struct drm_atomic_state *new_state)
415*c8a17756SThomas Zimmermann {
416*c8a17756SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
417*c8a17756SThomas Zimmermann 
418*c8a17756SThomas Zimmermann 	if (!new_crtc_state->enable)
419*c8a17756SThomas Zimmermann 		return 0;
420*c8a17756SThomas Zimmermann 
421*c8a17756SThomas Zimmermann 	return drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
422*c8a17756SThomas Zimmermann }
423*c8a17756SThomas Zimmermann 
424*c8a17756SThomas Zimmermann /*
425*c8a17756SThomas Zimmermann  * The CRTC is always enabled. Screen updates are performed by
426*c8a17756SThomas Zimmermann  * the primary plane's atomic_update function. Disabling clears
427*c8a17756SThomas Zimmermann  * the screen in the primary plane's atomic_disable function.
428*c8a17756SThomas Zimmermann  */
429*c8a17756SThomas Zimmermann static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = {
430*c8a17756SThomas Zimmermann 	.mode_valid = ofdrm_crtc_helper_mode_valid,
431*c8a17756SThomas Zimmermann 	.atomic_check = ofdrm_crtc_helper_atomic_check,
432*c8a17756SThomas Zimmermann };
433*c8a17756SThomas Zimmermann 
434*c8a17756SThomas Zimmermann static const struct drm_crtc_funcs ofdrm_crtc_funcs = {
435*c8a17756SThomas Zimmermann 	.reset = drm_atomic_helper_crtc_reset,
436*c8a17756SThomas Zimmermann 	.destroy = drm_crtc_cleanup,
437*c8a17756SThomas Zimmermann 	.set_config = drm_atomic_helper_set_config,
438*c8a17756SThomas Zimmermann 	.page_flip = drm_atomic_helper_page_flip,
439*c8a17756SThomas Zimmermann 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
440*c8a17756SThomas Zimmermann 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
441*c8a17756SThomas Zimmermann };
442*c8a17756SThomas Zimmermann 
443*c8a17756SThomas Zimmermann static int ofdrm_connector_helper_get_modes(struct drm_connector *connector)
444*c8a17756SThomas Zimmermann {
445*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(connector->dev);
446*c8a17756SThomas Zimmermann 
447*c8a17756SThomas Zimmermann 	return drm_connector_helper_get_modes_fixed(connector, &odev->mode);
448*c8a17756SThomas Zimmermann }
449*c8a17756SThomas Zimmermann 
450*c8a17756SThomas Zimmermann static const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = {
451*c8a17756SThomas Zimmermann 	.get_modes = ofdrm_connector_helper_get_modes,
452*c8a17756SThomas Zimmermann };
453*c8a17756SThomas Zimmermann 
454*c8a17756SThomas Zimmermann static const struct drm_connector_funcs ofdrm_connector_funcs = {
455*c8a17756SThomas Zimmermann 	.reset = drm_atomic_helper_connector_reset,
456*c8a17756SThomas Zimmermann 	.fill_modes = drm_helper_probe_single_connector_modes,
457*c8a17756SThomas Zimmermann 	.destroy = drm_connector_cleanup,
458*c8a17756SThomas Zimmermann 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
459*c8a17756SThomas Zimmermann 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
460*c8a17756SThomas Zimmermann };
461*c8a17756SThomas Zimmermann 
462*c8a17756SThomas Zimmermann static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = {
463*c8a17756SThomas Zimmermann 	.fb_create = drm_gem_fb_create_with_dirty,
464*c8a17756SThomas Zimmermann 	.atomic_check = drm_atomic_helper_check,
465*c8a17756SThomas Zimmermann 	.atomic_commit = drm_atomic_helper_commit,
466*c8a17756SThomas Zimmermann };
467*c8a17756SThomas Zimmermann 
468*c8a17756SThomas Zimmermann /*
469*c8a17756SThomas Zimmermann  * Init / Cleanup
470*c8a17756SThomas Zimmermann  */
471*c8a17756SThomas Zimmermann 
472*c8a17756SThomas Zimmermann static struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height)
473*c8a17756SThomas Zimmermann {
474*c8a17756SThomas Zimmermann 	/*
475*c8a17756SThomas Zimmermann 	 * Assume a monitor resolution of 96 dpi to
476*c8a17756SThomas Zimmermann 	 * get a somewhat reasonable screen size.
477*c8a17756SThomas Zimmermann 	 */
478*c8a17756SThomas Zimmermann 	const struct drm_display_mode mode = {
479*c8a17756SThomas Zimmermann 		DRM_MODE_INIT(60, width, height,
480*c8a17756SThomas Zimmermann 			      DRM_MODE_RES_MM(width, 96ul),
481*c8a17756SThomas Zimmermann 			      DRM_MODE_RES_MM(height, 96ul))
482*c8a17756SThomas Zimmermann 	};
483*c8a17756SThomas Zimmermann 
484*c8a17756SThomas Zimmermann 	return mode;
485*c8a17756SThomas Zimmermann }
486*c8a17756SThomas Zimmermann 
487*c8a17756SThomas Zimmermann static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
488*c8a17756SThomas Zimmermann 						struct platform_device *pdev)
489*c8a17756SThomas Zimmermann {
490*c8a17756SThomas Zimmermann 	struct device_node *of_node = pdev->dev.of_node;
491*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev;
492*c8a17756SThomas Zimmermann 	struct drm_device *dev;
493*c8a17756SThomas Zimmermann 	int width, height, depth, linebytes;
494*c8a17756SThomas Zimmermann 	const struct drm_format_info *format;
495*c8a17756SThomas Zimmermann 	u64 address;
496*c8a17756SThomas Zimmermann 	resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize;
497*c8a17756SThomas Zimmermann 	struct resource *res, *mem;
498*c8a17756SThomas Zimmermann 	void __iomem *screen_base;
499*c8a17756SThomas Zimmermann 	struct drm_plane *primary_plane;
500*c8a17756SThomas Zimmermann 	struct drm_crtc *crtc;
501*c8a17756SThomas Zimmermann 	struct drm_encoder *encoder;
502*c8a17756SThomas Zimmermann 	struct drm_connector *connector;
503*c8a17756SThomas Zimmermann 	unsigned long max_width, max_height;
504*c8a17756SThomas Zimmermann 	size_t nformats;
505*c8a17756SThomas Zimmermann 	int ret;
506*c8a17756SThomas Zimmermann 
507*c8a17756SThomas Zimmermann 	odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev);
508*c8a17756SThomas Zimmermann 	if (IS_ERR(odev))
509*c8a17756SThomas Zimmermann 		return ERR_CAST(odev);
510*c8a17756SThomas Zimmermann 	dev = &odev->dev;
511*c8a17756SThomas Zimmermann 	platform_set_drvdata(pdev, dev);
512*c8a17756SThomas Zimmermann 
513*c8a17756SThomas Zimmermann 	ret = ofdrm_device_init_pci(odev);
514*c8a17756SThomas Zimmermann 	if (ret)
515*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
516*c8a17756SThomas Zimmermann 
517*c8a17756SThomas Zimmermann 	/*
518*c8a17756SThomas Zimmermann 	 * OF display-node settings
519*c8a17756SThomas Zimmermann 	 */
520*c8a17756SThomas Zimmermann 
521*c8a17756SThomas Zimmermann 	width = display_get_width_of(dev, of_node);
522*c8a17756SThomas Zimmermann 	if (width < 0)
523*c8a17756SThomas Zimmermann 		return ERR_PTR(width);
524*c8a17756SThomas Zimmermann 	height = display_get_height_of(dev, of_node);
525*c8a17756SThomas Zimmermann 	if (height < 0)
526*c8a17756SThomas Zimmermann 		return ERR_PTR(height);
527*c8a17756SThomas Zimmermann 	depth = display_get_depth_of(dev, of_node);
528*c8a17756SThomas Zimmermann 	if (depth < 0)
529*c8a17756SThomas Zimmermann 		return ERR_PTR(depth);
530*c8a17756SThomas Zimmermann 	linebytes = display_get_linebytes_of(dev, of_node);
531*c8a17756SThomas Zimmermann 	if (linebytes < 0)
532*c8a17756SThomas Zimmermann 		return ERR_PTR(linebytes);
533*c8a17756SThomas Zimmermann 
534*c8a17756SThomas Zimmermann 	format = display_get_validated_format(dev, depth);
535*c8a17756SThomas Zimmermann 	if (IS_ERR(format))
536*c8a17756SThomas Zimmermann 		return ERR_CAST(format);
537*c8a17756SThomas Zimmermann 	if (!linebytes) {
538*c8a17756SThomas Zimmermann 		linebytes = drm_format_info_min_pitch(format, 0, width);
539*c8a17756SThomas Zimmermann 		if (drm_WARN_ON(dev, !linebytes))
540*c8a17756SThomas Zimmermann 			return ERR_PTR(-EINVAL);
541*c8a17756SThomas Zimmermann 	}
542*c8a17756SThomas Zimmermann 
543*c8a17756SThomas Zimmermann 	fb_size = linebytes * height;
544*c8a17756SThomas Zimmermann 
545*c8a17756SThomas Zimmermann 	/*
546*c8a17756SThomas Zimmermann 	 * Try to figure out the address of the framebuffer. Unfortunately, Open
547*c8a17756SThomas Zimmermann 	 * Firmware doesn't provide a standard way to do so. All we can do is a
548*c8a17756SThomas Zimmermann 	 * dodgy heuristic that happens to work in practice.
549*c8a17756SThomas Zimmermann 	 *
550*c8a17756SThomas Zimmermann 	 * On most machines, the "address" property contains what we need, though
551*c8a17756SThomas Zimmermann 	 * not on Matrox cards found in IBM machines. What appears to give good
552*c8a17756SThomas Zimmermann 	 * results is to go through the PCI ranges and pick one that encloses the
553*c8a17756SThomas Zimmermann 	 * "address" property. If none match, we pick the largest.
554*c8a17756SThomas Zimmermann 	 */
555*c8a17756SThomas Zimmermann 	address = display_get_address_of(dev, of_node);
556*c8a17756SThomas Zimmermann 	if (address != OF_BAD_ADDR) {
557*c8a17756SThomas Zimmermann 		struct resource fb_res = DEFINE_RES_MEM(address, fb_size);
558*c8a17756SThomas Zimmermann 
559*c8a17756SThomas Zimmermann 		res = ofdrm_find_fb_resource(odev, &fb_res);
560*c8a17756SThomas Zimmermann 		if (!res)
561*c8a17756SThomas Zimmermann 			return ERR_PTR(-EINVAL);
562*c8a17756SThomas Zimmermann 		if (resource_contains(res, &fb_res))
563*c8a17756SThomas Zimmermann 			fb_base = address;
564*c8a17756SThomas Zimmermann 		else
565*c8a17756SThomas Zimmermann 			fb_base = res->start;
566*c8a17756SThomas Zimmermann 	} else {
567*c8a17756SThomas Zimmermann 		struct resource fb_res = DEFINE_RES_MEM(0u, fb_size);
568*c8a17756SThomas Zimmermann 
569*c8a17756SThomas Zimmermann 		res = ofdrm_find_fb_resource(odev, &fb_res);
570*c8a17756SThomas Zimmermann 		if (!res)
571*c8a17756SThomas Zimmermann 			return ERR_PTR(-EINVAL);
572*c8a17756SThomas Zimmermann 		fb_base = res->start;
573*c8a17756SThomas Zimmermann 	}
574*c8a17756SThomas Zimmermann 
575*c8a17756SThomas Zimmermann 	/*
576*c8a17756SThomas Zimmermann 	 * I/O resources
577*c8a17756SThomas Zimmermann 	 */
578*c8a17756SThomas Zimmermann 
579*c8a17756SThomas Zimmermann 	fb_pgbase = round_down(fb_base, PAGE_SIZE);
580*c8a17756SThomas Zimmermann 	fb_pgsize = fb_base - fb_pgbase + round_up(fb_size, PAGE_SIZE);
581*c8a17756SThomas Zimmermann 
582*c8a17756SThomas Zimmermann 	ret = devm_aperture_acquire_from_firmware(dev, fb_pgbase, fb_pgsize);
583*c8a17756SThomas Zimmermann 	if (ret) {
584*c8a17756SThomas Zimmermann 		drm_err(dev, "could not acquire memory range %pr: error %d\n", &res, ret);
585*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
586*c8a17756SThomas Zimmermann 	}
587*c8a17756SThomas Zimmermann 
588*c8a17756SThomas Zimmermann 	mem = devm_request_mem_region(&pdev->dev, fb_pgbase, fb_pgsize, drv->name);
589*c8a17756SThomas Zimmermann 	if (!mem) {
590*c8a17756SThomas Zimmermann 		drm_warn(dev, "could not acquire memory region %pr\n", &res);
591*c8a17756SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
592*c8a17756SThomas Zimmermann 	}
593*c8a17756SThomas Zimmermann 
594*c8a17756SThomas Zimmermann 	screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
595*c8a17756SThomas Zimmermann 	if (!screen_base)
596*c8a17756SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
597*c8a17756SThomas Zimmermann 
598*c8a17756SThomas Zimmermann 	/*
599*c8a17756SThomas Zimmermann 	 * Firmware framebuffer
600*c8a17756SThomas Zimmermann 	 */
601*c8a17756SThomas Zimmermann 
602*c8a17756SThomas Zimmermann 	iosys_map_set_vaddr_iomem(&odev->screen_base, screen_base);
603*c8a17756SThomas Zimmermann 	odev->mode = ofdrm_mode(width, height);
604*c8a17756SThomas Zimmermann 	odev->format = format;
605*c8a17756SThomas Zimmermann 	odev->pitch = linebytes;
606*c8a17756SThomas Zimmermann 
607*c8a17756SThomas Zimmermann 	drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&odev->mode));
608*c8a17756SThomas Zimmermann 	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n",
609*c8a17756SThomas Zimmermann 		&format->format, width, height, linebytes);
610*c8a17756SThomas Zimmermann 
611*c8a17756SThomas Zimmermann 	/*
612*c8a17756SThomas Zimmermann 	 * Mode-setting pipeline
613*c8a17756SThomas Zimmermann 	 */
614*c8a17756SThomas Zimmermann 
615*c8a17756SThomas Zimmermann 	ret = drmm_mode_config_init(dev);
616*c8a17756SThomas Zimmermann 	if (ret)
617*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
618*c8a17756SThomas Zimmermann 
619*c8a17756SThomas Zimmermann 	max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
620*c8a17756SThomas Zimmermann 	max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
621*c8a17756SThomas Zimmermann 
622*c8a17756SThomas Zimmermann 	dev->mode_config.min_width = width;
623*c8a17756SThomas Zimmermann 	dev->mode_config.max_width = max_width;
624*c8a17756SThomas Zimmermann 	dev->mode_config.min_height = height;
625*c8a17756SThomas Zimmermann 	dev->mode_config.max_height = max_height;
626*c8a17756SThomas Zimmermann 	dev->mode_config.funcs = &ofdrm_mode_config_funcs;
627*c8a17756SThomas Zimmermann 	switch (depth) {
628*c8a17756SThomas Zimmermann 	case 32:
629*c8a17756SThomas Zimmermann 		dev->mode_config.preferred_depth = 24;
630*c8a17756SThomas Zimmermann 		break;
631*c8a17756SThomas Zimmermann 	default:
632*c8a17756SThomas Zimmermann 		dev->mode_config.preferred_depth = depth;
633*c8a17756SThomas Zimmermann 		break;
634*c8a17756SThomas Zimmermann 	}
635*c8a17756SThomas Zimmermann 
636*c8a17756SThomas Zimmermann 	/* Primary plane */
637*c8a17756SThomas Zimmermann 
638*c8a17756SThomas Zimmermann 	nformats = drm_fb_build_fourcc_list(dev, &format->format, 1,
639*c8a17756SThomas Zimmermann 					    ofdrm_primary_plane_formats,
640*c8a17756SThomas Zimmermann 					    ARRAY_SIZE(ofdrm_primary_plane_formats),
641*c8a17756SThomas Zimmermann 					    odev->formats, ARRAY_SIZE(odev->formats));
642*c8a17756SThomas Zimmermann 
643*c8a17756SThomas Zimmermann 	primary_plane = &odev->primary_plane;
644*c8a17756SThomas Zimmermann 	ret = drm_universal_plane_init(dev, primary_plane, 0, &ofdrm_primary_plane_funcs,
645*c8a17756SThomas Zimmermann 				       odev->formats, nformats,
646*c8a17756SThomas Zimmermann 				       ofdrm_primary_plane_format_modifiers,
647*c8a17756SThomas Zimmermann 				       DRM_PLANE_TYPE_PRIMARY, NULL);
648*c8a17756SThomas Zimmermann 	if (ret)
649*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
650*c8a17756SThomas Zimmermann 	drm_plane_helper_add(primary_plane, &ofdrm_primary_plane_helper_funcs);
651*c8a17756SThomas Zimmermann 	drm_plane_enable_fb_damage_clips(primary_plane);
652*c8a17756SThomas Zimmermann 
653*c8a17756SThomas Zimmermann 	/* CRTC */
654*c8a17756SThomas Zimmermann 
655*c8a17756SThomas Zimmermann 	crtc = &odev->crtc;
656*c8a17756SThomas Zimmermann 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
657*c8a17756SThomas Zimmermann 					&ofdrm_crtc_funcs, NULL);
658*c8a17756SThomas Zimmermann 	if (ret)
659*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
660*c8a17756SThomas Zimmermann 	drm_crtc_helper_add(crtc, &ofdrm_crtc_helper_funcs);
661*c8a17756SThomas Zimmermann 
662*c8a17756SThomas Zimmermann 	/* Encoder */
663*c8a17756SThomas Zimmermann 
664*c8a17756SThomas Zimmermann 	encoder = &odev->encoder;
665*c8a17756SThomas Zimmermann 	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
666*c8a17756SThomas Zimmermann 	if (ret)
667*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
668*c8a17756SThomas Zimmermann 	encoder->possible_crtcs = drm_crtc_mask(crtc);
669*c8a17756SThomas Zimmermann 
670*c8a17756SThomas Zimmermann 	/* Connector */
671*c8a17756SThomas Zimmermann 
672*c8a17756SThomas Zimmermann 	connector = &odev->connector;
673*c8a17756SThomas Zimmermann 	ret = drm_connector_init(dev, connector, &ofdrm_connector_funcs,
674*c8a17756SThomas Zimmermann 				 DRM_MODE_CONNECTOR_Unknown);
675*c8a17756SThomas Zimmermann 	if (ret)
676*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
677*c8a17756SThomas Zimmermann 	drm_connector_helper_add(connector, &ofdrm_connector_helper_funcs);
678*c8a17756SThomas Zimmermann 	drm_connector_set_panel_orientation_with_quirk(connector,
679*c8a17756SThomas Zimmermann 						       DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
680*c8a17756SThomas Zimmermann 						       width, height);
681*c8a17756SThomas Zimmermann 
682*c8a17756SThomas Zimmermann 	ret = drm_connector_attach_encoder(connector, encoder);
683*c8a17756SThomas Zimmermann 	if (ret)
684*c8a17756SThomas Zimmermann 		return ERR_PTR(ret);
685*c8a17756SThomas Zimmermann 
686*c8a17756SThomas Zimmermann 	drm_mode_config_reset(dev);
687*c8a17756SThomas Zimmermann 
688*c8a17756SThomas Zimmermann 	return odev;
689*c8a17756SThomas Zimmermann }
690*c8a17756SThomas Zimmermann 
691*c8a17756SThomas Zimmermann /*
692*c8a17756SThomas Zimmermann  * DRM driver
693*c8a17756SThomas Zimmermann  */
694*c8a17756SThomas Zimmermann 
695*c8a17756SThomas Zimmermann DEFINE_DRM_GEM_FOPS(ofdrm_fops);
696*c8a17756SThomas Zimmermann 
697*c8a17756SThomas Zimmermann static struct drm_driver ofdrm_driver = {
698*c8a17756SThomas Zimmermann 	DRM_GEM_SHMEM_DRIVER_OPS,
699*c8a17756SThomas Zimmermann 	.name			= DRIVER_NAME,
700*c8a17756SThomas Zimmermann 	.desc			= DRIVER_DESC,
701*c8a17756SThomas Zimmermann 	.date			= DRIVER_DATE,
702*c8a17756SThomas Zimmermann 	.major			= DRIVER_MAJOR,
703*c8a17756SThomas Zimmermann 	.minor			= DRIVER_MINOR,
704*c8a17756SThomas Zimmermann 	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
705*c8a17756SThomas Zimmermann 	.fops			= &ofdrm_fops,
706*c8a17756SThomas Zimmermann };
707*c8a17756SThomas Zimmermann 
708*c8a17756SThomas Zimmermann /*
709*c8a17756SThomas Zimmermann  * Platform driver
710*c8a17756SThomas Zimmermann  */
711*c8a17756SThomas Zimmermann 
712*c8a17756SThomas Zimmermann static int ofdrm_probe(struct platform_device *pdev)
713*c8a17756SThomas Zimmermann {
714*c8a17756SThomas Zimmermann 	struct ofdrm_device *odev;
715*c8a17756SThomas Zimmermann 	struct drm_device *dev;
716*c8a17756SThomas Zimmermann 	int ret;
717*c8a17756SThomas Zimmermann 
718*c8a17756SThomas Zimmermann 	odev = ofdrm_device_create(&ofdrm_driver, pdev);
719*c8a17756SThomas Zimmermann 	if (IS_ERR(odev))
720*c8a17756SThomas Zimmermann 		return PTR_ERR(odev);
721*c8a17756SThomas Zimmermann 	dev = &odev->dev;
722*c8a17756SThomas Zimmermann 
723*c8a17756SThomas Zimmermann 	ret = drm_dev_register(dev, 0);
724*c8a17756SThomas Zimmermann 	if (ret)
725*c8a17756SThomas Zimmermann 		return ret;
726*c8a17756SThomas Zimmermann 
727*c8a17756SThomas Zimmermann 	/*
728*c8a17756SThomas Zimmermann 	 * FIXME: 24-bit color depth does not work reliably with a 32-bpp
729*c8a17756SThomas Zimmermann 	 * value. Force the bpp value of the scanout buffer's format.
730*c8a17756SThomas Zimmermann 	 */
731*c8a17756SThomas Zimmermann 	drm_fbdev_generic_setup(dev, drm_format_info_bpp(odev->format, 0));
732*c8a17756SThomas Zimmermann 
733*c8a17756SThomas Zimmermann 	return 0;
734*c8a17756SThomas Zimmermann }
735*c8a17756SThomas Zimmermann 
736*c8a17756SThomas Zimmermann static int ofdrm_remove(struct platform_device *pdev)
737*c8a17756SThomas Zimmermann {
738*c8a17756SThomas Zimmermann 	struct drm_device *dev = platform_get_drvdata(pdev);
739*c8a17756SThomas Zimmermann 
740*c8a17756SThomas Zimmermann 	drm_dev_unplug(dev);
741*c8a17756SThomas Zimmermann 
742*c8a17756SThomas Zimmermann 	return 0;
743*c8a17756SThomas Zimmermann }
744*c8a17756SThomas Zimmermann 
745*c8a17756SThomas Zimmermann static const struct of_device_id ofdrm_of_match_display[] = {
746*c8a17756SThomas Zimmermann 	{ .compatible = "display", },
747*c8a17756SThomas Zimmermann 	{ },
748*c8a17756SThomas Zimmermann };
749*c8a17756SThomas Zimmermann MODULE_DEVICE_TABLE(of, ofdrm_of_match_display);
750*c8a17756SThomas Zimmermann 
751*c8a17756SThomas Zimmermann static struct platform_driver ofdrm_platform_driver = {
752*c8a17756SThomas Zimmermann 	.driver = {
753*c8a17756SThomas Zimmermann 		.name = "of-display",
754*c8a17756SThomas Zimmermann 		.of_match_table = ofdrm_of_match_display,
755*c8a17756SThomas Zimmermann 	},
756*c8a17756SThomas Zimmermann 	.probe = ofdrm_probe,
757*c8a17756SThomas Zimmermann 	.remove = ofdrm_remove,
758*c8a17756SThomas Zimmermann };
759*c8a17756SThomas Zimmermann 
760*c8a17756SThomas Zimmermann module_platform_driver(ofdrm_platform_driver);
761*c8a17756SThomas Zimmermann 
762*c8a17756SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC);
763*c8a17756SThomas Zimmermann MODULE_LICENSE("GPL");
764