xref: /linux/drivers/gpu/drm/sysfb/ofdrm.c (revision 319251c5e3840410d4bef27b17aa2c2573d28edd)
1*319251c5SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
2*319251c5SThomas Zimmermann 
3*319251c5SThomas Zimmermann #include <linux/aperture.h>
4*319251c5SThomas Zimmermann #include <linux/of_address.h>
5*319251c5SThomas Zimmermann #include <linux/pci.h>
6*319251c5SThomas Zimmermann #include <linux/platform_device.h>
7*319251c5SThomas Zimmermann 
8*319251c5SThomas Zimmermann #include <drm/clients/drm_client_setup.h>
9*319251c5SThomas Zimmermann #include <drm/drm_atomic.h>
10*319251c5SThomas Zimmermann #include <drm/drm_atomic_state_helper.h>
11*319251c5SThomas Zimmermann #include <drm/drm_connector.h>
12*319251c5SThomas Zimmermann #include <drm/drm_damage_helper.h>
13*319251c5SThomas Zimmermann #include <drm/drm_device.h>
14*319251c5SThomas Zimmermann #include <drm/drm_drv.h>
15*319251c5SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
16*319251c5SThomas Zimmermann #include <drm/drm_format_helper.h>
17*319251c5SThomas Zimmermann #include <drm/drm_framebuffer.h>
18*319251c5SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
19*319251c5SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
20*319251c5SThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
21*319251c5SThomas Zimmermann #include <drm/drm_managed.h>
22*319251c5SThomas Zimmermann #include <drm/drm_modeset_helper_vtables.h>
23*319251c5SThomas Zimmermann #include <drm/drm_probe_helper.h>
24*319251c5SThomas Zimmermann 
25*319251c5SThomas Zimmermann #define DRIVER_NAME	"ofdrm"
26*319251c5SThomas Zimmermann #define DRIVER_DESC	"DRM driver for OF platform devices"
27*319251c5SThomas Zimmermann #define DRIVER_MAJOR	1
28*319251c5SThomas Zimmermann #define DRIVER_MINOR	0
29*319251c5SThomas Zimmermann 
30*319251c5SThomas Zimmermann #define PCI_VENDOR_ID_ATI_R520	0x7100
31*319251c5SThomas Zimmermann #define PCI_VENDOR_ID_ATI_R600	0x9400
32*319251c5SThomas Zimmermann 
33*319251c5SThomas Zimmermann #define OFDRM_GAMMA_LUT_SIZE	256
34*319251c5SThomas Zimmermann 
35*319251c5SThomas Zimmermann /* Definitions used by the Avivo palette  */
36*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_RW_SELECT                  0x6480
37*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_RW_MODE                    0x6484
38*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_RW_INDEX                   0x6488
39*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_SEQ_COLOR                  0x648c
40*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_PWL_DATA                   0x6490
41*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_30_COLOR                   0x6494
42*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_READ_PIPE_SELECT           0x6498
43*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_WRITE_EN_MASK              0x649c
44*319251c5SThomas Zimmermann #define AVIVO_DC_LUT_AUTOFILL                   0x64a0
45*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_CONTROL                   0x64c0
46*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE         0x64c4
47*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN        0x64c8
48*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_BLACK_OFFSET_RED          0x64cc
49*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE         0x64d0
50*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN        0x64d4
51*319251c5SThomas Zimmermann #define AVIVO_DC_LUTA_WHITE_OFFSET_RED          0x64d8
52*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_CONTROL                   0x6cc0
53*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE         0x6cc4
54*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN        0x6cc8
55*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_BLACK_OFFSET_RED          0x6ccc
56*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE         0x6cd0
57*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN        0x6cd4
58*319251c5SThomas Zimmermann #define AVIVO_DC_LUTB_WHITE_OFFSET_RED          0x6cd8
59*319251c5SThomas Zimmermann 
60*319251c5SThomas Zimmermann enum ofdrm_model {
61*319251c5SThomas Zimmermann 	OFDRM_MODEL_UNKNOWN,
62*319251c5SThomas Zimmermann 	OFDRM_MODEL_MACH64, /* ATI Mach64 */
63*319251c5SThomas Zimmermann 	OFDRM_MODEL_RAGE128, /* ATI Rage128 */
64*319251c5SThomas Zimmermann 	OFDRM_MODEL_RAGE_M3A, /* ATI Rage Mobility M3 Head A */
65*319251c5SThomas Zimmermann 	OFDRM_MODEL_RAGE_M3B, /* ATI Rage Mobility M3 Head B */
66*319251c5SThomas Zimmermann 	OFDRM_MODEL_RADEON, /* ATI Radeon */
67*319251c5SThomas Zimmermann 	OFDRM_MODEL_GXT2000, /* IBM GXT2000 */
68*319251c5SThomas Zimmermann 	OFDRM_MODEL_AVIVO, /* ATI R5xx */
69*319251c5SThomas Zimmermann 	OFDRM_MODEL_QEMU, /* QEMU VGA */
70*319251c5SThomas Zimmermann };
71*319251c5SThomas Zimmermann 
72*319251c5SThomas Zimmermann /*
73*319251c5SThomas Zimmermann  * Helpers for display nodes
74*319251c5SThomas Zimmermann  */
75*319251c5SThomas Zimmermann 
76*319251c5SThomas Zimmermann static int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value)
77*319251c5SThomas Zimmermann {
78*319251c5SThomas Zimmermann 	if (value > INT_MAX) {
79*319251c5SThomas Zimmermann 		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
80*319251c5SThomas Zimmermann 		return -EINVAL;
81*319251c5SThomas Zimmermann 	}
82*319251c5SThomas Zimmermann 	return (int)value;
83*319251c5SThomas Zimmermann }
84*319251c5SThomas Zimmermann 
85*319251c5SThomas Zimmermann static int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value)
86*319251c5SThomas Zimmermann {
87*319251c5SThomas Zimmermann 	if (!value) {
88*319251c5SThomas Zimmermann 		drm_err(dev, "invalid framebuffer %s of %u\n", name, value);
89*319251c5SThomas Zimmermann 		return -EINVAL;
90*319251c5SThomas Zimmermann 	}
91*319251c5SThomas Zimmermann 	return display_get_validated_int(dev, name, value);
92*319251c5SThomas Zimmermann }
93*319251c5SThomas Zimmermann 
94*319251c5SThomas Zimmermann static const struct drm_format_info *display_get_validated_format(struct drm_device *dev,
95*319251c5SThomas Zimmermann 								  u32 depth, bool big_endian)
96*319251c5SThomas Zimmermann {
97*319251c5SThomas Zimmermann 	const struct drm_format_info *info;
98*319251c5SThomas Zimmermann 	u32 format;
99*319251c5SThomas Zimmermann 
100*319251c5SThomas Zimmermann 	switch (depth) {
101*319251c5SThomas Zimmermann 	case 8:
102*319251c5SThomas Zimmermann 		format = drm_mode_legacy_fb_format(8, 8);
103*319251c5SThomas Zimmermann 		break;
104*319251c5SThomas Zimmermann 	case 15:
105*319251c5SThomas Zimmermann 	case 16:
106*319251c5SThomas Zimmermann 		format = drm_mode_legacy_fb_format(16, depth);
107*319251c5SThomas Zimmermann 		break;
108*319251c5SThomas Zimmermann 	case 32:
109*319251c5SThomas Zimmermann 		format = drm_mode_legacy_fb_format(32, 24);
110*319251c5SThomas Zimmermann 		break;
111*319251c5SThomas Zimmermann 	default:
112*319251c5SThomas Zimmermann 		drm_err(dev, "unsupported framebuffer depth %u\n", depth);
113*319251c5SThomas Zimmermann 		return ERR_PTR(-EINVAL);
114*319251c5SThomas Zimmermann 	}
115*319251c5SThomas Zimmermann 
116*319251c5SThomas Zimmermann 	/*
117*319251c5SThomas Zimmermann 	 * DRM formats assume little-endian byte order. Update the format
118*319251c5SThomas Zimmermann 	 * if the scanout buffer uses big-endian ordering.
119*319251c5SThomas Zimmermann 	 */
120*319251c5SThomas Zimmermann 	if (big_endian) {
121*319251c5SThomas Zimmermann 		switch (format) {
122*319251c5SThomas Zimmermann 		case DRM_FORMAT_XRGB8888:
123*319251c5SThomas Zimmermann 			format = DRM_FORMAT_BGRX8888;
124*319251c5SThomas Zimmermann 			break;
125*319251c5SThomas Zimmermann 		case DRM_FORMAT_ARGB8888:
126*319251c5SThomas Zimmermann 			format = DRM_FORMAT_BGRA8888;
127*319251c5SThomas Zimmermann 			break;
128*319251c5SThomas Zimmermann 		case DRM_FORMAT_RGB565:
129*319251c5SThomas Zimmermann 			format = DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN;
130*319251c5SThomas Zimmermann 			break;
131*319251c5SThomas Zimmermann 		case DRM_FORMAT_XRGB1555:
132*319251c5SThomas Zimmermann 			format = DRM_FORMAT_XRGB1555 | DRM_FORMAT_BIG_ENDIAN;
133*319251c5SThomas Zimmermann 			break;
134*319251c5SThomas Zimmermann 		default:
135*319251c5SThomas Zimmermann 			break;
136*319251c5SThomas Zimmermann 		}
137*319251c5SThomas Zimmermann 	}
138*319251c5SThomas Zimmermann 
139*319251c5SThomas Zimmermann 	info = drm_format_info(format);
140*319251c5SThomas Zimmermann 	if (!info) {
141*319251c5SThomas Zimmermann 		drm_err(dev, "cannot find framebuffer format for depth %u\n", depth);
142*319251c5SThomas Zimmermann 		return ERR_PTR(-EINVAL);
143*319251c5SThomas Zimmermann 	}
144*319251c5SThomas Zimmermann 
145*319251c5SThomas Zimmermann 	return info;
146*319251c5SThomas Zimmermann }
147*319251c5SThomas Zimmermann 
148*319251c5SThomas Zimmermann static int display_read_u32_of(struct drm_device *dev, struct device_node *of_node,
149*319251c5SThomas Zimmermann 			       const char *name, u32 *value)
150*319251c5SThomas Zimmermann {
151*319251c5SThomas Zimmermann 	int ret = of_property_read_u32(of_node, name, value);
152*319251c5SThomas Zimmermann 
153*319251c5SThomas Zimmermann 	if (ret)
154*319251c5SThomas Zimmermann 		drm_err(dev, "cannot parse framebuffer %s: error %d\n", name, ret);
155*319251c5SThomas Zimmermann 	return ret;
156*319251c5SThomas Zimmermann }
157*319251c5SThomas Zimmermann 
158*319251c5SThomas Zimmermann static bool display_get_big_endian_of(struct drm_device *dev, struct device_node *of_node)
159*319251c5SThomas Zimmermann {
160*319251c5SThomas Zimmermann 	bool big_endian;
161*319251c5SThomas Zimmermann 
162*319251c5SThomas Zimmermann #ifdef __BIG_ENDIAN
163*319251c5SThomas Zimmermann 	big_endian = !of_property_read_bool(of_node, "little-endian");
164*319251c5SThomas Zimmermann #else
165*319251c5SThomas Zimmermann 	big_endian = of_property_read_bool(of_node, "big-endian");
166*319251c5SThomas Zimmermann #endif
167*319251c5SThomas Zimmermann 
168*319251c5SThomas Zimmermann 	return big_endian;
169*319251c5SThomas Zimmermann }
170*319251c5SThomas Zimmermann 
171*319251c5SThomas Zimmermann static int display_get_width_of(struct drm_device *dev, struct device_node *of_node)
172*319251c5SThomas Zimmermann {
173*319251c5SThomas Zimmermann 	u32 width;
174*319251c5SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "width", &width);
175*319251c5SThomas Zimmermann 
176*319251c5SThomas Zimmermann 	if (ret)
177*319251c5SThomas Zimmermann 		return ret;
178*319251c5SThomas Zimmermann 	return display_get_validated_int0(dev, "width", width);
179*319251c5SThomas Zimmermann }
180*319251c5SThomas Zimmermann 
181*319251c5SThomas Zimmermann static int display_get_height_of(struct drm_device *dev, struct device_node *of_node)
182*319251c5SThomas Zimmermann {
183*319251c5SThomas Zimmermann 	u32 height;
184*319251c5SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "height", &height);
185*319251c5SThomas Zimmermann 
186*319251c5SThomas Zimmermann 	if (ret)
187*319251c5SThomas Zimmermann 		return ret;
188*319251c5SThomas Zimmermann 	return display_get_validated_int0(dev, "height", height);
189*319251c5SThomas Zimmermann }
190*319251c5SThomas Zimmermann 
191*319251c5SThomas Zimmermann static int display_get_depth_of(struct drm_device *dev, struct device_node *of_node)
192*319251c5SThomas Zimmermann {
193*319251c5SThomas Zimmermann 	u32 depth;
194*319251c5SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "depth", &depth);
195*319251c5SThomas Zimmermann 
196*319251c5SThomas Zimmermann 	if (ret)
197*319251c5SThomas Zimmermann 		return ret;
198*319251c5SThomas Zimmermann 	return display_get_validated_int0(dev, "depth", depth);
199*319251c5SThomas Zimmermann }
200*319251c5SThomas Zimmermann 
201*319251c5SThomas Zimmermann static int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node)
202*319251c5SThomas Zimmermann {
203*319251c5SThomas Zimmermann 	u32 linebytes;
204*319251c5SThomas Zimmermann 	int ret = display_read_u32_of(dev, of_node, "linebytes", &linebytes);
205*319251c5SThomas Zimmermann 
206*319251c5SThomas Zimmermann 	if (ret)
207*319251c5SThomas Zimmermann 		return ret;
208*319251c5SThomas Zimmermann 	return display_get_validated_int(dev, "linebytes", linebytes);
209*319251c5SThomas Zimmermann }
210*319251c5SThomas Zimmermann 
211*319251c5SThomas Zimmermann static u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node)
212*319251c5SThomas Zimmermann {
213*319251c5SThomas Zimmermann 	u32 address;
214*319251c5SThomas Zimmermann 	int ret;
215*319251c5SThomas Zimmermann 
216*319251c5SThomas Zimmermann 	/*
217*319251c5SThomas Zimmermann 	 * Not all devices provide an address property, it's not
218*319251c5SThomas Zimmermann 	 * a bug if this fails. The driver will try to find the
219*319251c5SThomas Zimmermann 	 * framebuffer base address from the device's memory regions.
220*319251c5SThomas Zimmermann 	 */
221*319251c5SThomas Zimmermann 	ret = of_property_read_u32(of_node, "address", &address);
222*319251c5SThomas Zimmermann 	if (ret)
223*319251c5SThomas Zimmermann 		return OF_BAD_ADDR;
224*319251c5SThomas Zimmermann 
225*319251c5SThomas Zimmermann 	return address;
226*319251c5SThomas Zimmermann }
227*319251c5SThomas Zimmermann 
228*319251c5SThomas Zimmermann static bool is_avivo(u32 vendor, u32 device)
229*319251c5SThomas Zimmermann {
230*319251c5SThomas Zimmermann 	/* This will match most R5xx */
231*319251c5SThomas Zimmermann 	return (vendor == PCI_VENDOR_ID_ATI) &&
232*319251c5SThomas Zimmermann 	       ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) ||
233*319251c5SThomas Zimmermann 		(PCI_VENDOR_ID_ATI_R600 >= 0x9400));
234*319251c5SThomas Zimmermann }
235*319251c5SThomas Zimmermann 
236*319251c5SThomas Zimmermann static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node)
237*319251c5SThomas Zimmermann {
238*319251c5SThomas Zimmermann 	enum ofdrm_model model = OFDRM_MODEL_UNKNOWN;
239*319251c5SThomas Zimmermann 
240*319251c5SThomas Zimmermann 	if (of_node_name_prefix(of_node, "ATY,Rage128")) {
241*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_RAGE128;
242*319251c5SThomas Zimmermann 	} else if (of_node_name_prefix(of_node, "ATY,RageM3pA") ||
243*319251c5SThomas Zimmermann 		   of_node_name_prefix(of_node, "ATY,RageM3p12A")) {
244*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_RAGE_M3A;
245*319251c5SThomas Zimmermann 	} else if (of_node_name_prefix(of_node, "ATY,RageM3pB")) {
246*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_RAGE_M3B;
247*319251c5SThomas Zimmermann 	} else if (of_node_name_prefix(of_node, "ATY,Rage6")) {
248*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_RADEON;
249*319251c5SThomas Zimmermann 	} else if (of_node_name_prefix(of_node, "ATY,")) {
250*319251c5SThomas Zimmermann 		return OFDRM_MODEL_MACH64;
251*319251c5SThomas Zimmermann 	} else if (of_device_is_compatible(of_node, "pci1014,b7") ||
252*319251c5SThomas Zimmermann 		   of_device_is_compatible(of_node, "pci1014,21c")) {
253*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_GXT2000;
254*319251c5SThomas Zimmermann 	} else if (of_node_name_prefix(of_node, "vga,Display-")) {
255*319251c5SThomas Zimmermann 		struct device_node *of_parent;
256*319251c5SThomas Zimmermann 		const __be32 *vendor_p, *device_p;
257*319251c5SThomas Zimmermann 
258*319251c5SThomas Zimmermann 		/* Look for AVIVO initialized by SLOF */
259*319251c5SThomas Zimmermann 		of_parent = of_get_parent(of_node);
260*319251c5SThomas Zimmermann 		vendor_p = of_get_property(of_parent, "vendor-id", NULL);
261*319251c5SThomas Zimmermann 		device_p = of_get_property(of_parent, "device-id", NULL);
262*319251c5SThomas Zimmermann 		if (vendor_p && device_p) {
263*319251c5SThomas Zimmermann 			u32 vendor = be32_to_cpup(vendor_p);
264*319251c5SThomas Zimmermann 			u32 device = be32_to_cpup(device_p);
265*319251c5SThomas Zimmermann 
266*319251c5SThomas Zimmermann 			if (is_avivo(vendor, device))
267*319251c5SThomas Zimmermann 				model = OFDRM_MODEL_AVIVO;
268*319251c5SThomas Zimmermann 		}
269*319251c5SThomas Zimmermann 		of_node_put(of_parent);
270*319251c5SThomas Zimmermann 	} else if (of_device_is_compatible(of_node, "qemu,std-vga")) {
271*319251c5SThomas Zimmermann 		model = OFDRM_MODEL_QEMU;
272*319251c5SThomas Zimmermann 	}
273*319251c5SThomas Zimmermann 
274*319251c5SThomas Zimmermann 	return model;
275*319251c5SThomas Zimmermann }
276*319251c5SThomas Zimmermann 
277*319251c5SThomas Zimmermann /*
278*319251c5SThomas Zimmermann  * Open Firmware display device
279*319251c5SThomas Zimmermann  */
280*319251c5SThomas Zimmermann 
281*319251c5SThomas Zimmermann struct ofdrm_device;
282*319251c5SThomas Zimmermann 
283*319251c5SThomas Zimmermann struct ofdrm_device_funcs {
284*319251c5SThomas Zimmermann 	void __iomem *(*cmap_ioremap)(struct ofdrm_device *odev,
285*319251c5SThomas Zimmermann 				      struct device_node *of_node,
286*319251c5SThomas Zimmermann 				      u64 fb_bas);
287*319251c5SThomas Zimmermann 	void (*cmap_write)(struct ofdrm_device *odev, unsigned char index,
288*319251c5SThomas Zimmermann 			   unsigned char r, unsigned char g, unsigned char b);
289*319251c5SThomas Zimmermann };
290*319251c5SThomas Zimmermann 
291*319251c5SThomas Zimmermann struct ofdrm_device {
292*319251c5SThomas Zimmermann 	struct drm_device dev;
293*319251c5SThomas Zimmermann 
294*319251c5SThomas Zimmermann 	const struct ofdrm_device_funcs *funcs;
295*319251c5SThomas Zimmermann 
296*319251c5SThomas Zimmermann 	/* firmware-buffer settings */
297*319251c5SThomas Zimmermann 	struct iosys_map screen_base;
298*319251c5SThomas Zimmermann 	struct drm_display_mode mode;
299*319251c5SThomas Zimmermann 	const struct drm_format_info *format;
300*319251c5SThomas Zimmermann 	unsigned int pitch;
301*319251c5SThomas Zimmermann 
302*319251c5SThomas Zimmermann 	/* colormap */
303*319251c5SThomas Zimmermann 	void __iomem *cmap_base;
304*319251c5SThomas Zimmermann 
305*319251c5SThomas Zimmermann 	/* modesetting */
306*319251c5SThomas Zimmermann 	uint32_t formats[8];
307*319251c5SThomas Zimmermann 	struct drm_plane primary_plane;
308*319251c5SThomas Zimmermann 	struct drm_crtc crtc;
309*319251c5SThomas Zimmermann 	struct drm_encoder encoder;
310*319251c5SThomas Zimmermann 	struct drm_connector connector;
311*319251c5SThomas Zimmermann };
312*319251c5SThomas Zimmermann 
313*319251c5SThomas Zimmermann static struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev)
314*319251c5SThomas Zimmermann {
315*319251c5SThomas Zimmermann 	return container_of(dev, struct ofdrm_device, dev);
316*319251c5SThomas Zimmermann }
317*319251c5SThomas Zimmermann 
318*319251c5SThomas Zimmermann /*
319*319251c5SThomas Zimmermann  * Hardware
320*319251c5SThomas Zimmermann  */
321*319251c5SThomas Zimmermann 
322*319251c5SThomas Zimmermann #if defined(CONFIG_PCI)
323*319251c5SThomas Zimmermann static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node)
324*319251c5SThomas Zimmermann {
325*319251c5SThomas Zimmermann 	const __be32 *vendor_p, *device_p;
326*319251c5SThomas Zimmermann 	u32 vendor, device;
327*319251c5SThomas Zimmermann 	struct pci_dev *pcidev;
328*319251c5SThomas Zimmermann 
329*319251c5SThomas Zimmermann 	vendor_p = of_get_property(of_node, "vendor-id", NULL);
330*319251c5SThomas Zimmermann 	if (!vendor_p)
331*319251c5SThomas Zimmermann 		return ERR_PTR(-ENODEV);
332*319251c5SThomas Zimmermann 	vendor = be32_to_cpup(vendor_p);
333*319251c5SThomas Zimmermann 
334*319251c5SThomas Zimmermann 	device_p = of_get_property(of_node, "device-id", NULL);
335*319251c5SThomas Zimmermann 	if (!device_p)
336*319251c5SThomas Zimmermann 		return ERR_PTR(-ENODEV);
337*319251c5SThomas Zimmermann 	device = be32_to_cpup(device_p);
338*319251c5SThomas Zimmermann 
339*319251c5SThomas Zimmermann 	pcidev = pci_get_device(vendor, device, NULL);
340*319251c5SThomas Zimmermann 	if (!pcidev)
341*319251c5SThomas Zimmermann 		return ERR_PTR(-ENODEV);
342*319251c5SThomas Zimmermann 
343*319251c5SThomas Zimmermann 	return pcidev;
344*319251c5SThomas Zimmermann }
345*319251c5SThomas Zimmermann 
346*319251c5SThomas Zimmermann static void ofdrm_pci_release(void *data)
347*319251c5SThomas Zimmermann {
348*319251c5SThomas Zimmermann 	struct pci_dev *pcidev = data;
349*319251c5SThomas Zimmermann 
350*319251c5SThomas Zimmermann 	pci_disable_device(pcidev);
351*319251c5SThomas Zimmermann }
352*319251c5SThomas Zimmermann 
353*319251c5SThomas Zimmermann static int ofdrm_device_init_pci(struct ofdrm_device *odev)
354*319251c5SThomas Zimmermann {
355*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
356*319251c5SThomas Zimmermann 	struct platform_device *pdev = to_platform_device(dev->dev);
357*319251c5SThomas Zimmermann 	struct device_node *of_node = pdev->dev.of_node;
358*319251c5SThomas Zimmermann 	struct pci_dev *pcidev;
359*319251c5SThomas Zimmermann 	int ret;
360*319251c5SThomas Zimmermann 
361*319251c5SThomas Zimmermann 	/*
362*319251c5SThomas Zimmermann 	 * Never use pcim_ or other managed helpers on the returned PCI
363*319251c5SThomas Zimmermann 	 * device. Otherwise, probing the native driver will fail for
364*319251c5SThomas Zimmermann 	 * resource conflicts. PCI-device management has to be tied to
365*319251c5SThomas Zimmermann 	 * the lifetime of the platform device until the native driver
366*319251c5SThomas Zimmermann 	 * takes over.
367*319251c5SThomas Zimmermann 	 */
368*319251c5SThomas Zimmermann 	pcidev = display_get_pci_dev_of(dev, of_node);
369*319251c5SThomas Zimmermann 	if (IS_ERR(pcidev))
370*319251c5SThomas Zimmermann 		return 0; /* no PCI device found; ignore the error */
371*319251c5SThomas Zimmermann 
372*319251c5SThomas Zimmermann 	ret = pci_enable_device(pcidev);
373*319251c5SThomas Zimmermann 	if (ret) {
374*319251c5SThomas Zimmermann 		drm_err(dev, "pci_enable_device(%s) failed: %d\n",
375*319251c5SThomas Zimmermann 			dev_name(&pcidev->dev), ret);
376*319251c5SThomas Zimmermann 		return ret;
377*319251c5SThomas Zimmermann 	}
378*319251c5SThomas Zimmermann 	ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev);
379*319251c5SThomas Zimmermann 	if (ret)
380*319251c5SThomas Zimmermann 		return ret;
381*319251c5SThomas Zimmermann 
382*319251c5SThomas Zimmermann 	return 0;
383*319251c5SThomas Zimmermann }
384*319251c5SThomas Zimmermann #else
385*319251c5SThomas Zimmermann static int ofdrm_device_init_pci(struct ofdrm_device *odev)
386*319251c5SThomas Zimmermann {
387*319251c5SThomas Zimmermann 	return 0;
388*319251c5SThomas Zimmermann }
389*319251c5SThomas Zimmermann #endif
390*319251c5SThomas Zimmermann 
391*319251c5SThomas Zimmermann /*
392*319251c5SThomas Zimmermann  *  OF display settings
393*319251c5SThomas Zimmermann  */
394*319251c5SThomas Zimmermann 
395*319251c5SThomas Zimmermann static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev,
396*319251c5SThomas Zimmermann 					       struct resource *fb_res)
397*319251c5SThomas Zimmermann {
398*319251c5SThomas Zimmermann 	struct platform_device *pdev = to_platform_device(odev->dev.dev);
399*319251c5SThomas Zimmermann 	struct resource *res, *max_res = NULL;
400*319251c5SThomas Zimmermann 	u32 i;
401*319251c5SThomas Zimmermann 
402*319251c5SThomas Zimmermann 	for (i = 0; pdev->num_resources; ++i) {
403*319251c5SThomas Zimmermann 		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
404*319251c5SThomas Zimmermann 		if (!res)
405*319251c5SThomas Zimmermann 			break; /* all resources processed */
406*319251c5SThomas Zimmermann 		if (resource_size(res) < resource_size(fb_res))
407*319251c5SThomas Zimmermann 			continue; /* resource too small */
408*319251c5SThomas Zimmermann 		if (fb_res->start && resource_contains(res, fb_res))
409*319251c5SThomas Zimmermann 			return res; /* resource contains framebuffer */
410*319251c5SThomas Zimmermann 		if (!max_res || resource_size(res) > resource_size(max_res))
411*319251c5SThomas Zimmermann 			max_res = res; /* store largest resource as fallback */
412*319251c5SThomas Zimmermann 	}
413*319251c5SThomas Zimmermann 
414*319251c5SThomas Zimmermann 	return max_res;
415*319251c5SThomas Zimmermann }
416*319251c5SThomas Zimmermann 
417*319251c5SThomas Zimmermann /*
418*319251c5SThomas Zimmermann  * Colormap / Palette
419*319251c5SThomas Zimmermann  */
420*319251c5SThomas Zimmermann 
421*319251c5SThomas Zimmermann static void __iomem *get_cmap_address_of(struct ofdrm_device *odev, struct device_node *of_node,
422*319251c5SThomas Zimmermann 					 int bar_no, unsigned long offset, unsigned long size)
423*319251c5SThomas Zimmermann {
424*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
425*319251c5SThomas Zimmermann 	const __be32 *addr_p;
426*319251c5SThomas Zimmermann 	u64 max_size, address;
427*319251c5SThomas Zimmermann 	unsigned int flags;
428*319251c5SThomas Zimmermann 	void __iomem *mem;
429*319251c5SThomas Zimmermann 
430*319251c5SThomas Zimmermann 	addr_p = of_get_pci_address(of_node, bar_no, &max_size, &flags);
431*319251c5SThomas Zimmermann 	if (!addr_p)
432*319251c5SThomas Zimmermann 		addr_p = of_get_address(of_node, bar_no, &max_size, &flags);
433*319251c5SThomas Zimmermann 	if (!addr_p)
434*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENODEV);
435*319251c5SThomas Zimmermann 
436*319251c5SThomas Zimmermann 	if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
437*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENODEV);
438*319251c5SThomas Zimmermann 
439*319251c5SThomas Zimmermann 	if ((offset + size) >= max_size)
440*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENODEV);
441*319251c5SThomas Zimmermann 
442*319251c5SThomas Zimmermann 	address = of_translate_address(of_node, addr_p);
443*319251c5SThomas Zimmermann 	if (address == OF_BAD_ADDR)
444*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENODEV);
445*319251c5SThomas Zimmermann 
446*319251c5SThomas Zimmermann 	mem = devm_ioremap(dev->dev, address + offset, size);
447*319251c5SThomas Zimmermann 	if (!mem)
448*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENOMEM);
449*319251c5SThomas Zimmermann 
450*319251c5SThomas Zimmermann 	return mem;
451*319251c5SThomas Zimmermann }
452*319251c5SThomas Zimmermann 
453*319251c5SThomas Zimmermann static void __iomem *ofdrm_mach64_cmap_ioremap(struct ofdrm_device *odev,
454*319251c5SThomas Zimmermann 					       struct device_node *of_node,
455*319251c5SThomas Zimmermann 					       u64 fb_base)
456*319251c5SThomas Zimmermann {
457*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
458*319251c5SThomas Zimmermann 	u64 address;
459*319251c5SThomas Zimmermann 	void __iomem *cmap_base;
460*319251c5SThomas Zimmermann 
461*319251c5SThomas Zimmermann 	address = fb_base & 0xff000000ul;
462*319251c5SThomas Zimmermann 	address += 0x7ff000;
463*319251c5SThomas Zimmermann 
464*319251c5SThomas Zimmermann 	cmap_base = devm_ioremap(dev->dev, address, 0x1000);
465*319251c5SThomas Zimmermann 	if (!cmap_base)
466*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENOMEM);
467*319251c5SThomas Zimmermann 
468*319251c5SThomas Zimmermann 	return cmap_base;
469*319251c5SThomas Zimmermann }
470*319251c5SThomas Zimmermann 
471*319251c5SThomas Zimmermann static void ofdrm_mach64_cmap_write(struct ofdrm_device *odev, unsigned char index,
472*319251c5SThomas Zimmermann 				    unsigned char r, unsigned char g, unsigned char b)
473*319251c5SThomas Zimmermann {
474*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base + 0xcc0;
475*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + 0xcc0 + 1;
476*319251c5SThomas Zimmermann 
477*319251c5SThomas Zimmermann 	writeb(index, addr);
478*319251c5SThomas Zimmermann 	writeb(r, data);
479*319251c5SThomas Zimmermann 	writeb(g, data);
480*319251c5SThomas Zimmermann 	writeb(b, data);
481*319251c5SThomas Zimmermann }
482*319251c5SThomas Zimmermann 
483*319251c5SThomas Zimmermann static void __iomem *ofdrm_rage128_cmap_ioremap(struct ofdrm_device *odev,
484*319251c5SThomas Zimmermann 						struct device_node *of_node,
485*319251c5SThomas Zimmermann 						u64 fb_base)
486*319251c5SThomas Zimmermann {
487*319251c5SThomas Zimmermann 	return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff);
488*319251c5SThomas Zimmermann }
489*319251c5SThomas Zimmermann 
490*319251c5SThomas Zimmermann static void ofdrm_rage128_cmap_write(struct ofdrm_device *odev, unsigned char index,
491*319251c5SThomas Zimmermann 				     unsigned char r, unsigned char g, unsigned char b)
492*319251c5SThomas Zimmermann {
493*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base + 0xb0;
494*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + 0xb4;
495*319251c5SThomas Zimmermann 	u32 color = (r << 16) | (g << 8) | b;
496*319251c5SThomas Zimmermann 
497*319251c5SThomas Zimmermann 	writeb(index, addr);
498*319251c5SThomas Zimmermann 	writel(color, data);
499*319251c5SThomas Zimmermann }
500*319251c5SThomas Zimmermann 
501*319251c5SThomas Zimmermann static void __iomem *ofdrm_rage_m3a_cmap_ioremap(struct ofdrm_device *odev,
502*319251c5SThomas Zimmermann 						 struct device_node *of_node,
503*319251c5SThomas Zimmermann 						 u64 fb_base)
504*319251c5SThomas Zimmermann {
505*319251c5SThomas Zimmermann 	return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff);
506*319251c5SThomas Zimmermann }
507*319251c5SThomas Zimmermann 
508*319251c5SThomas Zimmermann static void ofdrm_rage_m3a_cmap_write(struct ofdrm_device *odev, unsigned char index,
509*319251c5SThomas Zimmermann 				      unsigned char r, unsigned char g, unsigned char b)
510*319251c5SThomas Zimmermann {
511*319251c5SThomas Zimmermann 	void __iomem *dac_ctl = odev->cmap_base + 0x58;
512*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base + 0xb0;
513*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + 0xb4;
514*319251c5SThomas Zimmermann 	u32 color = (r << 16) | (g << 8) | b;
515*319251c5SThomas Zimmermann 	u32 val;
516*319251c5SThomas Zimmermann 
517*319251c5SThomas Zimmermann 	/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
518*319251c5SThomas Zimmermann 	val = readl(dac_ctl);
519*319251c5SThomas Zimmermann 	val &= ~0x20;
520*319251c5SThomas Zimmermann 	writel(val, dac_ctl);
521*319251c5SThomas Zimmermann 
522*319251c5SThomas Zimmermann 	/* Set color at palette index */
523*319251c5SThomas Zimmermann 	writeb(index, addr);
524*319251c5SThomas Zimmermann 	writel(color, data);
525*319251c5SThomas Zimmermann }
526*319251c5SThomas Zimmermann 
527*319251c5SThomas Zimmermann static void __iomem *ofdrm_rage_m3b_cmap_ioremap(struct ofdrm_device *odev,
528*319251c5SThomas Zimmermann 						 struct device_node *of_node,
529*319251c5SThomas Zimmermann 						 u64 fb_base)
530*319251c5SThomas Zimmermann {
531*319251c5SThomas Zimmermann 	return get_cmap_address_of(odev, of_node, 2, 0, 0x1fff);
532*319251c5SThomas Zimmermann }
533*319251c5SThomas Zimmermann 
534*319251c5SThomas Zimmermann static void ofdrm_rage_m3b_cmap_write(struct ofdrm_device *odev, unsigned char index,
535*319251c5SThomas Zimmermann 				      unsigned char r, unsigned char g, unsigned char b)
536*319251c5SThomas Zimmermann {
537*319251c5SThomas Zimmermann 	void __iomem *dac_ctl = odev->cmap_base + 0x58;
538*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base + 0xb0;
539*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + 0xb4;
540*319251c5SThomas Zimmermann 	u32 color = (r << 16) | (g << 8) | b;
541*319251c5SThomas Zimmermann 	u32 val;
542*319251c5SThomas Zimmermann 
543*319251c5SThomas Zimmermann 	/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
544*319251c5SThomas Zimmermann 	val = readl(dac_ctl);
545*319251c5SThomas Zimmermann 	val |= 0x20;
546*319251c5SThomas Zimmermann 	writel(val, dac_ctl);
547*319251c5SThomas Zimmermann 
548*319251c5SThomas Zimmermann 	/* Set color at palette index */
549*319251c5SThomas Zimmermann 	writeb(index, addr);
550*319251c5SThomas Zimmermann 	writel(color, data);
551*319251c5SThomas Zimmermann }
552*319251c5SThomas Zimmermann 
553*319251c5SThomas Zimmermann static void __iomem *ofdrm_radeon_cmap_ioremap(struct ofdrm_device *odev,
554*319251c5SThomas Zimmermann 					       struct device_node *of_node,
555*319251c5SThomas Zimmermann 					       u64 fb_base)
556*319251c5SThomas Zimmermann {
557*319251c5SThomas Zimmermann 	return get_cmap_address_of(odev, of_node, 1, 0, 0x1fff);
558*319251c5SThomas Zimmermann }
559*319251c5SThomas Zimmermann 
560*319251c5SThomas Zimmermann static void __iomem *ofdrm_gxt2000_cmap_ioremap(struct ofdrm_device *odev,
561*319251c5SThomas Zimmermann 						struct device_node *of_node,
562*319251c5SThomas Zimmermann 						u64 fb_base)
563*319251c5SThomas Zimmermann {
564*319251c5SThomas Zimmermann 	return get_cmap_address_of(odev, of_node, 0, 0x6000, 0x1000);
565*319251c5SThomas Zimmermann }
566*319251c5SThomas Zimmermann 
567*319251c5SThomas Zimmermann static void ofdrm_gxt2000_cmap_write(struct ofdrm_device *odev, unsigned char index,
568*319251c5SThomas Zimmermann 				     unsigned char r, unsigned char g, unsigned char b)
569*319251c5SThomas Zimmermann {
570*319251c5SThomas Zimmermann 	void __iomem *data = ((unsigned int __iomem *)odev->cmap_base) + index;
571*319251c5SThomas Zimmermann 	u32 color = (r << 16) | (g << 8) | b;
572*319251c5SThomas Zimmermann 
573*319251c5SThomas Zimmermann 	writel(color, data);
574*319251c5SThomas Zimmermann }
575*319251c5SThomas Zimmermann 
576*319251c5SThomas Zimmermann static void __iomem *ofdrm_avivo_cmap_ioremap(struct ofdrm_device *odev,
577*319251c5SThomas Zimmermann 					      struct device_node *of_node,
578*319251c5SThomas Zimmermann 					      u64 fb_base)
579*319251c5SThomas Zimmermann {
580*319251c5SThomas Zimmermann 	struct device_node *of_parent;
581*319251c5SThomas Zimmermann 	void __iomem *cmap_base;
582*319251c5SThomas Zimmermann 
583*319251c5SThomas Zimmermann 	of_parent = of_get_parent(of_node);
584*319251c5SThomas Zimmermann 	cmap_base = get_cmap_address_of(odev, of_parent, 0, 0, 0x10000);
585*319251c5SThomas Zimmermann 	of_node_put(of_parent);
586*319251c5SThomas Zimmermann 
587*319251c5SThomas Zimmermann 	return cmap_base;
588*319251c5SThomas Zimmermann }
589*319251c5SThomas Zimmermann 
590*319251c5SThomas Zimmermann static void ofdrm_avivo_cmap_write(struct ofdrm_device *odev, unsigned char index,
591*319251c5SThomas Zimmermann 				   unsigned char r, unsigned char g, unsigned char b)
592*319251c5SThomas Zimmermann {
593*319251c5SThomas Zimmermann 	void __iomem *lutsel = odev->cmap_base + AVIVO_DC_LUT_RW_SELECT;
594*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base + AVIVO_DC_LUT_RW_INDEX;
595*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + AVIVO_DC_LUT_30_COLOR;
596*319251c5SThomas Zimmermann 	u32 color = (r << 22) | (g << 12) | (b << 2);
597*319251c5SThomas Zimmermann 
598*319251c5SThomas Zimmermann 	/* Write to both LUTs for now */
599*319251c5SThomas Zimmermann 
600*319251c5SThomas Zimmermann 	writel(1, lutsel);
601*319251c5SThomas Zimmermann 	writeb(index, addr);
602*319251c5SThomas Zimmermann 	writel(color, data);
603*319251c5SThomas Zimmermann 
604*319251c5SThomas Zimmermann 	writel(0, lutsel);
605*319251c5SThomas Zimmermann 	writeb(index, addr);
606*319251c5SThomas Zimmermann 	writel(color, data);
607*319251c5SThomas Zimmermann }
608*319251c5SThomas Zimmermann 
609*319251c5SThomas Zimmermann static void __iomem *ofdrm_qemu_cmap_ioremap(struct ofdrm_device *odev,
610*319251c5SThomas Zimmermann 					     struct device_node *of_node,
611*319251c5SThomas Zimmermann 					     u64 fb_base)
612*319251c5SThomas Zimmermann {
613*319251c5SThomas Zimmermann 	static const __be32 io_of_addr[3] = {
614*319251c5SThomas Zimmermann 		cpu_to_be32(0x01000000),
615*319251c5SThomas Zimmermann 		cpu_to_be32(0x00),
616*319251c5SThomas Zimmermann 		cpu_to_be32(0x00),
617*319251c5SThomas Zimmermann 	};
618*319251c5SThomas Zimmermann 
619*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
620*319251c5SThomas Zimmermann 	u64 address;
621*319251c5SThomas Zimmermann 	void __iomem *cmap_base;
622*319251c5SThomas Zimmermann 
623*319251c5SThomas Zimmermann 	address = of_translate_address(of_node, io_of_addr);
624*319251c5SThomas Zimmermann 	if (address == OF_BAD_ADDR)
625*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENODEV);
626*319251c5SThomas Zimmermann 
627*319251c5SThomas Zimmermann 	cmap_base = devm_ioremap(dev->dev, address + 0x3c8, 2);
628*319251c5SThomas Zimmermann 	if (!cmap_base)
629*319251c5SThomas Zimmermann 		return IOMEM_ERR_PTR(-ENOMEM);
630*319251c5SThomas Zimmermann 
631*319251c5SThomas Zimmermann 	return cmap_base;
632*319251c5SThomas Zimmermann }
633*319251c5SThomas Zimmermann 
634*319251c5SThomas Zimmermann static void ofdrm_qemu_cmap_write(struct ofdrm_device *odev, unsigned char index,
635*319251c5SThomas Zimmermann 				  unsigned char r, unsigned char g, unsigned char b)
636*319251c5SThomas Zimmermann {
637*319251c5SThomas Zimmermann 	void __iomem *addr = odev->cmap_base;
638*319251c5SThomas Zimmermann 	void __iomem *data = odev->cmap_base + 1;
639*319251c5SThomas Zimmermann 
640*319251c5SThomas Zimmermann 	writeb(index, addr);
641*319251c5SThomas Zimmermann 	writeb(r, data);
642*319251c5SThomas Zimmermann 	writeb(g, data);
643*319251c5SThomas Zimmermann 	writeb(b, data);
644*319251c5SThomas Zimmermann }
645*319251c5SThomas Zimmermann 
646*319251c5SThomas Zimmermann static void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev,
647*319251c5SThomas Zimmermann 					  const struct drm_format_info *format)
648*319251c5SThomas Zimmermann {
649*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
650*319251c5SThomas Zimmermann 	int i;
651*319251c5SThomas Zimmermann 
652*319251c5SThomas Zimmermann 	switch (format->format) {
653*319251c5SThomas Zimmermann 	case DRM_FORMAT_RGB565:
654*319251c5SThomas Zimmermann 	case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN:
655*319251c5SThomas Zimmermann 		/* Use better interpolation, to take 32 values from 0 to 255 */
656*319251c5SThomas Zimmermann 		for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) {
657*319251c5SThomas Zimmermann 			unsigned char r = i * 8 + i / 4;
658*319251c5SThomas Zimmermann 			unsigned char g = i * 4 + i / 16;
659*319251c5SThomas Zimmermann 			unsigned char b = i * 8 + i / 4;
660*319251c5SThomas Zimmermann 
661*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, r, g, b);
662*319251c5SThomas Zimmermann 		}
663*319251c5SThomas Zimmermann 		/* Green has one more bit, so add padding with 0 for red and blue. */
664*319251c5SThomas Zimmermann 		for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) {
665*319251c5SThomas Zimmermann 			unsigned char r = 0;
666*319251c5SThomas Zimmermann 			unsigned char g = i * 4 + i / 16;
667*319251c5SThomas Zimmermann 			unsigned char b = 0;
668*319251c5SThomas Zimmermann 
669*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, r, g, b);
670*319251c5SThomas Zimmermann 		}
671*319251c5SThomas Zimmermann 		break;
672*319251c5SThomas Zimmermann 	case DRM_FORMAT_XRGB8888:
673*319251c5SThomas Zimmermann 	case DRM_FORMAT_BGRX8888:
674*319251c5SThomas Zimmermann 		for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++)
675*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, i, i, i);
676*319251c5SThomas Zimmermann 		break;
677*319251c5SThomas Zimmermann 	default:
678*319251c5SThomas Zimmermann 		drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n",
679*319251c5SThomas Zimmermann 			      &format->format);
680*319251c5SThomas Zimmermann 		break;
681*319251c5SThomas Zimmermann 	}
682*319251c5SThomas Zimmermann }
683*319251c5SThomas Zimmermann 
684*319251c5SThomas Zimmermann static void ofdrm_device_set_gamma(struct ofdrm_device *odev,
685*319251c5SThomas Zimmermann 				   const struct drm_format_info *format,
686*319251c5SThomas Zimmermann 				   struct drm_color_lut *lut)
687*319251c5SThomas Zimmermann {
688*319251c5SThomas Zimmermann 	struct drm_device *dev = &odev->dev;
689*319251c5SThomas Zimmermann 	int i;
690*319251c5SThomas Zimmermann 
691*319251c5SThomas Zimmermann 	switch (format->format) {
692*319251c5SThomas Zimmermann 	case DRM_FORMAT_RGB565:
693*319251c5SThomas Zimmermann 	case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN:
694*319251c5SThomas Zimmermann 		/* Use better interpolation, to take 32 values from lut[0] to lut[255] */
695*319251c5SThomas Zimmermann 		for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) {
696*319251c5SThomas Zimmermann 			unsigned char r = lut[i * 8 + i / 4].red >> 8;
697*319251c5SThomas Zimmermann 			unsigned char g = lut[i * 4 + i / 16].green >> 8;
698*319251c5SThomas Zimmermann 			unsigned char b = lut[i * 8 + i / 4].blue >> 8;
699*319251c5SThomas Zimmermann 
700*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, r, g, b);
701*319251c5SThomas Zimmermann 		}
702*319251c5SThomas Zimmermann 		/* Green has one more bit, so add padding with 0 for red and blue. */
703*319251c5SThomas Zimmermann 		for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) {
704*319251c5SThomas Zimmermann 			unsigned char r = 0;
705*319251c5SThomas Zimmermann 			unsigned char g = lut[i * 4 + i / 16].green >> 8;
706*319251c5SThomas Zimmermann 			unsigned char b = 0;
707*319251c5SThomas Zimmermann 
708*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, r, g, b);
709*319251c5SThomas Zimmermann 		}
710*319251c5SThomas Zimmermann 		break;
711*319251c5SThomas Zimmermann 	case DRM_FORMAT_XRGB8888:
712*319251c5SThomas Zimmermann 	case DRM_FORMAT_BGRX8888:
713*319251c5SThomas Zimmermann 		for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) {
714*319251c5SThomas Zimmermann 			unsigned char r = lut[i].red >> 8;
715*319251c5SThomas Zimmermann 			unsigned char g = lut[i].green >> 8;
716*319251c5SThomas Zimmermann 			unsigned char b = lut[i].blue >> 8;
717*319251c5SThomas Zimmermann 
718*319251c5SThomas Zimmermann 			odev->funcs->cmap_write(odev, i, r, g, b);
719*319251c5SThomas Zimmermann 		}
720*319251c5SThomas Zimmermann 		break;
721*319251c5SThomas Zimmermann 	default:
722*319251c5SThomas Zimmermann 		drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n",
723*319251c5SThomas Zimmermann 			      &format->format);
724*319251c5SThomas Zimmermann 		break;
725*319251c5SThomas Zimmermann 	}
726*319251c5SThomas Zimmermann }
727*319251c5SThomas Zimmermann 
728*319251c5SThomas Zimmermann /*
729*319251c5SThomas Zimmermann  * Modesetting
730*319251c5SThomas Zimmermann  */
731*319251c5SThomas Zimmermann 
732*319251c5SThomas Zimmermann struct ofdrm_crtc_state {
733*319251c5SThomas Zimmermann 	struct drm_crtc_state base;
734*319251c5SThomas Zimmermann 
735*319251c5SThomas Zimmermann 	/* Primary-plane format; required for color mgmt. */
736*319251c5SThomas Zimmermann 	const struct drm_format_info *format;
737*319251c5SThomas Zimmermann };
738*319251c5SThomas Zimmermann 
739*319251c5SThomas Zimmermann static struct ofdrm_crtc_state *to_ofdrm_crtc_state(struct drm_crtc_state *base)
740*319251c5SThomas Zimmermann {
741*319251c5SThomas Zimmermann 	return container_of(base, struct ofdrm_crtc_state, base);
742*319251c5SThomas Zimmermann }
743*319251c5SThomas Zimmermann 
744*319251c5SThomas Zimmermann static void ofdrm_crtc_state_destroy(struct ofdrm_crtc_state *ofdrm_crtc_state)
745*319251c5SThomas Zimmermann {
746*319251c5SThomas Zimmermann 	__drm_atomic_helper_crtc_destroy_state(&ofdrm_crtc_state->base);
747*319251c5SThomas Zimmermann 	kfree(ofdrm_crtc_state);
748*319251c5SThomas Zimmermann }
749*319251c5SThomas Zimmermann 
750*319251c5SThomas Zimmermann static const uint64_t ofdrm_primary_plane_format_modifiers[] = {
751*319251c5SThomas Zimmermann 	DRM_FORMAT_MOD_LINEAR,
752*319251c5SThomas Zimmermann 	DRM_FORMAT_MOD_INVALID
753*319251c5SThomas Zimmermann };
754*319251c5SThomas Zimmermann 
755*319251c5SThomas Zimmermann static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
756*319251c5SThomas Zimmermann 						   struct drm_atomic_state *new_state)
757*319251c5SThomas Zimmermann {
758*319251c5SThomas Zimmermann 	struct drm_device *dev = plane->dev;
759*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(dev);
760*319251c5SThomas Zimmermann 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
761*319251c5SThomas Zimmermann 	struct drm_shadow_plane_state *new_shadow_plane_state =
762*319251c5SThomas Zimmermann 		to_drm_shadow_plane_state(new_plane_state);
763*319251c5SThomas Zimmermann 	struct drm_framebuffer *new_fb = new_plane_state->fb;
764*319251c5SThomas Zimmermann 	struct drm_crtc *new_crtc = new_plane_state->crtc;
765*319251c5SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = NULL;
766*319251c5SThomas Zimmermann 	struct ofdrm_crtc_state *new_ofdrm_crtc_state;
767*319251c5SThomas Zimmermann 	int ret;
768*319251c5SThomas Zimmermann 
769*319251c5SThomas Zimmermann 	if (new_crtc)
770*319251c5SThomas Zimmermann 		new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
771*319251c5SThomas Zimmermann 
772*319251c5SThomas Zimmermann 	ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
773*319251c5SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
774*319251c5SThomas Zimmermann 						  DRM_PLANE_NO_SCALING,
775*319251c5SThomas Zimmermann 						  false, false);
776*319251c5SThomas Zimmermann 	if (ret)
777*319251c5SThomas Zimmermann 		return ret;
778*319251c5SThomas Zimmermann 	else if (!new_plane_state->visible)
779*319251c5SThomas Zimmermann 		return 0;
780*319251c5SThomas Zimmermann 
781*319251c5SThomas Zimmermann 	if (new_fb->format != odev->format) {
782*319251c5SThomas Zimmermann 		void *buf;
783*319251c5SThomas Zimmermann 
784*319251c5SThomas Zimmermann 		/* format conversion necessary; reserve buffer */
785*319251c5SThomas Zimmermann 		buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
786*319251c5SThomas Zimmermann 						    odev->pitch, GFP_KERNEL);
787*319251c5SThomas Zimmermann 		if (!buf)
788*319251c5SThomas Zimmermann 			return -ENOMEM;
789*319251c5SThomas Zimmermann 	}
790*319251c5SThomas Zimmermann 
791*319251c5SThomas Zimmermann 	new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
792*319251c5SThomas Zimmermann 
793*319251c5SThomas Zimmermann 	new_ofdrm_crtc_state = to_ofdrm_crtc_state(new_crtc_state);
794*319251c5SThomas Zimmermann 	new_ofdrm_crtc_state->format = new_fb->format;
795*319251c5SThomas Zimmermann 
796*319251c5SThomas Zimmermann 	return 0;
797*319251c5SThomas Zimmermann }
798*319251c5SThomas Zimmermann 
799*319251c5SThomas Zimmermann static void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
800*319251c5SThomas Zimmermann 						     struct drm_atomic_state *state)
801*319251c5SThomas Zimmermann {
802*319251c5SThomas Zimmermann 	struct drm_device *dev = plane->dev;
803*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(dev);
804*319251c5SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
805*319251c5SThomas Zimmermann 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
806*319251c5SThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
807*319251c5SThomas Zimmermann 	struct drm_framebuffer *fb = plane_state->fb;
808*319251c5SThomas Zimmermann 	unsigned int dst_pitch = odev->pitch;
809*319251c5SThomas Zimmermann 	const struct drm_format_info *dst_format = odev->format;
810*319251c5SThomas Zimmermann 	struct drm_atomic_helper_damage_iter iter;
811*319251c5SThomas Zimmermann 	struct drm_rect damage;
812*319251c5SThomas Zimmermann 	int ret, idx;
813*319251c5SThomas Zimmermann 
814*319251c5SThomas Zimmermann 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
815*319251c5SThomas Zimmermann 	if (ret)
816*319251c5SThomas Zimmermann 		return;
817*319251c5SThomas Zimmermann 
818*319251c5SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
819*319251c5SThomas Zimmermann 		goto out_drm_gem_fb_end_cpu_access;
820*319251c5SThomas Zimmermann 
821*319251c5SThomas Zimmermann 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
822*319251c5SThomas Zimmermann 	drm_atomic_for_each_plane_damage(&iter, &damage) {
823*319251c5SThomas Zimmermann 		struct iosys_map dst = odev->screen_base;
824*319251c5SThomas Zimmermann 		struct drm_rect dst_clip = plane_state->dst;
825*319251c5SThomas Zimmermann 
826*319251c5SThomas Zimmermann 		if (!drm_rect_intersect(&dst_clip, &damage))
827*319251c5SThomas Zimmermann 			continue;
828*319251c5SThomas Zimmermann 
829*319251c5SThomas Zimmermann 		iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
830*319251c5SThomas Zimmermann 		drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
831*319251c5SThomas Zimmermann 			    &damage, &shadow_plane_state->fmtcnv_state);
832*319251c5SThomas Zimmermann 	}
833*319251c5SThomas Zimmermann 
834*319251c5SThomas Zimmermann 	drm_dev_exit(idx);
835*319251c5SThomas Zimmermann out_drm_gem_fb_end_cpu_access:
836*319251c5SThomas Zimmermann 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
837*319251c5SThomas Zimmermann }
838*319251c5SThomas Zimmermann 
839*319251c5SThomas Zimmermann static void ofdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane,
840*319251c5SThomas Zimmermann 						      struct drm_atomic_state *state)
841*319251c5SThomas Zimmermann {
842*319251c5SThomas Zimmermann 	struct drm_device *dev = plane->dev;
843*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(dev);
844*319251c5SThomas Zimmermann 	struct iosys_map dst = odev->screen_base;
845*319251c5SThomas Zimmermann 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
846*319251c5SThomas Zimmermann 	void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
847*319251c5SThomas Zimmermann 	unsigned int dst_pitch = odev->pitch;
848*319251c5SThomas Zimmermann 	const struct drm_format_info *dst_format = odev->format;
849*319251c5SThomas Zimmermann 	struct drm_rect dst_clip;
850*319251c5SThomas Zimmermann 	unsigned long lines, linepixels, i;
851*319251c5SThomas Zimmermann 	int idx;
852*319251c5SThomas Zimmermann 
853*319251c5SThomas Zimmermann 	drm_rect_init(&dst_clip,
854*319251c5SThomas Zimmermann 		      plane_state->src_x >> 16, plane_state->src_y >> 16,
855*319251c5SThomas Zimmermann 		      plane_state->src_w >> 16, plane_state->src_h >> 16);
856*319251c5SThomas Zimmermann 
857*319251c5SThomas Zimmermann 	lines = drm_rect_height(&dst_clip);
858*319251c5SThomas Zimmermann 	linepixels = drm_rect_width(&dst_clip);
859*319251c5SThomas Zimmermann 
860*319251c5SThomas Zimmermann 	if (!drm_dev_enter(dev, &idx))
861*319251c5SThomas Zimmermann 		return;
862*319251c5SThomas Zimmermann 
863*319251c5SThomas Zimmermann 	/* Clear buffer to black if disabled */
864*319251c5SThomas Zimmermann 	dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
865*319251c5SThomas Zimmermann 	for (i = 0; i < lines; ++i) {
866*319251c5SThomas Zimmermann 		memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
867*319251c5SThomas Zimmermann 		dst_vmap += dst_pitch;
868*319251c5SThomas Zimmermann 	}
869*319251c5SThomas Zimmermann 
870*319251c5SThomas Zimmermann 	drm_dev_exit(idx);
871*319251c5SThomas Zimmermann }
872*319251c5SThomas Zimmermann 
873*319251c5SThomas Zimmermann static const struct drm_plane_helper_funcs ofdrm_primary_plane_helper_funcs = {
874*319251c5SThomas Zimmermann 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
875*319251c5SThomas Zimmermann 	.atomic_check = ofdrm_primary_plane_helper_atomic_check,
876*319251c5SThomas Zimmermann 	.atomic_update = ofdrm_primary_plane_helper_atomic_update,
877*319251c5SThomas Zimmermann 	.atomic_disable = ofdrm_primary_plane_helper_atomic_disable,
878*319251c5SThomas Zimmermann };
879*319251c5SThomas Zimmermann 
880*319251c5SThomas Zimmermann static const struct drm_plane_funcs ofdrm_primary_plane_funcs = {
881*319251c5SThomas Zimmermann 	.update_plane = drm_atomic_helper_update_plane,
882*319251c5SThomas Zimmermann 	.disable_plane = drm_atomic_helper_disable_plane,
883*319251c5SThomas Zimmermann 	.destroy = drm_plane_cleanup,
884*319251c5SThomas Zimmermann 	DRM_GEM_SHADOW_PLANE_FUNCS,
885*319251c5SThomas Zimmermann };
886*319251c5SThomas Zimmermann 
887*319251c5SThomas Zimmermann static enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc,
888*319251c5SThomas Zimmermann 							 const struct drm_display_mode *mode)
889*319251c5SThomas Zimmermann {
890*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev);
891*319251c5SThomas Zimmermann 
892*319251c5SThomas Zimmermann 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &odev->mode);
893*319251c5SThomas Zimmermann }
894*319251c5SThomas Zimmermann 
895*319251c5SThomas Zimmermann static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc,
896*319251c5SThomas Zimmermann 					  struct drm_atomic_state *new_state)
897*319251c5SThomas Zimmermann {
898*319251c5SThomas Zimmermann 	static const size_t gamma_lut_length = OFDRM_GAMMA_LUT_SIZE * sizeof(struct drm_color_lut);
899*319251c5SThomas Zimmermann 
900*319251c5SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
901*319251c5SThomas Zimmermann 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
902*319251c5SThomas Zimmermann 	int ret;
903*319251c5SThomas Zimmermann 
904*319251c5SThomas Zimmermann 	if (!new_crtc_state->enable)
905*319251c5SThomas Zimmermann 		return 0;
906*319251c5SThomas Zimmermann 
907*319251c5SThomas Zimmermann 	ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
908*319251c5SThomas Zimmermann 	if (ret)
909*319251c5SThomas Zimmermann 		return ret;
910*319251c5SThomas Zimmermann 
911*319251c5SThomas Zimmermann 	if (new_crtc_state->color_mgmt_changed) {
912*319251c5SThomas Zimmermann 		struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
913*319251c5SThomas Zimmermann 
914*319251c5SThomas Zimmermann 		if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
915*319251c5SThomas Zimmermann 			drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
916*319251c5SThomas Zimmermann 			return -EINVAL;
917*319251c5SThomas Zimmermann 		}
918*319251c5SThomas Zimmermann 	}
919*319251c5SThomas Zimmermann 
920*319251c5SThomas Zimmermann 	return 0;
921*319251c5SThomas Zimmermann }
922*319251c5SThomas Zimmermann 
923*319251c5SThomas Zimmermann static void ofdrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state)
924*319251c5SThomas Zimmermann {
925*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(crtc->dev);
926*319251c5SThomas Zimmermann 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
927*319251c5SThomas Zimmermann 	struct ofdrm_crtc_state *ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state);
928*319251c5SThomas Zimmermann 
929*319251c5SThomas Zimmermann 	if (crtc_state->enable && crtc_state->color_mgmt_changed) {
930*319251c5SThomas Zimmermann 		const struct drm_format_info *format = ofdrm_crtc_state->format;
931*319251c5SThomas Zimmermann 
932*319251c5SThomas Zimmermann 		if (crtc_state->gamma_lut)
933*319251c5SThomas Zimmermann 			ofdrm_device_set_gamma(odev, format, crtc_state->gamma_lut->data);
934*319251c5SThomas Zimmermann 		else
935*319251c5SThomas Zimmermann 			ofdrm_device_set_gamma_linear(odev, format);
936*319251c5SThomas Zimmermann 	}
937*319251c5SThomas Zimmermann }
938*319251c5SThomas Zimmermann 
939*319251c5SThomas Zimmermann /*
940*319251c5SThomas Zimmermann  * The CRTC is always enabled. Screen updates are performed by
941*319251c5SThomas Zimmermann  * the primary plane's atomic_update function. Disabling clears
942*319251c5SThomas Zimmermann  * the screen in the primary plane's atomic_disable function.
943*319251c5SThomas Zimmermann  */
944*319251c5SThomas Zimmermann static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = {
945*319251c5SThomas Zimmermann 	.mode_valid = ofdrm_crtc_helper_mode_valid,
946*319251c5SThomas Zimmermann 	.atomic_check = ofdrm_crtc_helper_atomic_check,
947*319251c5SThomas Zimmermann 	.atomic_flush = ofdrm_crtc_helper_atomic_flush,
948*319251c5SThomas Zimmermann };
949*319251c5SThomas Zimmermann 
950*319251c5SThomas Zimmermann static void ofdrm_crtc_reset(struct drm_crtc *crtc)
951*319251c5SThomas Zimmermann {
952*319251c5SThomas Zimmermann 	struct ofdrm_crtc_state *ofdrm_crtc_state =
953*319251c5SThomas Zimmermann 		kzalloc(sizeof(*ofdrm_crtc_state), GFP_KERNEL);
954*319251c5SThomas Zimmermann 
955*319251c5SThomas Zimmermann 	if (crtc->state)
956*319251c5SThomas Zimmermann 		ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc->state));
957*319251c5SThomas Zimmermann 
958*319251c5SThomas Zimmermann 	if (ofdrm_crtc_state)
959*319251c5SThomas Zimmermann 		__drm_atomic_helper_crtc_reset(crtc, &ofdrm_crtc_state->base);
960*319251c5SThomas Zimmermann 	else
961*319251c5SThomas Zimmermann 		__drm_atomic_helper_crtc_reset(crtc, NULL);
962*319251c5SThomas Zimmermann }
963*319251c5SThomas Zimmermann 
964*319251c5SThomas Zimmermann static struct drm_crtc_state *ofdrm_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
965*319251c5SThomas Zimmermann {
966*319251c5SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
967*319251c5SThomas Zimmermann 	struct drm_crtc_state *crtc_state = crtc->state;
968*319251c5SThomas Zimmermann 	struct ofdrm_crtc_state *new_ofdrm_crtc_state;
969*319251c5SThomas Zimmermann 	struct ofdrm_crtc_state *ofdrm_crtc_state;
970*319251c5SThomas Zimmermann 
971*319251c5SThomas Zimmermann 	if (drm_WARN_ON(dev, !crtc_state))
972*319251c5SThomas Zimmermann 		return NULL;
973*319251c5SThomas Zimmermann 
974*319251c5SThomas Zimmermann 	new_ofdrm_crtc_state = kzalloc(sizeof(*new_ofdrm_crtc_state), GFP_KERNEL);
975*319251c5SThomas Zimmermann 	if (!new_ofdrm_crtc_state)
976*319251c5SThomas Zimmermann 		return NULL;
977*319251c5SThomas Zimmermann 
978*319251c5SThomas Zimmermann 	ofdrm_crtc_state = to_ofdrm_crtc_state(crtc_state);
979*319251c5SThomas Zimmermann 
980*319251c5SThomas Zimmermann 	__drm_atomic_helper_crtc_duplicate_state(crtc, &new_ofdrm_crtc_state->base);
981*319251c5SThomas Zimmermann 	new_ofdrm_crtc_state->format = ofdrm_crtc_state->format;
982*319251c5SThomas Zimmermann 
983*319251c5SThomas Zimmermann 	return &new_ofdrm_crtc_state->base;
984*319251c5SThomas Zimmermann }
985*319251c5SThomas Zimmermann 
986*319251c5SThomas Zimmermann static void ofdrm_crtc_atomic_destroy_state(struct drm_crtc *crtc,
987*319251c5SThomas Zimmermann 					    struct drm_crtc_state *crtc_state)
988*319251c5SThomas Zimmermann {
989*319251c5SThomas Zimmermann 	ofdrm_crtc_state_destroy(to_ofdrm_crtc_state(crtc_state));
990*319251c5SThomas Zimmermann }
991*319251c5SThomas Zimmermann 
992*319251c5SThomas Zimmermann static const struct drm_crtc_funcs ofdrm_crtc_funcs = {
993*319251c5SThomas Zimmermann 	.reset = ofdrm_crtc_reset,
994*319251c5SThomas Zimmermann 	.destroy = drm_crtc_cleanup,
995*319251c5SThomas Zimmermann 	.set_config = drm_atomic_helper_set_config,
996*319251c5SThomas Zimmermann 	.page_flip = drm_atomic_helper_page_flip,
997*319251c5SThomas Zimmermann 	.atomic_duplicate_state = ofdrm_crtc_atomic_duplicate_state,
998*319251c5SThomas Zimmermann 	.atomic_destroy_state = ofdrm_crtc_atomic_destroy_state,
999*319251c5SThomas Zimmermann };
1000*319251c5SThomas Zimmermann 
1001*319251c5SThomas Zimmermann static const struct drm_encoder_funcs ofdrm_encoder_funcs = {
1002*319251c5SThomas Zimmermann 	.destroy = drm_encoder_cleanup,
1003*319251c5SThomas Zimmermann };
1004*319251c5SThomas Zimmermann 
1005*319251c5SThomas Zimmermann static int ofdrm_connector_helper_get_modes(struct drm_connector *connector)
1006*319251c5SThomas Zimmermann {
1007*319251c5SThomas Zimmermann 	struct ofdrm_device *odev = ofdrm_device_of_dev(connector->dev);
1008*319251c5SThomas Zimmermann 
1009*319251c5SThomas Zimmermann 	return drm_connector_helper_get_modes_fixed(connector, &odev->mode);
1010*319251c5SThomas Zimmermann }
1011*319251c5SThomas Zimmermann 
1012*319251c5SThomas Zimmermann static const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = {
1013*319251c5SThomas Zimmermann 	.get_modes = ofdrm_connector_helper_get_modes,
1014*319251c5SThomas Zimmermann };
1015*319251c5SThomas Zimmermann 
1016*319251c5SThomas Zimmermann static const struct drm_connector_funcs ofdrm_connector_funcs = {
1017*319251c5SThomas Zimmermann 	.reset = drm_atomic_helper_connector_reset,
1018*319251c5SThomas Zimmermann 	.fill_modes = drm_helper_probe_single_connector_modes,
1019*319251c5SThomas Zimmermann 	.destroy = drm_connector_cleanup,
1020*319251c5SThomas Zimmermann 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
1021*319251c5SThomas Zimmermann 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
1022*319251c5SThomas Zimmermann };
1023*319251c5SThomas Zimmermann 
1024*319251c5SThomas Zimmermann static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = {
1025*319251c5SThomas Zimmermann 	.fb_create = drm_gem_fb_create_with_dirty,
1026*319251c5SThomas Zimmermann 	.atomic_check = drm_atomic_helper_check,
1027*319251c5SThomas Zimmermann 	.atomic_commit = drm_atomic_helper_commit,
1028*319251c5SThomas Zimmermann };
1029*319251c5SThomas Zimmermann 
1030*319251c5SThomas Zimmermann /*
1031*319251c5SThomas Zimmermann  * Init / Cleanup
1032*319251c5SThomas Zimmermann  */
1033*319251c5SThomas Zimmermann 
1034*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_unknown_device_funcs = {
1035*319251c5SThomas Zimmermann };
1036*319251c5SThomas Zimmermann 
1037*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_mach64_device_funcs = {
1038*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_mach64_cmap_ioremap,
1039*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_mach64_cmap_write,
1040*319251c5SThomas Zimmermann };
1041*319251c5SThomas Zimmermann 
1042*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_rage128_device_funcs = {
1043*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_rage128_cmap_ioremap,
1044*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_rage128_cmap_write,
1045*319251c5SThomas Zimmermann };
1046*319251c5SThomas Zimmermann 
1047*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_rage_m3a_device_funcs = {
1048*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_rage_m3a_cmap_ioremap,
1049*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_rage_m3a_cmap_write,
1050*319251c5SThomas Zimmermann };
1051*319251c5SThomas Zimmermann 
1052*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_rage_m3b_device_funcs = {
1053*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_rage_m3b_cmap_ioremap,
1054*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_rage_m3b_cmap_write,
1055*319251c5SThomas Zimmermann };
1056*319251c5SThomas Zimmermann 
1057*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_radeon_device_funcs = {
1058*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_radeon_cmap_ioremap,
1059*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_rage128_cmap_write, /* same as Rage128 */
1060*319251c5SThomas Zimmermann };
1061*319251c5SThomas Zimmermann 
1062*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_gxt2000_device_funcs = {
1063*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_gxt2000_cmap_ioremap,
1064*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_gxt2000_cmap_write,
1065*319251c5SThomas Zimmermann };
1066*319251c5SThomas Zimmermann 
1067*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_avivo_device_funcs = {
1068*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_avivo_cmap_ioremap,
1069*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_avivo_cmap_write,
1070*319251c5SThomas Zimmermann };
1071*319251c5SThomas Zimmermann 
1072*319251c5SThomas Zimmermann static const struct ofdrm_device_funcs ofdrm_qemu_device_funcs = {
1073*319251c5SThomas Zimmermann 	.cmap_ioremap = ofdrm_qemu_cmap_ioremap,
1074*319251c5SThomas Zimmermann 	.cmap_write = ofdrm_qemu_cmap_write,
1075*319251c5SThomas Zimmermann };
1076*319251c5SThomas Zimmermann 
1077*319251c5SThomas Zimmermann static struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height)
1078*319251c5SThomas Zimmermann {
1079*319251c5SThomas Zimmermann 	/*
1080*319251c5SThomas Zimmermann 	 * Assume a monitor resolution of 96 dpi to
1081*319251c5SThomas Zimmermann 	 * get a somewhat reasonable screen size.
1082*319251c5SThomas Zimmermann 	 */
1083*319251c5SThomas Zimmermann 	const struct drm_display_mode mode = {
1084*319251c5SThomas Zimmermann 		DRM_MODE_INIT(60, width, height,
1085*319251c5SThomas Zimmermann 			      DRM_MODE_RES_MM(width, 96ul),
1086*319251c5SThomas Zimmermann 			      DRM_MODE_RES_MM(height, 96ul))
1087*319251c5SThomas Zimmermann 	};
1088*319251c5SThomas Zimmermann 
1089*319251c5SThomas Zimmermann 	return mode;
1090*319251c5SThomas Zimmermann }
1091*319251c5SThomas Zimmermann 
1092*319251c5SThomas Zimmermann static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
1093*319251c5SThomas Zimmermann 						struct platform_device *pdev)
1094*319251c5SThomas Zimmermann {
1095*319251c5SThomas Zimmermann 	struct device_node *of_node = pdev->dev.of_node;
1096*319251c5SThomas Zimmermann 	struct ofdrm_device *odev;
1097*319251c5SThomas Zimmermann 	struct drm_device *dev;
1098*319251c5SThomas Zimmermann 	enum ofdrm_model model;
1099*319251c5SThomas Zimmermann 	bool big_endian;
1100*319251c5SThomas Zimmermann 	int width, height, depth, linebytes;
1101*319251c5SThomas Zimmermann 	const struct drm_format_info *format;
1102*319251c5SThomas Zimmermann 	u64 address;
1103*319251c5SThomas Zimmermann 	resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize;
1104*319251c5SThomas Zimmermann 	struct resource *res, *mem;
1105*319251c5SThomas Zimmermann 	void __iomem *screen_base;
1106*319251c5SThomas Zimmermann 	struct drm_plane *primary_plane;
1107*319251c5SThomas Zimmermann 	struct drm_crtc *crtc;
1108*319251c5SThomas Zimmermann 	struct drm_encoder *encoder;
1109*319251c5SThomas Zimmermann 	struct drm_connector *connector;
1110*319251c5SThomas Zimmermann 	unsigned long max_width, max_height;
1111*319251c5SThomas Zimmermann 	size_t nformats;
1112*319251c5SThomas Zimmermann 	int ret;
1113*319251c5SThomas Zimmermann 
1114*319251c5SThomas Zimmermann 	odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev);
1115*319251c5SThomas Zimmermann 	if (IS_ERR(odev))
1116*319251c5SThomas Zimmermann 		return ERR_CAST(odev);
1117*319251c5SThomas Zimmermann 	dev = &odev->dev;
1118*319251c5SThomas Zimmermann 	platform_set_drvdata(pdev, dev);
1119*319251c5SThomas Zimmermann 
1120*319251c5SThomas Zimmermann 	ret = ofdrm_device_init_pci(odev);
1121*319251c5SThomas Zimmermann 	if (ret)
1122*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1123*319251c5SThomas Zimmermann 
1124*319251c5SThomas Zimmermann 	/*
1125*319251c5SThomas Zimmermann 	 * OF display-node settings
1126*319251c5SThomas Zimmermann 	 */
1127*319251c5SThomas Zimmermann 
1128*319251c5SThomas Zimmermann 	model = display_get_model_of(dev, of_node);
1129*319251c5SThomas Zimmermann 	drm_dbg(dev, "detected model %d\n", model);
1130*319251c5SThomas Zimmermann 
1131*319251c5SThomas Zimmermann 	switch (model) {
1132*319251c5SThomas Zimmermann 	case OFDRM_MODEL_UNKNOWN:
1133*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_unknown_device_funcs;
1134*319251c5SThomas Zimmermann 		break;
1135*319251c5SThomas Zimmermann 	case OFDRM_MODEL_MACH64:
1136*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_mach64_device_funcs;
1137*319251c5SThomas Zimmermann 		break;
1138*319251c5SThomas Zimmermann 	case OFDRM_MODEL_RAGE128:
1139*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_rage128_device_funcs;
1140*319251c5SThomas Zimmermann 		break;
1141*319251c5SThomas Zimmermann 	case OFDRM_MODEL_RAGE_M3A:
1142*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_rage_m3a_device_funcs;
1143*319251c5SThomas Zimmermann 		break;
1144*319251c5SThomas Zimmermann 	case OFDRM_MODEL_RAGE_M3B:
1145*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_rage_m3b_device_funcs;
1146*319251c5SThomas Zimmermann 		break;
1147*319251c5SThomas Zimmermann 	case OFDRM_MODEL_RADEON:
1148*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_radeon_device_funcs;
1149*319251c5SThomas Zimmermann 		break;
1150*319251c5SThomas Zimmermann 	case OFDRM_MODEL_GXT2000:
1151*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_gxt2000_device_funcs;
1152*319251c5SThomas Zimmermann 		break;
1153*319251c5SThomas Zimmermann 	case OFDRM_MODEL_AVIVO:
1154*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_avivo_device_funcs;
1155*319251c5SThomas Zimmermann 		break;
1156*319251c5SThomas Zimmermann 	case OFDRM_MODEL_QEMU:
1157*319251c5SThomas Zimmermann 		odev->funcs = &ofdrm_qemu_device_funcs;
1158*319251c5SThomas Zimmermann 		break;
1159*319251c5SThomas Zimmermann 	}
1160*319251c5SThomas Zimmermann 
1161*319251c5SThomas Zimmermann 	big_endian = display_get_big_endian_of(dev, of_node);
1162*319251c5SThomas Zimmermann 
1163*319251c5SThomas Zimmermann 	width = display_get_width_of(dev, of_node);
1164*319251c5SThomas Zimmermann 	if (width < 0)
1165*319251c5SThomas Zimmermann 		return ERR_PTR(width);
1166*319251c5SThomas Zimmermann 	height = display_get_height_of(dev, of_node);
1167*319251c5SThomas Zimmermann 	if (height < 0)
1168*319251c5SThomas Zimmermann 		return ERR_PTR(height);
1169*319251c5SThomas Zimmermann 	depth = display_get_depth_of(dev, of_node);
1170*319251c5SThomas Zimmermann 	if (depth < 0)
1171*319251c5SThomas Zimmermann 		return ERR_PTR(depth);
1172*319251c5SThomas Zimmermann 	linebytes = display_get_linebytes_of(dev, of_node);
1173*319251c5SThomas Zimmermann 	if (linebytes < 0)
1174*319251c5SThomas Zimmermann 		return ERR_PTR(linebytes);
1175*319251c5SThomas Zimmermann 
1176*319251c5SThomas Zimmermann 	format = display_get_validated_format(dev, depth, big_endian);
1177*319251c5SThomas Zimmermann 	if (IS_ERR(format))
1178*319251c5SThomas Zimmermann 		return ERR_CAST(format);
1179*319251c5SThomas Zimmermann 	if (!linebytes) {
1180*319251c5SThomas Zimmermann 		linebytes = drm_format_info_min_pitch(format, 0, width);
1181*319251c5SThomas Zimmermann 		if (drm_WARN_ON(dev, !linebytes))
1182*319251c5SThomas Zimmermann 			return ERR_PTR(-EINVAL);
1183*319251c5SThomas Zimmermann 	}
1184*319251c5SThomas Zimmermann 
1185*319251c5SThomas Zimmermann 	fb_size = linebytes * height;
1186*319251c5SThomas Zimmermann 
1187*319251c5SThomas Zimmermann 	/*
1188*319251c5SThomas Zimmermann 	 * Try to figure out the address of the framebuffer. Unfortunately, Open
1189*319251c5SThomas Zimmermann 	 * Firmware doesn't provide a standard way to do so. All we can do is a
1190*319251c5SThomas Zimmermann 	 * dodgy heuristic that happens to work in practice.
1191*319251c5SThomas Zimmermann 	 *
1192*319251c5SThomas Zimmermann 	 * On most machines, the "address" property contains what we need, though
1193*319251c5SThomas Zimmermann 	 * not on Matrox cards found in IBM machines. What appears to give good
1194*319251c5SThomas Zimmermann 	 * results is to go through the PCI ranges and pick one that encloses the
1195*319251c5SThomas Zimmermann 	 * "address" property. If none match, we pick the largest.
1196*319251c5SThomas Zimmermann 	 */
1197*319251c5SThomas Zimmermann 	address = display_get_address_of(dev, of_node);
1198*319251c5SThomas Zimmermann 	if (address != OF_BAD_ADDR) {
1199*319251c5SThomas Zimmermann 		struct resource fb_res = DEFINE_RES_MEM(address, fb_size);
1200*319251c5SThomas Zimmermann 
1201*319251c5SThomas Zimmermann 		res = ofdrm_find_fb_resource(odev, &fb_res);
1202*319251c5SThomas Zimmermann 		if (!res)
1203*319251c5SThomas Zimmermann 			return ERR_PTR(-EINVAL);
1204*319251c5SThomas Zimmermann 		if (resource_contains(res, &fb_res))
1205*319251c5SThomas Zimmermann 			fb_base = address;
1206*319251c5SThomas Zimmermann 		else
1207*319251c5SThomas Zimmermann 			fb_base = res->start;
1208*319251c5SThomas Zimmermann 	} else {
1209*319251c5SThomas Zimmermann 		struct resource fb_res = DEFINE_RES_MEM(0u, fb_size);
1210*319251c5SThomas Zimmermann 
1211*319251c5SThomas Zimmermann 		res = ofdrm_find_fb_resource(odev, &fb_res);
1212*319251c5SThomas Zimmermann 		if (!res)
1213*319251c5SThomas Zimmermann 			return ERR_PTR(-EINVAL);
1214*319251c5SThomas Zimmermann 		fb_base = res->start;
1215*319251c5SThomas Zimmermann 	}
1216*319251c5SThomas Zimmermann 
1217*319251c5SThomas Zimmermann 	/*
1218*319251c5SThomas Zimmermann 	 * I/O resources
1219*319251c5SThomas Zimmermann 	 */
1220*319251c5SThomas Zimmermann 
1221*319251c5SThomas Zimmermann 	fb_pgbase = round_down(fb_base, PAGE_SIZE);
1222*319251c5SThomas Zimmermann 	fb_pgsize = fb_base - fb_pgbase + round_up(fb_size, PAGE_SIZE);
1223*319251c5SThomas Zimmermann 
1224*319251c5SThomas Zimmermann 	ret = devm_aperture_acquire_for_platform_device(pdev, fb_pgbase, fb_pgsize);
1225*319251c5SThomas Zimmermann 	if (ret) {
1226*319251c5SThomas Zimmermann 		drm_err(dev, "could not acquire memory range %pr: error %d\n", &res, ret);
1227*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1228*319251c5SThomas Zimmermann 	}
1229*319251c5SThomas Zimmermann 
1230*319251c5SThomas Zimmermann 	mem = devm_request_mem_region(&pdev->dev, fb_pgbase, fb_pgsize, drv->name);
1231*319251c5SThomas Zimmermann 	if (!mem) {
1232*319251c5SThomas Zimmermann 		drm_warn(dev, "could not acquire memory region %pr\n", &res);
1233*319251c5SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
1234*319251c5SThomas Zimmermann 	}
1235*319251c5SThomas Zimmermann 
1236*319251c5SThomas Zimmermann 	screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
1237*319251c5SThomas Zimmermann 	if (!screen_base)
1238*319251c5SThomas Zimmermann 		return ERR_PTR(-ENOMEM);
1239*319251c5SThomas Zimmermann 
1240*319251c5SThomas Zimmermann 	if (odev->funcs->cmap_ioremap) {
1241*319251c5SThomas Zimmermann 		void __iomem *cmap_base = odev->funcs->cmap_ioremap(odev, of_node, fb_base);
1242*319251c5SThomas Zimmermann 
1243*319251c5SThomas Zimmermann 		if (IS_ERR(cmap_base)) {
1244*319251c5SThomas Zimmermann 			/* Don't fail; continue without colormap */
1245*319251c5SThomas Zimmermann 			drm_warn(dev, "could not find colormap: error %ld\n", PTR_ERR(cmap_base));
1246*319251c5SThomas Zimmermann 		} else {
1247*319251c5SThomas Zimmermann 			odev->cmap_base = cmap_base;
1248*319251c5SThomas Zimmermann 		}
1249*319251c5SThomas Zimmermann 	}
1250*319251c5SThomas Zimmermann 
1251*319251c5SThomas Zimmermann 	/*
1252*319251c5SThomas Zimmermann 	 * Firmware framebuffer
1253*319251c5SThomas Zimmermann 	 */
1254*319251c5SThomas Zimmermann 
1255*319251c5SThomas Zimmermann 	iosys_map_set_vaddr_iomem(&odev->screen_base, screen_base);
1256*319251c5SThomas Zimmermann 	odev->mode = ofdrm_mode(width, height);
1257*319251c5SThomas Zimmermann 	odev->format = format;
1258*319251c5SThomas Zimmermann 	odev->pitch = linebytes;
1259*319251c5SThomas Zimmermann 
1260*319251c5SThomas Zimmermann 	drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&odev->mode));
1261*319251c5SThomas Zimmermann 	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n",
1262*319251c5SThomas Zimmermann 		&format->format, width, height, linebytes);
1263*319251c5SThomas Zimmermann 
1264*319251c5SThomas Zimmermann 	/*
1265*319251c5SThomas Zimmermann 	 * Mode-setting pipeline
1266*319251c5SThomas Zimmermann 	 */
1267*319251c5SThomas Zimmermann 
1268*319251c5SThomas Zimmermann 	ret = drmm_mode_config_init(dev);
1269*319251c5SThomas Zimmermann 	if (ret)
1270*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1271*319251c5SThomas Zimmermann 
1272*319251c5SThomas Zimmermann 	max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH);
1273*319251c5SThomas Zimmermann 	max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
1274*319251c5SThomas Zimmermann 
1275*319251c5SThomas Zimmermann 	dev->mode_config.min_width = width;
1276*319251c5SThomas Zimmermann 	dev->mode_config.max_width = max_width;
1277*319251c5SThomas Zimmermann 	dev->mode_config.min_height = height;
1278*319251c5SThomas Zimmermann 	dev->mode_config.max_height = max_height;
1279*319251c5SThomas Zimmermann 	dev->mode_config.funcs = &ofdrm_mode_config_funcs;
1280*319251c5SThomas Zimmermann 	dev->mode_config.preferred_depth = format->depth;
1281*319251c5SThomas Zimmermann 	dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
1282*319251c5SThomas Zimmermann 
1283*319251c5SThomas Zimmermann 	/* Primary plane */
1284*319251c5SThomas Zimmermann 
1285*319251c5SThomas Zimmermann 	nformats = drm_fb_build_fourcc_list(dev, &format->format, 1,
1286*319251c5SThomas Zimmermann 					    odev->formats, ARRAY_SIZE(odev->formats));
1287*319251c5SThomas Zimmermann 
1288*319251c5SThomas Zimmermann 	primary_plane = &odev->primary_plane;
1289*319251c5SThomas Zimmermann 	ret = drm_universal_plane_init(dev, primary_plane, 0, &ofdrm_primary_plane_funcs,
1290*319251c5SThomas Zimmermann 				       odev->formats, nformats,
1291*319251c5SThomas Zimmermann 				       ofdrm_primary_plane_format_modifiers,
1292*319251c5SThomas Zimmermann 				       DRM_PLANE_TYPE_PRIMARY, NULL);
1293*319251c5SThomas Zimmermann 	if (ret)
1294*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1295*319251c5SThomas Zimmermann 	drm_plane_helper_add(primary_plane, &ofdrm_primary_plane_helper_funcs);
1296*319251c5SThomas Zimmermann 	drm_plane_enable_fb_damage_clips(primary_plane);
1297*319251c5SThomas Zimmermann 
1298*319251c5SThomas Zimmermann 	/* CRTC */
1299*319251c5SThomas Zimmermann 
1300*319251c5SThomas Zimmermann 	crtc = &odev->crtc;
1301*319251c5SThomas Zimmermann 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
1302*319251c5SThomas Zimmermann 					&ofdrm_crtc_funcs, NULL);
1303*319251c5SThomas Zimmermann 	if (ret)
1304*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1305*319251c5SThomas Zimmermann 	drm_crtc_helper_add(crtc, &ofdrm_crtc_helper_funcs);
1306*319251c5SThomas Zimmermann 
1307*319251c5SThomas Zimmermann 	if (odev->cmap_base) {
1308*319251c5SThomas Zimmermann 		drm_mode_crtc_set_gamma_size(crtc, OFDRM_GAMMA_LUT_SIZE);
1309*319251c5SThomas Zimmermann 		drm_crtc_enable_color_mgmt(crtc, 0, false, OFDRM_GAMMA_LUT_SIZE);
1310*319251c5SThomas Zimmermann 	}
1311*319251c5SThomas Zimmermann 
1312*319251c5SThomas Zimmermann 	/* Encoder */
1313*319251c5SThomas Zimmermann 
1314*319251c5SThomas Zimmermann 	encoder = &odev->encoder;
1315*319251c5SThomas Zimmermann 	ret = drm_encoder_init(dev, encoder, &ofdrm_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
1316*319251c5SThomas Zimmermann 	if (ret)
1317*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1318*319251c5SThomas Zimmermann 	encoder->possible_crtcs = drm_crtc_mask(crtc);
1319*319251c5SThomas Zimmermann 
1320*319251c5SThomas Zimmermann 	/* Connector */
1321*319251c5SThomas Zimmermann 
1322*319251c5SThomas Zimmermann 	connector = &odev->connector;
1323*319251c5SThomas Zimmermann 	ret = drm_connector_init(dev, connector, &ofdrm_connector_funcs,
1324*319251c5SThomas Zimmermann 				 DRM_MODE_CONNECTOR_Unknown);
1325*319251c5SThomas Zimmermann 	if (ret)
1326*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1327*319251c5SThomas Zimmermann 	drm_connector_helper_add(connector, &ofdrm_connector_helper_funcs);
1328*319251c5SThomas Zimmermann 	drm_connector_set_panel_orientation_with_quirk(connector,
1329*319251c5SThomas Zimmermann 						       DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
1330*319251c5SThomas Zimmermann 						       width, height);
1331*319251c5SThomas Zimmermann 
1332*319251c5SThomas Zimmermann 	ret = drm_connector_attach_encoder(connector, encoder);
1333*319251c5SThomas Zimmermann 	if (ret)
1334*319251c5SThomas Zimmermann 		return ERR_PTR(ret);
1335*319251c5SThomas Zimmermann 
1336*319251c5SThomas Zimmermann 	drm_mode_config_reset(dev);
1337*319251c5SThomas Zimmermann 
1338*319251c5SThomas Zimmermann 	return odev;
1339*319251c5SThomas Zimmermann }
1340*319251c5SThomas Zimmermann 
1341*319251c5SThomas Zimmermann /*
1342*319251c5SThomas Zimmermann  * DRM driver
1343*319251c5SThomas Zimmermann  */
1344*319251c5SThomas Zimmermann 
1345*319251c5SThomas Zimmermann DEFINE_DRM_GEM_FOPS(ofdrm_fops);
1346*319251c5SThomas Zimmermann 
1347*319251c5SThomas Zimmermann static struct drm_driver ofdrm_driver = {
1348*319251c5SThomas Zimmermann 	DRM_GEM_SHMEM_DRIVER_OPS,
1349*319251c5SThomas Zimmermann 	DRM_FBDEV_SHMEM_DRIVER_OPS,
1350*319251c5SThomas Zimmermann 	.name			= DRIVER_NAME,
1351*319251c5SThomas Zimmermann 	.desc			= DRIVER_DESC,
1352*319251c5SThomas Zimmermann 	.major			= DRIVER_MAJOR,
1353*319251c5SThomas Zimmermann 	.minor			= DRIVER_MINOR,
1354*319251c5SThomas Zimmermann 	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
1355*319251c5SThomas Zimmermann 	.fops			= &ofdrm_fops,
1356*319251c5SThomas Zimmermann };
1357*319251c5SThomas Zimmermann 
1358*319251c5SThomas Zimmermann /*
1359*319251c5SThomas Zimmermann  * Platform driver
1360*319251c5SThomas Zimmermann  */
1361*319251c5SThomas Zimmermann 
1362*319251c5SThomas Zimmermann static int ofdrm_probe(struct platform_device *pdev)
1363*319251c5SThomas Zimmermann {
1364*319251c5SThomas Zimmermann 	struct ofdrm_device *odev;
1365*319251c5SThomas Zimmermann 	struct drm_device *dev;
1366*319251c5SThomas Zimmermann 	int ret;
1367*319251c5SThomas Zimmermann 
1368*319251c5SThomas Zimmermann 	odev = ofdrm_device_create(&ofdrm_driver, pdev);
1369*319251c5SThomas Zimmermann 	if (IS_ERR(odev))
1370*319251c5SThomas Zimmermann 		return PTR_ERR(odev);
1371*319251c5SThomas Zimmermann 	dev = &odev->dev;
1372*319251c5SThomas Zimmermann 
1373*319251c5SThomas Zimmermann 	ret = drm_dev_register(dev, 0);
1374*319251c5SThomas Zimmermann 	if (ret)
1375*319251c5SThomas Zimmermann 		return ret;
1376*319251c5SThomas Zimmermann 
1377*319251c5SThomas Zimmermann 	drm_client_setup(dev, odev->format);
1378*319251c5SThomas Zimmermann 
1379*319251c5SThomas Zimmermann 	return 0;
1380*319251c5SThomas Zimmermann }
1381*319251c5SThomas Zimmermann 
1382*319251c5SThomas Zimmermann static void ofdrm_remove(struct platform_device *pdev)
1383*319251c5SThomas Zimmermann {
1384*319251c5SThomas Zimmermann 	struct drm_device *dev = platform_get_drvdata(pdev);
1385*319251c5SThomas Zimmermann 
1386*319251c5SThomas Zimmermann 	drm_dev_unplug(dev);
1387*319251c5SThomas Zimmermann }
1388*319251c5SThomas Zimmermann 
1389*319251c5SThomas Zimmermann static const struct of_device_id ofdrm_of_match_display[] = {
1390*319251c5SThomas Zimmermann 	{ .compatible = "display", },
1391*319251c5SThomas Zimmermann 	{ },
1392*319251c5SThomas Zimmermann };
1393*319251c5SThomas Zimmermann MODULE_DEVICE_TABLE(of, ofdrm_of_match_display);
1394*319251c5SThomas Zimmermann 
1395*319251c5SThomas Zimmermann static struct platform_driver ofdrm_platform_driver = {
1396*319251c5SThomas Zimmermann 	.driver = {
1397*319251c5SThomas Zimmermann 		.name = "of-display",
1398*319251c5SThomas Zimmermann 		.of_match_table = ofdrm_of_match_display,
1399*319251c5SThomas Zimmermann 	},
1400*319251c5SThomas Zimmermann 	.probe = ofdrm_probe,
1401*319251c5SThomas Zimmermann 	.remove = ofdrm_remove,
1402*319251c5SThomas Zimmermann };
1403*319251c5SThomas Zimmermann 
1404*319251c5SThomas Zimmermann module_platform_driver(ofdrm_platform_driver);
1405*319251c5SThomas Zimmermann 
1406*319251c5SThomas Zimmermann MODULE_DESCRIPTION(DRIVER_DESC);
1407*319251c5SThomas Zimmermann MODULE_LICENSE("GPL");
1408