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