xref: /linux/drivers/gpu/drm/sun4i/sun8i_vi_layer.c (revision a9aaf1ff88a8cb99a1335c9eb76de637f0cf8c10)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
4  */
5 
6 #include <drm/drm_atomic.h>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_blend.h>
9 #include <drm/drm_crtc.h>
10 #include <drm/drm_fb_dma_helper.h>
11 #include <drm/drm_framebuffer.h>
12 #include <drm/drm_gem_atomic_helper.h>
13 #include <drm/drm_gem_dma_helper.h>
14 #include <drm/drm_probe_helper.h>
15 
16 #include "sun8i_csc.h"
17 #include "sun8i_mixer.h"
18 #include "sun8i_vi_layer.h"
19 #include "sun8i_vi_scaler.h"
20 
21 static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
22 					int overlay, struct drm_plane *plane)
23 {
24 	u32 mask, val, ch_base;
25 
26 	ch_base = sun8i_channel_base(mixer, channel);
27 
28 	if (mixer->cfg->is_de3) {
29 		mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
30 		       SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
31 		val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
32 			(plane->state->alpha >> 8);
33 
34 		val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
35 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
36 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
37 
38 		regmap_update_bits(mixer->engine.regs,
39 				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
40 								  overlay),
41 				   mask, val);
42 	} else if (mixer->cfg->vi_num == 1) {
43 		regmap_update_bits(mixer->engine.regs,
44 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
45 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
46 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA
47 					(plane->state->alpha >> 8));
48 	}
49 }
50 
51 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
52 				       int overlay, struct drm_plane *plane,
53 				       unsigned int zpos)
54 {
55 	struct drm_plane_state *state = plane->state;
56 	const struct drm_format_info *format = state->fb->format;
57 	u32 src_w, src_h, dst_w, dst_h;
58 	u32 bld_base, ch_base;
59 	u32 outsize, insize;
60 	u32 hphase, vphase;
61 	u32 hn = 0, hm = 0;
62 	u32 vn = 0, vm = 0;
63 	bool subsampled;
64 
65 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
66 			 channel, overlay);
67 
68 	bld_base = sun8i_blender_base(mixer);
69 	ch_base = sun8i_channel_base(mixer, channel);
70 
71 	src_w = drm_rect_width(&state->src) >> 16;
72 	src_h = drm_rect_height(&state->src) >> 16;
73 	dst_w = drm_rect_width(&state->dst);
74 	dst_h = drm_rect_height(&state->dst);
75 
76 	hphase = state->src.x1 & 0xffff;
77 	vphase = state->src.y1 & 0xffff;
78 
79 	/* make coordinates dividable by subsampling factor */
80 	if (format->hsub > 1) {
81 		int mask, remainder;
82 
83 		mask = format->hsub - 1;
84 		remainder = (state->src.x1 >> 16) & mask;
85 		src_w = (src_w + remainder) & ~mask;
86 		hphase += remainder << 16;
87 	}
88 
89 	if (format->vsub > 1) {
90 		int mask, remainder;
91 
92 		mask = format->vsub - 1;
93 		remainder = (state->src.y1 >> 16) & mask;
94 		src_h = (src_h + remainder) & ~mask;
95 		vphase += remainder << 16;
96 	}
97 
98 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
99 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
100 
101 	/* Set height and width */
102 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
103 			 (state->src.x1 >> 16) & ~(format->hsub - 1),
104 			 (state->src.y1 >> 16) & ~(format->vsub - 1));
105 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
106 	regmap_write(mixer->engine.regs,
107 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
108 		     insize);
109 	regmap_write(mixer->engine.regs,
110 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
111 		     insize);
112 
113 	/*
114 	 * Scaler must be enabled for subsampled formats, so it scales
115 	 * chroma to same size as luma.
116 	 */
117 	subsampled = format->hsub > 1 || format->vsub > 1;
118 
119 	if (insize != outsize || subsampled || hphase || vphase) {
120 		unsigned int scanline, required;
121 		struct drm_display_mode *mode;
122 		u32 hscale, vscale, fps;
123 		u64 ability;
124 
125 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
126 
127 		mode = &plane->state->crtc->state->mode;
128 		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
129 		ability = clk_get_rate(mixer->mod_clk);
130 		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
131 		ability *= 80;
132 		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
133 
134 		required = src_h * 100 / dst_h;
135 
136 		if (ability < required) {
137 			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
138 			vm = src_h;
139 			vn = (u32)ability * dst_h / 100;
140 			src_h = vn;
141 		}
142 
143 		/* it seems that every RGB scaler has buffer for 2048 pixels */
144 		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
145 
146 		if (src_w > scanline) {
147 			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
148 			hm = src_w;
149 			hn = scanline;
150 			src_w = hn;
151 		}
152 
153 		hscale = (src_w << 16) / dst_w;
154 		vscale = (src_h << 16) / dst_h;
155 
156 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
157 				      dst_h, hscale, vscale, hphase, vphase,
158 				      format);
159 		sun8i_vi_scaler_enable(mixer, channel, true);
160 	} else {
161 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
162 		sun8i_vi_scaler_enable(mixer, channel, false);
163 	}
164 
165 	regmap_write(mixer->engine.regs,
166 		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
167 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
168 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
169 	regmap_write(mixer->engine.regs,
170 		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
171 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
172 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
173 	regmap_write(mixer->engine.regs,
174 		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
175 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
176 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
177 	regmap_write(mixer->engine.regs,
178 		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
179 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
180 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
181 
182 	/* Set base coordinates */
183 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
184 			 state->dst.x1, state->dst.y1);
185 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
186 	regmap_write(mixer->engine.regs,
187 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
188 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
189 	regmap_write(mixer->engine.regs,
190 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
191 		     outsize);
192 
193 	return 0;
194 }
195 
196 static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
197 {
198 	if (!format->is_yuv)
199 		return SUN8I_CSC_MODE_OFF;
200 
201 	switch (format->format) {
202 	case DRM_FORMAT_YVU411:
203 	case DRM_FORMAT_YVU420:
204 	case DRM_FORMAT_YVU422:
205 	case DRM_FORMAT_YVU444:
206 		return SUN8I_CSC_MODE_YVU2RGB;
207 	default:
208 		return SUN8I_CSC_MODE_YUV2RGB;
209 	}
210 }
211 
212 static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
213 					 int overlay, struct drm_plane *plane)
214 {
215 	struct drm_plane_state *state = plane->state;
216 	u32 val, ch_base, csc_mode, hw_fmt;
217 	const struct drm_format_info *fmt;
218 	int ret;
219 
220 	ch_base = sun8i_channel_base(mixer, channel);
221 
222 	fmt = state->fb->format;
223 	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
224 	if (ret) {
225 		DRM_DEBUG_DRIVER("Invalid format\n");
226 		return ret;
227 	}
228 
229 	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
230 	regmap_update_bits(mixer->engine.regs,
231 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
232 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
233 
234 	csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
235 	if (csc_mode != SUN8I_CSC_MODE_OFF) {
236 		sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
237 						state->color_encoding,
238 						state->color_range);
239 		sun8i_csc_enable_ccsc(mixer, channel, true);
240 	} else {
241 		sun8i_csc_enable_ccsc(mixer, channel, false);
242 	}
243 
244 	if (!fmt->is_yuv)
245 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
246 	else
247 		val = 0;
248 
249 	regmap_update_bits(mixer->engine.regs,
250 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
251 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
252 
253 	return 0;
254 }
255 
256 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
257 					int overlay, struct drm_plane *plane)
258 {
259 	struct drm_plane_state *state = plane->state;
260 	struct drm_framebuffer *fb = state->fb;
261 	const struct drm_format_info *format = fb->format;
262 	struct drm_gem_dma_object *gem;
263 	u32 dx, dy, src_x, src_y;
264 	dma_addr_t dma_addr;
265 	u32 ch_base;
266 	int i;
267 
268 	ch_base = sun8i_channel_base(mixer, channel);
269 
270 	/* Adjust x and y to be dividable by subsampling factor */
271 	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
272 	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
273 
274 	for (i = 0; i < format->num_planes; i++) {
275 		/* Get the physical address of the buffer in memory */
276 		gem = drm_fb_dma_get_gem_obj(fb, i);
277 
278 		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
279 
280 		/* Compute the start of the displayed memory */
281 		dma_addr = gem->dma_addr + fb->offsets[i];
282 
283 		dx = src_x;
284 		dy = src_y;
285 
286 		if (i > 0) {
287 			dx /= format->hsub;
288 			dy /= format->vsub;
289 		}
290 
291 		/* Fixup framebuffer address for src coordinates */
292 		dma_addr += dx * format->cpp[i];
293 		dma_addr += dy * fb->pitches[i];
294 
295 		/* Set the line width */
296 		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
297 				 i + 1, fb->pitches[i]);
298 		regmap_write(mixer->engine.regs,
299 			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
300 							     overlay, i),
301 			     fb->pitches[i]);
302 
303 		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
304 				 i + 1, &dma_addr);
305 
306 		regmap_write(mixer->engine.regs,
307 			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
308 								 overlay, i),
309 			     lower_32_bits(dma_addr));
310 	}
311 
312 	return 0;
313 }
314 
315 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
316 				       struct drm_atomic_state *state)
317 {
318 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
319 										 plane);
320 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
321 	struct drm_crtc *crtc = new_plane_state->crtc;
322 	struct drm_crtc_state *crtc_state;
323 	int min_scale, max_scale;
324 
325 	if (!crtc)
326 		return 0;
327 
328 	crtc_state = drm_atomic_get_existing_crtc_state(state,
329 							crtc);
330 	if (WARN_ON(!crtc_state))
331 		return -EINVAL;
332 
333 	min_scale = DRM_PLANE_NO_SCALING;
334 	max_scale = DRM_PLANE_NO_SCALING;
335 
336 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
337 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
338 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
339 	}
340 
341 	return drm_atomic_helper_check_plane_state(new_plane_state,
342 						   crtc_state,
343 						   min_scale, max_scale,
344 						   true, true);
345 }
346 
347 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
348 					 struct drm_atomic_state *state)
349 {
350 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
351 									   plane);
352 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
353 	unsigned int zpos = new_state->normalized_zpos;
354 	struct sun8i_mixer *mixer = layer->mixer;
355 
356 	if (!new_state->crtc || !new_state->visible)
357 		return;
358 
359 	sun8i_vi_layer_update_coord(mixer, layer->channel,
360 				    layer->overlay, plane, zpos);
361 	sun8i_vi_layer_update_alpha(mixer, layer->channel,
362 				    layer->overlay, plane);
363 	sun8i_vi_layer_update_formats(mixer, layer->channel,
364 				      layer->overlay, plane);
365 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
366 				     layer->overlay, plane);
367 }
368 
369 static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
370 	.atomic_check	= sun8i_vi_layer_atomic_check,
371 	.atomic_update	= sun8i_vi_layer_atomic_update,
372 };
373 
374 static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
375 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
376 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
377 	.destroy		= drm_plane_cleanup,
378 	.disable_plane		= drm_atomic_helper_disable_plane,
379 	.reset			= drm_atomic_helper_plane_reset,
380 	.update_plane		= drm_atomic_helper_update_plane,
381 };
382 
383 /*
384  * While DE2 VI layer supports same RGB formats as UI layer, alpha
385  * channel is ignored. This structure lists all unique variants
386  * where alpha channel is replaced with "don't care" (X) channel.
387  */
388 static const u32 sun8i_vi_layer_formats[] = {
389 	DRM_FORMAT_BGR565,
390 	DRM_FORMAT_BGR888,
391 	DRM_FORMAT_BGRX4444,
392 	DRM_FORMAT_BGRX5551,
393 	DRM_FORMAT_BGRX8888,
394 	DRM_FORMAT_RGB565,
395 	DRM_FORMAT_RGB888,
396 	DRM_FORMAT_RGBX4444,
397 	DRM_FORMAT_RGBX5551,
398 	DRM_FORMAT_RGBX8888,
399 	DRM_FORMAT_XBGR1555,
400 	DRM_FORMAT_XBGR4444,
401 	DRM_FORMAT_XBGR8888,
402 	DRM_FORMAT_XRGB1555,
403 	DRM_FORMAT_XRGB4444,
404 	DRM_FORMAT_XRGB8888,
405 
406 	DRM_FORMAT_NV16,
407 	DRM_FORMAT_NV12,
408 	DRM_FORMAT_NV21,
409 	DRM_FORMAT_NV61,
410 	DRM_FORMAT_UYVY,
411 	DRM_FORMAT_VYUY,
412 	DRM_FORMAT_YUYV,
413 	DRM_FORMAT_YVYU,
414 	DRM_FORMAT_YUV411,
415 	DRM_FORMAT_YUV420,
416 	DRM_FORMAT_YUV422,
417 	DRM_FORMAT_YVU411,
418 	DRM_FORMAT_YVU420,
419 	DRM_FORMAT_YVU422,
420 };
421 
422 static const u32 sun8i_vi_layer_de3_formats[] = {
423 	DRM_FORMAT_ABGR1555,
424 	DRM_FORMAT_ABGR2101010,
425 	DRM_FORMAT_ABGR4444,
426 	DRM_FORMAT_ABGR8888,
427 	DRM_FORMAT_ARGB1555,
428 	DRM_FORMAT_ARGB2101010,
429 	DRM_FORMAT_ARGB4444,
430 	DRM_FORMAT_ARGB8888,
431 	DRM_FORMAT_BGR565,
432 	DRM_FORMAT_BGR888,
433 	DRM_FORMAT_BGRA1010102,
434 	DRM_FORMAT_BGRA5551,
435 	DRM_FORMAT_BGRA4444,
436 	DRM_FORMAT_BGRA8888,
437 	DRM_FORMAT_BGRX8888,
438 	DRM_FORMAT_RGB565,
439 	DRM_FORMAT_RGB888,
440 	DRM_FORMAT_RGBA1010102,
441 	DRM_FORMAT_RGBA4444,
442 	DRM_FORMAT_RGBA5551,
443 	DRM_FORMAT_RGBA8888,
444 	DRM_FORMAT_RGBX8888,
445 	DRM_FORMAT_XBGR8888,
446 	DRM_FORMAT_XRGB8888,
447 
448 	DRM_FORMAT_NV16,
449 	DRM_FORMAT_NV12,
450 	DRM_FORMAT_NV21,
451 	DRM_FORMAT_NV61,
452 	DRM_FORMAT_P010,
453 	DRM_FORMAT_P210,
454 	DRM_FORMAT_UYVY,
455 	DRM_FORMAT_VYUY,
456 	DRM_FORMAT_YUYV,
457 	DRM_FORMAT_YVYU,
458 	DRM_FORMAT_YUV411,
459 	DRM_FORMAT_YUV420,
460 	DRM_FORMAT_YUV422,
461 	DRM_FORMAT_YVU411,
462 	DRM_FORMAT_YVU420,
463 	DRM_FORMAT_YVU422,
464 };
465 
466 static const uint64_t sun8i_layer_modifiers[] = {
467 	DRM_FORMAT_MOD_LINEAR,
468 	DRM_FORMAT_MOD_INVALID
469 };
470 
471 struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
472 					    struct sun8i_mixer *mixer,
473 					    int index)
474 {
475 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
476 	u32 supported_encodings, supported_ranges;
477 	unsigned int plane_cnt, format_count;
478 	struct sun8i_layer *layer;
479 	const u32 *formats;
480 	int ret;
481 
482 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
483 	if (!layer)
484 		return ERR_PTR(-ENOMEM);
485 
486 	if (mixer->cfg->is_de3) {
487 		formats = sun8i_vi_layer_de3_formats;
488 		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
489 	} else {
490 		formats = sun8i_vi_layer_formats;
491 		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
492 	}
493 
494 	if (!mixer->cfg->ui_num && index == 0)
495 		type = DRM_PLANE_TYPE_PRIMARY;
496 
497 	/* possible crtcs are set later */
498 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
499 				       &sun8i_vi_layer_funcs,
500 				       formats, format_count,
501 				       sun8i_layer_modifiers,
502 				       type, NULL);
503 	if (ret) {
504 		dev_err(drm->dev, "Couldn't initialize layer\n");
505 		return ERR_PTR(ret);
506 	}
507 
508 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
509 
510 	if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
511 		ret = drm_plane_create_alpha_property(&layer->plane);
512 		if (ret) {
513 			dev_err(drm->dev, "Couldn't add alpha property\n");
514 			return ERR_PTR(ret);
515 		}
516 	}
517 
518 	ret = drm_plane_create_zpos_property(&layer->plane, index,
519 					     0, plane_cnt - 1);
520 	if (ret) {
521 		dev_err(drm->dev, "Couldn't add zpos property\n");
522 		return ERR_PTR(ret);
523 	}
524 
525 	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
526 			      BIT(DRM_COLOR_YCBCR_BT709);
527 	if (mixer->cfg->is_de3)
528 		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
529 
530 	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
531 			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
532 
533 	ret = drm_plane_create_color_properties(&layer->plane,
534 						supported_encodings,
535 						supported_ranges,
536 						DRM_COLOR_YCBCR_BT709,
537 						DRM_COLOR_YCBCR_LIMITED_RANGE);
538 	if (ret) {
539 		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
540 		return ERR_PTR(ret);
541 	}
542 
543 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
544 	layer->mixer = mixer;
545 	layer->type = SUN8I_LAYER_TYPE_VI;
546 	layer->channel = index;
547 	layer->overlay = 0;
548 
549 	return layer;
550 }
551