1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Chris Wilson
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it either under the terms of the GNU Lesser General Public
7  * License version 2.1 as published by the Free Software Foundation
8  * (the "LGPL") or, at your option, under the terms of the Mozilla
9  * Public License Version 1.1 (the "MPL"). If you do not alter this
10  * notice, a recipient may use your version of this file under either
11  * the MPL or the LGPL.
12  *
13  * You should have received a copy of the LGPL along with this library
14  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16  * You should have received a copy of the MPL along with this library
17  * in the file COPYING-MPL-1.1
18  *
19  * The contents of this file are subject to the Mozilla Public License
20  * Version 1.1 (the "License"); you may not use this file except in
21  * compliance with the License. You may obtain a copy of the License at
22  * http://www.mozilla.org/MPL/
23  *
24  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26  * the specific language governing rights and limitations.
27  *
28  */
29 
30 #include "cairoint.h"
31 
32 #include "cairo-drm-private.h"
33 #include "cairo-drm-radeon-private.h"
34 
35 #include "cairo-default-context-private.h"
36 #include "cairo-error-private.h"
37 #include "cairo-image-surface-private.h"
38 
39 #include <stddef.h>
40 #include <inttypes.h>		/* workaround for broken <drm/radeon_drm.h> */
41 #include <drm/radeon_drm.h>
42 
43 /* Basic stub surface for radeon chipsets */
44 
45 #define MAX_SIZE 2048
46 
47 typedef struct _radeon_surface {
48     cairo_drm_surface_t base;
49 } radeon_surface_t;
50 
51 static inline radeon_device_t *
to_radeon_device(cairo_device_t * device)52 to_radeon_device (cairo_device_t *device)
53 {
54     return (radeon_device_t *) device;
55 }
56 
57 static inline radeon_bo_t *
to_radeon_bo(cairo_drm_bo_t * bo)58 to_radeon_bo (cairo_drm_bo_t *bo)
59 {
60     return (radeon_bo_t *) bo;
61 }
62 
63 static cairo_surface_t *
radeon_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)64 radeon_surface_create_similar (void			*abstract_surface,
65 			      cairo_content_t		 content,
66 			      int			 width,
67 			      int			 height)
68 {
69     return cairo_image_surface_create (_cairo_format_from_content (content),
70 				       width, height);
71 }
72 
73 static cairo_status_t
radeon_surface_finish(void * abstract_surface)74 radeon_surface_finish (void *abstract_surface)
75 {
76     radeon_surface_t *surface = abstract_surface;
77 
78     return _cairo_drm_surface_finish (&surface->base);
79 }
80 
81 static cairo_status_t
radeon_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)82 radeon_surface_acquire_source_image (void *abstract_surface,
83 				     cairo_image_surface_t **image_out,
84 				     void **image_extra)
85 {
86     radeon_surface_t *surface = abstract_surface;
87     cairo_surface_t *image;
88     cairo_status_t status;
89 
90     /* XXX batch flush */
91 
92     if (surface->base.fallback != NULL) {
93 	image = surface->base.fallback;
94 	goto DONE;
95     }
96 
97     image = _cairo_surface_has_snapshot (&surface->base.base,
98 	                                 &_cairo_image_surface_backend);
99     if (image != NULL)
100 	goto DONE;
101 
102     if (surface->base.base.backend->flush != NULL) {
103 	status = surface->base.base.backend->flush (surface);
104 	if (unlikely (status))
105 	    return status;
106     }
107 
108     image = radeon_bo_get_image (to_radeon_device (surface->base.base.device),
109 				to_radeon_bo (surface->base.bo),
110 				&surface->base);
111     status = image->status;
112     if (unlikely (status))
113 	return status;
114 
115     _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy);
116 
117 DONE:
118     *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
119     *image_extra = NULL;
120     return CAIRO_STATUS_SUCCESS;
121 }
122 
123 static void
radeon_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)124 radeon_surface_release_source_image (void *abstract_surface,
125 				     cairo_image_surface_t *image,
126 				     void *image_extra)
127 {
128     cairo_surface_destroy (&image->base);
129 }
130 
131 static cairo_surface_t *
radeon_surface_map_to_image(radeon_surface_t * surface)132 radeon_surface_map_to_image (radeon_surface_t *surface)
133 {
134     if (surface->base.fallback == NULL) {
135 	cairo_surface_t *image;
136 	cairo_status_t status;
137 	void *ptr;
138 
139 	if (surface->base.base.backend->flush != NULL) {
140 	    status = surface->base.base.backend->flush (surface);
141 	    if (unlikely (status))
142 		return _cairo_surface_create_in_error (status);
143 	}
144 
145 	ptr = radeon_bo_map (to_radeon_device (surface->base.base.device),
146 			    to_radeon_bo (surface->base.bo));
147 	if (unlikely (ptr == NULL))
148 	    return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
149 
150 	image = cairo_image_surface_create_for_data (ptr,
151 						     surface->base.format,
152 						     surface->base.width,
153 						     surface->base.height,
154 						     surface->base.stride);
155 	if (unlikely (image->status)) {
156 	    radeon_bo_unmap (to_radeon_bo (surface->base.bo));
157 	    return image;
158 	}
159 
160 	surface->base.fallback = image;
161     }
162 
163     return surface->base.fallback;
164 }
165 
166 static cairo_status_t
radeon_surface_flush(void * abstract_surface,unsigned flags)167 radeon_surface_flush (void *abstract_surface,
168 		      unsigned flags)
169 {
170     radeon_surface_t *surface = abstract_surface;
171     cairo_status_t status;
172 
173     if (flags)
174 	return CAIRO_STATUS_SUCCESS;
175 
176     if (surface->base.fallback == NULL)
177 	return CAIRO_STATUS_SUCCESS;
178 
179     /* kill any outstanding maps */
180     cairo_surface_finish (surface->base.fallback);
181 
182     status = cairo_surface_status (surface->base.fallback);
183     cairo_surface_destroy (surface->base.fallback);
184     surface->base.fallback = NULL;
185 
186     radeon_bo_unmap (to_radeon_bo (surface->base.bo));
187 
188     return status;
189 }
190 
191 static cairo_int_status_t
radeon_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_clip_t * clip)192 radeon_surface_paint (void *abstract_surface,
193 		     cairo_operator_t		 op,
194 		     const cairo_pattern_t	*source,
195 		     cairo_clip_t		*clip)
196 {
197     return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface),
198 				 op, source, clip);
199 }
200 
201 static cairo_int_status_t
radeon_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,cairo_clip_t * clip)202 radeon_surface_mask (void			*abstract_surface,
203 		    cairo_operator_t		 op,
204 		    const cairo_pattern_t	*source,
205 		    const cairo_pattern_t	*mask,
206 		    cairo_clip_t		*clip)
207 {
208     return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface),
209 				op, source, mask, clip);
210 }
211 
212 static cairo_int_status_t
radeon_surface_stroke(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_path_fixed_t * path,const cairo_stroke_style_t * stroke_style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,cairo_clip_t * clip)213 radeon_surface_stroke (void			*abstract_surface,
214 		      cairo_operator_t		 op,
215 		      const cairo_pattern_t	*source,
216 		      cairo_path_fixed_t	*path,
217 		      const cairo_stroke_style_t	*stroke_style,
218 		      const cairo_matrix_t		*ctm,
219 		      const cairo_matrix_t		*ctm_inverse,
220 		      double			 tolerance,
221 		      cairo_antialias_t		 antialias,
222 		      cairo_clip_t		*clip)
223 {
224     return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface),
225 				  op, source, path, stroke_style, ctm, ctm_inverse,
226 				  tolerance, antialias, clip);
227 }
228 
229 static cairo_int_status_t
radeon_surface_fill(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,cairo_clip_t * clip)230 radeon_surface_fill (void			*abstract_surface,
231 		    cairo_operator_t		 op,
232 		    const cairo_pattern_t	*source,
233 		    cairo_path_fixed_t		*path,
234 		    cairo_fill_rule_t		 fill_rule,
235 		    double			 tolerance,
236 		    cairo_antialias_t		 antialias,
237 		    cairo_clip_t		*clip)
238 {
239     return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface),
240 				op, source, path, fill_rule,
241 				tolerance, antialias, clip);
242 }
243 
244 static cairo_int_status_t
radeon_surface_glyphs(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_glyph_t * glyphs,int num_glyphs,cairo_scaled_font_t * scaled_font,cairo_clip_t * clip,int * num_remaining)245 radeon_surface_glyphs (void			*abstract_surface,
246 		      cairo_operator_t		 op,
247 		      const cairo_pattern_t	*source,
248 		      cairo_glyph_t		*glyphs,
249 		      int			 num_glyphs,
250 		      cairo_scaled_font_t	*scaled_font,
251 		      cairo_clip_t		*clip,
252 		      int *num_remaining)
253 {
254     *num_remaining = 0;
255     return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface),
256 					    op, source,
257 					    NULL, 0,
258 					    glyphs, num_glyphs,
259 					    NULL, 0, 0,
260 					    scaled_font, clip);
261 }
262 
263 static const cairo_surface_backend_t radeon_surface_backend = {
264     CAIRO_SURFACE_TYPE_DRM,
265     _cairo_default_context_create,
266 
267     radeon_surface_create_similar,
268     radeon_surface_finish,
269 
270     NULL,
271     radeon_surface_acquire_source_image,
272     radeon_surface_release_source_image,
273 
274     NULL, NULL, NULL,
275     NULL, /* composite */
276     NULL, /* fill */
277     NULL, /* trapezoids */
278     NULL, /* span */
279     NULL, /* check-span */
280 
281     NULL, /* copy_page */
282     NULL, /* show_page */
283     _cairo_drm_surface_get_extents,
284     NULL, /* old-glyphs */
285     _cairo_drm_surface_get_font_options,
286 
287     radeon_surface_flush,
288     NULL, /* mark dirty */
289     NULL, NULL, /* font/glyph fini */
290 
291     radeon_surface_paint,
292     radeon_surface_mask,
293     radeon_surface_stroke,
294     radeon_surface_fill,
295     radeon_surface_glyphs,
296 };
297 
298 static void
radeon_surface_init(radeon_surface_t * surface,cairo_drm_device_t * device,cairo_format_t format,int width,int height)299 radeon_surface_init (radeon_surface_t *surface,
300 		     cairo_drm_device_t *device,
301 		     cairo_format_t format,
302 		     int width, int height)
303 {
304     _cairo_surface_init (&surface->base.base,
305 			 &radeon_surface_backend,
306 			 &device->base,
307 			 _cairo_content_from_format (format),
308 			FALSE);
309     _cairo_drm_surface_init (&surface->base, format, width, height);
310 }
311 
312 static cairo_surface_t *
radeon_surface_create_internal(cairo_drm_device_t * device,cairo_format_t format,int width,int height)313 radeon_surface_create_internal (cairo_drm_device_t *device,
314 				cairo_format_t format,
315 				int width, int height)
316 {
317     radeon_surface_t *surface;
318     cairo_status_t status;
319 
320     surface = _cairo_malloc (sizeof (radeon_surface_t));
321     if (unlikely (surface == NULL))
322 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
323 
324     radeon_surface_init (surface, device, format, width, height);
325 
326     if (width && height) {
327 	surface->base.stride =
328 	    cairo_format_stride_for_width (surface->base.format, width);
329 
330 	surface->base.bo = radeon_bo_create (to_radeon_device (&device->base),
331 					     surface->base.stride * height,
332 					     RADEON_GEM_DOMAIN_GTT);
333 
334 	if (unlikely (surface->base.bo == NULL)) {
335 	    status = _cairo_drm_surface_finish (&surface->base);
336 	    free (surface);
337 	    return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
338 	}
339     }
340 
341     return &surface->base.base;
342 }
343 
344 static cairo_surface_t *
radeon_surface_create(cairo_drm_device_t * device,cairo_format_t format,int width,int height)345 radeon_surface_create (cairo_drm_device_t *device,
346 		       cairo_format_t format,
347 		       int width, int height)
348 {
349     switch (format) {
350     default:
351     case CAIRO_FORMAT_INVALID:
352     case CAIRO_FORMAT_A1:
353     case CAIRO_FORMAT_RGB16_565:
354 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
355     case CAIRO_FORMAT_ARGB32:
356     case CAIRO_FORMAT_RGB24:
357     case CAIRO_FORMAT_A8:
358 	break;
359     }
360 
361     return radeon_surface_create_internal (device, format, width, height);
362 }
363 
364 static cairo_surface_t *
radeon_surface_create_for_name(cairo_drm_device_t * device,unsigned int name,cairo_format_t format,int width,int height,int stride)365 radeon_surface_create_for_name (cairo_drm_device_t *device,
366 			      unsigned int name,
367 			      cairo_format_t format,
368 			      int width, int height, int stride)
369 {
370     radeon_surface_t *surface;
371     cairo_status_t status;
372 
373     switch (format) {
374     default:
375     case CAIRO_FORMAT_INVALID:
376     case CAIRO_FORMAT_A1:
377     case CAIRO_FORMAT_RGB16_565:
378 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
379     case CAIRO_FORMAT_ARGB32:
380     case CAIRO_FORMAT_RGB24:
381     case CAIRO_FORMAT_A8:
382 	break;
383     }
384 
385     if (stride < cairo_format_stride_for_width (format, width))
386 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
387 
388     surface = _cairo_malloc (sizeof (radeon_surface_t));
389     if (unlikely (surface == NULL))
390 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
391 
392     radeon_surface_init (surface, device, format, width, height);
393 
394     if (width && height) {
395 	surface->base.stride = stride;
396 
397 	surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base),
398 						      name);
399 
400 	if (unlikely (surface->base.bo == NULL)) {
401 	    status = _cairo_drm_surface_finish (&surface->base);
402 	    free (surface);
403 	    return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
404 	}
405     }
406 
407     return &surface->base.base;
408 }
409 
410 static void
radeon_device_destroy(void * data)411 radeon_device_destroy (void *data)
412 {
413     radeon_device_t *device = data;
414 
415     radeon_device_fini (device);
416 
417     free (data);
418 }
419 
420 cairo_drm_device_t *
_cairo_drm_radeon_device_create(int fd,dev_t dev,int vendor_id,int chip_id)421 _cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
422 {
423     radeon_device_t *device;
424     uint64_t gart_size, vram_size;
425     cairo_status_t status;
426 
427     if (! radeon_info (fd, &gart_size, &vram_size))
428 	return NULL;
429 
430     device = _cairo_malloc (sizeof (radeon_device_t));
431     if (device == NULL)
432 	return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
433 
434     status = radeon_device_init (device, fd);
435     if (unlikely (status)) {
436 	free (device);
437 	return _cairo_drm_device_create_in_error (status);
438     }
439 
440     device->base.surface.create = radeon_surface_create;
441     device->base.surface.create_for_name = radeon_surface_create_for_name;
442     device->base.surface.create_from_cacheable_image = NULL;
443     device->base.surface.flink = _cairo_drm_surface_flink;
444     device->base.surface.enable_scan_out = NULL;
445 
446     device->base.device.flush = NULL;
447     device->base.device.throttle = NULL;
448     device->base.device.destroy = radeon_device_destroy;
449 
450     device->vram_limit = vram_size;
451     device->gart_limit = gart_size;
452 
453     return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE);
454 }
455