1 /*
2 * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <gralloc_priv.h>
31
32 #include <core/buffer_allocator.h>
33 #include <utils/constants.h>
34 #include <utils/debug.h>
35
36 #include "gr_utils.h"
37 #include "hwc_buffer_allocator.h"
38 #include "hwc_debugger.h"
39 #include "hwc_layers.h"
40
41 #define __CLASS__ "HWCBufferAllocator"
42
43 using android::hardware::graphics::mapper::V2_0::Error;
44 using MapperV3Error = android::hardware::graphics::mapper::V3_0::Error;
45 using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
46 using MapperV3BufferDescriptor = android::hardware::graphics::mapper::V3_0::BufferDescriptor;
47 using android::hardware::hidl_handle;
48 using android::hardware::hidl_vec;
49
50 namespace sdm {
51
GetGrallocInstance()52 DisplayError HWCBufferAllocator::GetGrallocInstance() {
53 // Lazy initialization of gralloc HALs
54 if (mapper_V3_ != nullptr || mapper_V2_ != nullptr || allocator_V3_ != nullptr ||
55 allocator_V2_ != nullptr) {
56 return kErrorNone;
57 }
58
59 allocator_V3_ = IAllocatorV3::getService();
60 if (allocator_V3_ == nullptr) {
61 allocator_V2_ = IAllocatorV2::getService();
62 if (allocator_V2_ == nullptr) {
63 DLOGE("Unable to get allocator");
64 return kErrorCriticalResource;
65 }
66 }
67
68 mapper_V3_ = IMapperV3::getService();
69 if (mapper_V3_ == nullptr) {
70 mapper_V2_ = IMapperV2::getService();
71 if (mapper_V2_ == nullptr) {
72 DLOGE("Unable to get mapper");
73 return kErrorCriticalResource;
74 }
75 }
76
77 return kErrorNone;
78 }
79
AllocateBuffer(BufferInfo * buffer_info)80 DisplayError HWCBufferAllocator::AllocateBuffer(BufferInfo *buffer_info) {
81 auto err = GetGrallocInstance();
82 if (err != kErrorNone) {
83 return err;
84 }
85 const BufferConfig &buffer_config = buffer_info->buffer_config;
86 AllocatedBufferInfo *alloc_buffer_info = &buffer_info->alloc_buffer_info;
87 int format;
88 uint64_t alloc_flags = 0;
89 int error = SetBufferInfo(buffer_config.format, &format, &alloc_flags);
90 if (error != 0) {
91 return kErrorParameters;
92 }
93
94 if (buffer_config.secure) {
95 alloc_flags |= BufferUsage::PROTECTED;
96 }
97
98 if (buffer_config.secure_camera) {
99 alloc_flags |= BufferUsage::CAMERA_OUTPUT;
100 }
101
102 if (!buffer_config.cache) {
103 // Allocate uncached buffers
104 alloc_flags |= GRALLOC_USAGE_PRIVATE_UNCACHED;
105 }
106
107 if (buffer_config.gfx_client) {
108 alloc_flags |= BufferUsage::GPU_TEXTURE;
109 }
110
111 alloc_flags |= BufferUsage::COMPOSER_OVERLAY;
112
113 const native_handle_t *buf = nullptr;
114
115 if (mapper_V3_ != nullptr) {
116 IMapperV3::BufferDescriptorInfo descriptor_info;
117 descriptor_info.width = buffer_config.width;
118 descriptor_info.height = buffer_config.height;
119 descriptor_info.layerCount = 1;
120 descriptor_info.format =
121 static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(format);
122 descriptor_info.usage = alloc_flags;
123
124 auto hidl_err = MapperV3Error::NONE;
125
126 auto descriptor = BufferDescriptor();
127 mapper_V3_->createDescriptor(descriptor_info, [&](const auto &_error, const auto &_descriptor) {
128 hidl_err = _error;
129 if (hidl_err != MapperV3Error::NONE) {
130 return;
131 }
132 descriptor = _descriptor;
133 });
134
135 if (hidl_err != MapperV3Error::NONE) {
136 DLOGE("Failed to create descriptor");
137 return kErrorMemory;
138 }
139
140 hidl_handle raw_handle = nullptr;
141
142 allocator_V3_->allocate(descriptor, 1,
143 [&](const auto &_error, const auto &_stride, const auto &_buffers) {
144 hidl_err = _error;
145 if (hidl_err != MapperV3Error::NONE) {
146 return;
147 }
148 raw_handle = _buffers[0];
149 });
150
151 if (hidl_err != MapperV3Error::NONE) {
152 DLOGE("Failed to allocate buffer");
153 return kErrorMemory;
154 }
155
156 mapper_V3_->importBuffer(raw_handle, [&](const auto &_error, const auto &_buffer) {
157 hidl_err = _error;
158 if (hidl_err != MapperV3Error::NONE) {
159 return;
160 }
161 buf = static_cast<const native_handle_t *>(_buffer);
162 });
163
164 if (hidl_err != MapperV3Error::NONE) {
165 DLOGE("Failed to import buffer into HWC");
166 return kErrorMemory;
167 }
168 } else {
169 IMapperV2::BufferDescriptorInfo descriptor_info;
170 descriptor_info.width = buffer_config.width;
171 descriptor_info.height = buffer_config.height;
172 descriptor_info.layerCount = 1;
173 descriptor_info.format =
174 static_cast<android::hardware::graphics::common::V1_0::PixelFormat>(format);
175 descriptor_info.usage = alloc_flags;
176
177 auto hidl_err = Error::NONE;
178
179 auto descriptor = BufferDescriptor();
180 mapper_V2_->createDescriptor(descriptor_info, [&](const auto &_error, const auto &_descriptor) {
181 hidl_err = _error;
182 if (hidl_err != Error::NONE) {
183 return;
184 }
185 descriptor = _descriptor;
186 });
187
188 if (hidl_err != Error::NONE) {
189 DLOGE("Failed to create descriptor");
190 return kErrorMemory;
191 }
192
193 hidl_handle raw_handle = nullptr;
194
195 allocator_V2_->allocate(descriptor, 1,
196 [&](const auto &_error, const auto &_stride, const auto &_buffers) {
197 hidl_err = _error;
198 if (hidl_err != Error::NONE) {
199 return;
200 }
201 raw_handle = _buffers[0];
202 });
203
204 if (hidl_err != Error::NONE) {
205 DLOGE("Failed to allocate buffer");
206 return kErrorMemory;
207 }
208
209 mapper_V2_->importBuffer(raw_handle, [&](const auto &_error, const auto &_buffer) {
210 hidl_err = _error;
211 if (hidl_err != Error::NONE) {
212 return;
213 }
214 buf = static_cast<const native_handle_t *>(_buffer);
215 });
216
217 if (hidl_err != Error::NONE) {
218 DLOGE("Failed to import buffer into HWC");
219 return kErrorMemory;
220 }
221 }
222
223 private_handle_t *hnd = nullptr;
224 hnd = (private_handle_t *)buf; // NOLINT
225 alloc_buffer_info->fd = hnd->fd;
226 alloc_buffer_info->stride = UINT32(hnd->width);
227 alloc_buffer_info->aligned_width = UINT32(hnd->width);
228 alloc_buffer_info->aligned_height = UINT32(hnd->height);
229 alloc_buffer_info->size = hnd->size;
230 alloc_buffer_info->id = hnd->id;
231 alloc_buffer_info->format = HWCLayer::GetSDMFormat(hnd->format, hnd->flags);
232
233 buffer_info->private_data = reinterpret_cast<void *>(hnd);
234 return kErrorNone;
235 }
236
FreeBuffer(BufferInfo * buffer_info)237 DisplayError HWCBufferAllocator::FreeBuffer(BufferInfo *buffer_info) {
238 DisplayError err = kErrorNone;
239 auto hnd = reinterpret_cast<void *>(buffer_info->private_data);
240 if (mapper_V3_ != nullptr) {
241 mapper_V3_->freeBuffer(hnd);
242 } else {
243 mapper_V2_->freeBuffer(hnd);
244 }
245 AllocatedBufferInfo &alloc_buffer_info = buffer_info->alloc_buffer_info;
246
247 alloc_buffer_info.fd = -1;
248 alloc_buffer_info.stride = 0;
249 alloc_buffer_info.size = 0;
250 buffer_info->private_data = NULL;
251 return err;
252 }
253
GetCustomWidthAndHeight(const private_handle_t * handle,int * width,int * height)254 void HWCBufferAllocator::GetCustomWidthAndHeight(const private_handle_t *handle, int *width,
255 int *height) {
256 *width = handle->width;
257 *height = handle->height;
258 gralloc::GetCustomDimensions(const_cast<private_handle_t *>(handle), width, height);
259 }
260
GetAlignedWidthAndHeight(int width,int height,int format,uint32_t alloc_type,int * aligned_width,int * aligned_height)261 void HWCBufferAllocator::GetAlignedWidthAndHeight(int width, int height, int format,
262 uint32_t alloc_type, int *aligned_width,
263 int *aligned_height) {
264 uint64_t usage = 0;
265 if (alloc_type & GRALLOC_USAGE_HW_FB) {
266 usage |= BufferUsage::COMPOSER_CLIENT_TARGET;
267 }
268 if (alloc_type & GRALLOC_USAGE_PRIVATE_ALLOC_UBWC) {
269 usage |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
270 }
271 uint32_t aligned_w = UINT(width);
272 uint32_t aligned_h = UINT(height);
273 gralloc::BufferInfo info(width, height, format, usage);
274 gralloc::GetAlignedWidthAndHeight(info, &aligned_w, &aligned_h);
275 *aligned_width = INT(aligned_w);
276 *aligned_height = INT(aligned_h);
277 }
278
GetBufferSize(BufferInfo * buffer_info)279 uint32_t HWCBufferAllocator::GetBufferSize(BufferInfo *buffer_info) {
280 const BufferConfig &buffer_config = buffer_info->buffer_config;
281 uint64_t alloc_flags = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP;
282
283 int width = INT(buffer_config.width);
284 int height = INT(buffer_config.height);
285 int format;
286
287 if (buffer_config.secure) {
288 alloc_flags |= INT(GRALLOC_USAGE_PROTECTED);
289 }
290
291 if (!buffer_config.cache) {
292 // Allocate uncached buffers
293 alloc_flags |= GRALLOC_USAGE_PRIVATE_UNCACHED;
294 }
295
296 if (SetBufferInfo(buffer_config.format, &format, &alloc_flags) < 0) {
297 return 0;
298 }
299
300 uint32_t aligned_width = 0, aligned_height = 0, buffer_size = 0;
301 gralloc::BufferInfo info(width, height, format, alloc_flags);
302 int ret = GetBufferSizeAndDimensions(info, &buffer_size, &aligned_width, &aligned_height);
303 if (ret < 0) {
304 return 0;
305 }
306 return buffer_size;
307 }
308
SetBufferInfo(LayerBufferFormat format,int * target,uint64_t * flags)309 int HWCBufferAllocator::SetBufferInfo(LayerBufferFormat format, int *target, uint64_t *flags) {
310 switch (format) {
311 case kFormatRGBA8888:
312 *target = HAL_PIXEL_FORMAT_RGBA_8888;
313 break;
314 case kFormatRGBX8888:
315 *target = HAL_PIXEL_FORMAT_RGBX_8888;
316 break;
317 case kFormatRGB888:
318 *target = HAL_PIXEL_FORMAT_RGB_888;
319 break;
320 case kFormatRGB565:
321 *target = HAL_PIXEL_FORMAT_RGB_565;
322 break;
323 case kFormatBGR565:
324 *target = HAL_PIXEL_FORMAT_BGR_565;
325 break;
326 case kFormatBGR888:
327 *target = HAL_PIXEL_FORMAT_BGR_888;
328 break;
329 case kFormatBGRA8888:
330 *target = HAL_PIXEL_FORMAT_BGRA_8888;
331 break;
332 case kFormatYCrCb420PlanarStride16:
333 *target = HAL_PIXEL_FORMAT_YV12;
334 break;
335 case kFormatYCrCb420SemiPlanar:
336 *target = HAL_PIXEL_FORMAT_YCrCb_420_SP;
337 break;
338 case kFormatYCbCr420SemiPlanar:
339 *target = HAL_PIXEL_FORMAT_YCbCr_420_SP;
340 break;
341 case kFormatYCbCr422H2V1Packed:
342 *target = HAL_PIXEL_FORMAT_YCbCr_422_I;
343 break;
344 case kFormatCbYCrY422H2V1Packed:
345 *target = HAL_PIXEL_FORMAT_CbYCrY_422_I;
346 break;
347 case kFormatYCbCr422H2V1SemiPlanar:
348 *target = HAL_PIXEL_FORMAT_YCbCr_422_SP;
349 break;
350 case kFormatYCbCr420SemiPlanarVenus:
351 *target = HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS;
352 break;
353 case kFormatYCrCb420SemiPlanarVenus:
354 *target = HAL_PIXEL_FORMAT_YCrCb_420_SP_VENUS;
355 break;
356 case kFormatYCbCr420SPVenusUbwc:
357 case kFormatYCbCr420SPVenusTile:
358 *target = HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS_UBWC;
359 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
360 break;
361 case kFormatRGBA5551:
362 *target = HAL_PIXEL_FORMAT_RGBA_5551;
363 break;
364 case kFormatRGBA4444:
365 *target = HAL_PIXEL_FORMAT_RGBA_4444;
366 break;
367 case kFormatRGBA1010102:
368 *target = HAL_PIXEL_FORMAT_RGBA_1010102;
369 break;
370 case kFormatARGB2101010:
371 *target = HAL_PIXEL_FORMAT_ARGB_2101010;
372 break;
373 case kFormatRGBX1010102:
374 *target = HAL_PIXEL_FORMAT_RGBX_1010102;
375 break;
376 case kFormatXRGB2101010:
377 *target = HAL_PIXEL_FORMAT_XRGB_2101010;
378 break;
379 case kFormatBGRA1010102:
380 *target = HAL_PIXEL_FORMAT_BGRA_1010102;
381 break;
382 case kFormatABGR2101010:
383 *target = HAL_PIXEL_FORMAT_ABGR_2101010;
384 break;
385 case kFormatBGRX1010102:
386 *target = HAL_PIXEL_FORMAT_BGRX_1010102;
387 break;
388 case kFormatXBGR2101010:
389 *target = HAL_PIXEL_FORMAT_XBGR_2101010;
390 break;
391 case kFormatYCbCr420P010:
392 *target = HAL_PIXEL_FORMAT_YCbCr_420_P010;
393 break;
394 case kFormatYCbCr420TP10Ubwc:
395 case kFormatYCbCr420TP10Tile:
396 *target = HAL_PIXEL_FORMAT_YCbCr_420_TP10_UBWC;
397 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
398 break;
399 case kFormatYCbCr420P010Ubwc:
400 case kFormatYCbCr420P010Tile:
401 *target = HAL_PIXEL_FORMAT_YCbCr_420_P010_UBWC;
402 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
403 break;
404 case kFormatYCbCr420P010Venus:
405 *target = HAL_PIXEL_FORMAT_YCbCr_420_P010_VENUS;
406 break;
407 case kFormatRGBA8888Ubwc:
408 *target = HAL_PIXEL_FORMAT_RGBA_8888;
409 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
410 break;
411 case kFormatRGBX8888Ubwc:
412 *target = HAL_PIXEL_FORMAT_RGBX_8888;
413 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
414 break;
415 case kFormatBGR565Ubwc:
416 *target = HAL_PIXEL_FORMAT_BGR_565;
417 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
418 break;
419 case kFormatRGBA1010102Ubwc:
420 *target = HAL_PIXEL_FORMAT_RGBA_1010102;
421 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
422 break;
423 case kFormatRGBX1010102Ubwc:
424 *target = HAL_PIXEL_FORMAT_RGBX_1010102;
425 *flags |= GRALLOC_USAGE_PRIVATE_ALLOC_UBWC;
426 break;
427 default:
428 DLOGW("Unsupported format = 0x%x", format);
429 return -EINVAL;
430 }
431 return 0;
432 }
433
GetAllocatedBufferInfo(const BufferConfig & buffer_config,AllocatedBufferInfo * allocated_buffer_info)434 DisplayError HWCBufferAllocator::GetAllocatedBufferInfo(
435 const BufferConfig &buffer_config, AllocatedBufferInfo *allocated_buffer_info) {
436 // TODO(user): This API should pass the buffer_info of the already allocated buffer
437 // The private_data can then be typecast to the private_handle and used directly.
438 uint64_t alloc_flags = GRALLOC_USAGE_PRIVATE_IOMMU_HEAP;
439
440 int width = INT(buffer_config.width);
441 int height = INT(buffer_config.height);
442 int format;
443
444 if (buffer_config.secure) {
445 alloc_flags |= INT(GRALLOC_USAGE_PROTECTED);
446 }
447
448 if (!buffer_config.cache) {
449 // Allocate uncached buffers
450 alloc_flags |= GRALLOC_USAGE_PRIVATE_UNCACHED;
451 }
452
453 if (SetBufferInfo(buffer_config.format, &format, &alloc_flags) < 0) {
454 return kErrorParameters;
455 }
456
457 uint32_t aligned_width = 0, aligned_height = 0, buffer_size = 0;
458 gralloc::BufferInfo info(width, height, format, alloc_flags);
459 int ret = GetBufferSizeAndDimensions(info, &buffer_size, &aligned_width, &aligned_height);
460 if (ret < 0) {
461 return kErrorParameters;
462 }
463 allocated_buffer_info->stride = UINT32(aligned_width);
464 allocated_buffer_info->aligned_width = UINT32(aligned_width);
465 allocated_buffer_info->aligned_height = UINT32(aligned_height);
466 allocated_buffer_info->size = UINT32(buffer_size);
467
468 return kErrorNone;
469 }
470
GetBufferLayout(const AllocatedBufferInfo & buf_info,uint32_t stride[4],uint32_t offset[4],uint32_t * num_planes)471 DisplayError HWCBufferAllocator::GetBufferLayout(const AllocatedBufferInfo &buf_info,
472 uint32_t stride[4], uint32_t offset[4],
473 uint32_t *num_planes) {
474 // TODO(user): Transition APIs to not need a private handle
475 private_handle_t hnd(-1, 0, 0, 0, 0, 0, 0);
476 int format = HAL_PIXEL_FORMAT_RGBA_8888;
477 uint64_t flags = 0;
478
479 SetBufferInfo(buf_info.format, &format, &flags);
480 // Setup only the required stuff, skip rest
481 hnd.format = format;
482 hnd.width = INT32(buf_info.aligned_width);
483 hnd.height = INT32(buf_info.aligned_height);
484 if (flags & GRALLOC_USAGE_PRIVATE_ALLOC_UBWC) {
485 hnd.flags = private_handle_t::PRIV_FLAGS_UBWC_ALIGNED;
486 }
487
488 int ret = gralloc::GetBufferLayout(&hnd, stride, offset, num_planes);
489 if (ret < 0) {
490 DLOGE("GetBufferLayout failed");
491 return kErrorParameters;
492 }
493
494 return kErrorNone;
495 }
496
MapBuffer(const private_handle_t * handle,shared_ptr<Fence> acquire_fence)497 DisplayError HWCBufferAllocator::MapBuffer(const private_handle_t *handle,
498 shared_ptr<Fence> acquire_fence) {
499 auto err = GetGrallocInstance();
500 if (err != kErrorNone) {
501 return err;
502 }
503
504 Fence::ScopedRef scoped_ref;
505 NATIVE_HANDLE_DECLARE_STORAGE(acquire_fence_storage, 1, 0);
506 hidl_handle acquire_fence_handle;
507 if (acquire_fence) {
508 auto h = native_handle_init(acquire_fence_storage, 1, 0);
509 h->data[0] = scoped_ref.Get(acquire_fence);
510 acquire_fence_handle = h;
511 }
512
513 auto hnd = const_cast<private_handle_t *>(handle);
514 void *buffer_ptr = NULL;
515 if (mapper_V3_ != nullptr) {
516 const IMapperV3::Rect access_region = {.left = 0, .top = 0, .width = 0, .height = 0};
517 mapper_V3_->lock(
518 reinterpret_cast<void *>(hnd), (uint64_t)BufferUsage::CPU_READ_OFTEN, access_region,
519 acquire_fence_handle,
520 [&](const auto &_error, const auto &_buffer, const auto &_bpp, const auto &_stride) {
521 if (_error == MapperV3Error::NONE) {
522 buffer_ptr = _buffer;
523 }
524 });
525 } else {
526 const IMapperV2::Rect access_region = {.left = 0, .top = 0, .width = 0, .height = 0};
527 mapper_V2_->lock(reinterpret_cast<void *>(hnd), (uint64_t)BufferUsage::CPU_READ_OFTEN,
528 access_region, acquire_fence_handle,
529 [&](const auto &_error, const auto &_buffer) {
530 if (_error == Error::NONE) {
531 buffer_ptr = _buffer;
532 }
533 });
534 }
535 if (!buffer_ptr) {
536 return kErrorUndefined;
537 }
538 return kErrorNone;
539 }
540
UnmapBuffer(const private_handle_t * handle,int * release_fence)541 DisplayError HWCBufferAllocator::UnmapBuffer(const private_handle_t *handle, int *release_fence) {
542 DisplayError err = kErrorNone;
543 *release_fence = -1;
544 auto hnd = const_cast<private_handle_t *>(handle);
545 if (mapper_V3_ != nullptr) {
546 mapper_V3_->unlock(reinterpret_cast<void *>(hnd),
547 [&](const auto &_error, const auto &_release_fence) {
548 if (_error != MapperV3Error::NONE) {
549 err = kErrorUndefined;
550 }
551 });
552 } else {
553 mapper_V2_->unlock(reinterpret_cast<void *>(hnd),
554 [&](const auto &_error, const auto &_release_fence) {
555 if (_error != Error::NONE) {
556 err = kErrorUndefined;
557 }
558 });
559 }
560 return err;
561 }
562
563 } // namespace sdm
564