xref: /linux/drivers/gpu/drm/amd/display/dc/gpio/hw_ddc.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 /*
2  * Copyright 2012-15 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Authors: AMD
23  *
24  */
25 
26 #include "dm_services.h"
27 
28 #include "include/gpio_interface.h"
29 #include "include/gpio_types.h"
30 #include "hw_gpio.h"
31 #include "hw_ddc.h"
32 
33 #include "reg_helper.h"
34 #include "gpio_regs.h"
35 
36 
37 #undef FN
38 #define FN(reg_name, field_name) \
39 	gpio_reg_shift(ddc->shifts->field_name), ddc->masks->field_name
40 
41 #define CTX \
42 	ddc->base.base.ctx
43 #define REG(reg)\
44 	(ddc->regs->reg)
45 
46 struct gpio;
47 
48 static void dal_hw_ddc_destruct(
49 	struct hw_ddc *pin)
50 {
51 	dal_hw_gpio_destruct(&pin->base);
52 }
53 
54 static void dal_hw_ddc_destroy(
55 	struct hw_gpio_pin **ptr)
56 {
57 	struct hw_ddc *pin = HW_DDC_FROM_BASE(*ptr);
58 
59 	dal_hw_ddc_destruct(pin);
60 
61 	kfree(pin);
62 
63 	*ptr = NULL;
64 }
65 
66 static enum gpio_result set_config(
67 	struct hw_gpio_pin *ptr,
68 	const struct gpio_config_data *config_data)
69 {
70 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
71 	struct hw_gpio *hw_gpio = NULL;
72 	uint32_t regval;
73 	uint32_t ddc_data_pd_en = 0;
74 	uint32_t ddc_clk_pd_en = 0;
75 	uint32_t aux_pad_mode = 0;
76 
77 	hw_gpio = &ddc->base;
78 
79 	if (hw_gpio == NULL) {
80 		ASSERT_CRITICAL(false);
81 		return GPIO_RESULT_NULL_HANDLE;
82 	}
83 
84 	regval = REG_GET_3(gpio.MASK_reg,
85 			DC_GPIO_DDC1DATA_PD_EN, &ddc_data_pd_en,
86 			DC_GPIO_DDC1CLK_PD_EN, &ddc_clk_pd_en,
87 			AUX_PAD1_MODE, &aux_pad_mode);
88 
89 	switch (config_data->config.ddc.type) {
90 	case GPIO_DDC_CONFIG_TYPE_MODE_I2C:
91 		/* On plug-in, there is a transient level on the pad
92 		 * which must be discharged through the internal pull-down.
93 		 * Enable internal pull-down, 2.5msec discharge time
94 		 * is required for detection of AUX mode */
95 		if (hw_gpio->base.en != GPIO_DDC_LINE_VIP_PAD) {
96 			if (!ddc_data_pd_en || !ddc_clk_pd_en) {
97 				if (hw_gpio->base.en == GPIO_DDC_LINE_DDC_VGA) {
98 					// bit 4 of mask has different usage in some cases
99 					REG_SET(gpio.MASK_reg, regval, DC_GPIO_DDC1DATA_PD_EN, 1);
100 				} else {
101 					REG_SET_2(gpio.MASK_reg, regval,
102 						DC_GPIO_DDC1DATA_PD_EN, 1,
103 						DC_GPIO_DDC1CLK_PD_EN, 1);
104 				}
105 				if (config_data->type ==
106 						GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE)
107 					msleep(3);
108 			}
109 		} else {
110 			uint32_t sda_pd_dis = 0;
111 			uint32_t scl_pd_dis = 0;
112 
113 			REG_GET_2(gpio.MASK_reg,
114 				  DC_GPIO_SDA_PD_DIS, &sda_pd_dis,
115 				  DC_GPIO_SCL_PD_DIS, &scl_pd_dis);
116 
117 			if (sda_pd_dis) {
118 				REG_SET(gpio.MASK_reg, regval,
119 						DC_GPIO_SDA_PD_DIS, 0);
120 
121 				if (config_data->type ==
122 						GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE)
123 					msleep(3);
124 			}
125 
126 			if (!scl_pd_dis) {
127 				REG_SET(gpio.MASK_reg, regval,
128 						DC_GPIO_SCL_PD_DIS, 1);
129 
130 				if (config_data->type ==
131 						GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE)
132 					msleep(3);
133 			}
134 		}
135 
136 		if (aux_pad_mode) {
137 			/* let pins to get de-asserted
138 			 * before setting pad to I2C mode */
139 			if (config_data->config.ddc.data_en_bit_present ||
140 				config_data->config.ddc.clock_en_bit_present)
141 				/* [anaumov] in DAL2, there was
142 				 * dc_service_delay_in_microseconds(2000); */
143 				msleep(2);
144 
145 			/* set the I2C pad mode */
146 			/* read the register again,
147 			 * some bits may have been changed */
148 			REG_UPDATE(gpio.MASK_reg,
149 					AUX_PAD1_MODE, 0);
150 		}
151 
152 		if (ddc->regs->dc_gpio_aux_ctrl_5 != 0) {
153 				REG_UPDATE(dc_gpio_aux_ctrl_5, DDC_PAD_I2CMODE, 1);
154 		}
155 		//set  DC_IO_aux_rxsel = 2'b01
156 		if (ddc->regs->phy_aux_cntl != 0) {
157 				REG_UPDATE(phy_aux_cntl, AUX_PAD_RXSEL, 1);
158 		}
159 		return GPIO_RESULT_OK;
160 	case GPIO_DDC_CONFIG_TYPE_MODE_AUX:
161 		/* set the AUX pad mode */
162 		if (!aux_pad_mode) {
163 			REG_SET(gpio.MASK_reg, regval,
164 					AUX_PAD1_MODE, 1);
165 		}
166 		if (ddc->regs->dc_gpio_aux_ctrl_5 != 0) {
167 			REG_UPDATE(dc_gpio_aux_ctrl_5,
168 					DDC_PAD_I2CMODE, 0);
169 		}
170 
171 		return GPIO_RESULT_OK;
172 	case GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT:
173 		if (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA) {
174 			REG_UPDATE_3(ddc_setup,
175 				DC_I2C_DDC1_ENABLE, 1,
176 				DC_I2C_DDC1_EDID_DETECT_ENABLE, 1,
177 				DC_I2C_DDC1_EDID_DETECT_MODE, 0);
178 			return GPIO_RESULT_OK;
179 		}
180 	break;
181 	case GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT:
182 		if (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA) {
183 			REG_UPDATE_3(ddc_setup,
184 				DC_I2C_DDC1_ENABLE, 1,
185 				DC_I2C_DDC1_EDID_DETECT_ENABLE, 1,
186 				DC_I2C_DDC1_EDID_DETECT_MODE, 1);
187 			return GPIO_RESULT_OK;
188 		}
189 	break;
190 	case GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING:
191 		if (hw_gpio->base.en <= GPIO_DDC_LINE_DDC_VGA) {
192 			REG_UPDATE_2(ddc_setup,
193 				DC_I2C_DDC1_ENABLE, 0,
194 				DC_I2C_DDC1_EDID_DETECT_ENABLE, 0);
195 			return GPIO_RESULT_OK;
196 		}
197 	break;
198 	}
199 
200 	BREAK_TO_DEBUGGER();
201 
202 	return GPIO_RESULT_NON_SPECIFIC_ERROR;
203 }
204 
205 static const struct hw_gpio_pin_funcs funcs = {
206 	.destroy = dal_hw_ddc_destroy,
207 	.open = dal_hw_gpio_open,
208 	.get_value = dal_hw_gpio_get_value,
209 	.set_value = dal_hw_gpio_set_value,
210 	.set_config = set_config,
211 	.change_mode = dal_hw_gpio_change_mode,
212 	.close = dal_hw_gpio_close,
213 };
214 
215 static void dal_hw_ddc_construct(
216 	struct hw_ddc *ddc,
217 	enum gpio_id id,
218 	uint32_t en,
219 	struct dc_context *ctx)
220 {
221 	dal_hw_gpio_construct(&ddc->base, id, en, ctx);
222 	ddc->base.base.funcs = &funcs;
223 }
224 
225 void dal_hw_ddc_init(
226 	struct hw_ddc **hw_ddc,
227 	struct dc_context *ctx,
228 	enum gpio_id id,
229 	uint32_t en)
230 {
231 	if (en > GPIO_DDC_LINE_MAX) {
232 		ASSERT_CRITICAL(false);
233 		*hw_ddc = NULL;
234 	}
235 
236 	*hw_ddc = kzalloc_obj(struct hw_ddc);
237 	if (!*hw_ddc) {
238 		ASSERT_CRITICAL(false);
239 		return;
240 	}
241 
242 	dal_hw_ddc_construct(*hw_ddc, id, en, ctx);
243 }
244 
245 struct hw_gpio_pin *dal_hw_ddc_get_pin(struct gpio *gpio)
246 {
247 	struct hw_ddc *hw_ddc = dal_gpio_get_ddc(gpio);
248 
249 	return &hw_ddc->base.base;
250 }
251 
252 static void store_registers_ddc_i3cpad(
253 	struct hw_ddc *ddc)
254 {
255 	switch (ddc->base.base.id) {
256 	case GPIO_ID_DDC_DATA:
257 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, &ddc->base.store.mask);
258 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_DATA_A, &ddc->base.store.a);
259 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_DATA_EN, &ddc->base.store.en);
260 		break;
261 	case GPIO_ID_DDC_CLOCK:
262 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, &ddc->base.store.mask);
263 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_CLK_A, &ddc->base.store.a);
264 		REG_GET(dc_i3cpad_control0, DC_I3CPAD_CLK_EN, &ddc->base.store.en);
265 		break;
266 	default:
267 		break;
268 	}
269 }
270 
271 static void restore_registers_ddc_i3cpad(
272 	struct hw_ddc *ddc)
273 {
274 	switch (ddc->base.base.id) {
275 	case GPIO_ID_DDC_DATA:
276 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, ddc->base.store.mask);
277 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_A, ddc->base.store.a);
278 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_EN, ddc->base.store.en);
279 		break;
280 	case GPIO_ID_DDC_CLOCK:
281 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, ddc->base.store.mask);
282 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_A, ddc->base.store.a);
283 		REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_EN, ddc->base.store.en);
284 		break;
285 	default:
286 		break;
287 	}
288 }
289 
290 bool dal_hw_ddc_open_i3cpad(
291 	struct hw_gpio_pin *ptr,
292 	enum gpio_mode mode)
293 {
294 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
295 
296 	store_registers_ddc_i3cpad(ddc);
297 
298 	ptr->opened = (dal_hw_ddc_config_mode_i3cpad(ddc, mode) == GPIO_RESULT_OK);
299 
300 	return ptr->opened;
301 }
302 
303 enum gpio_result dal_hw_ddc_get_value_i3cpad(
304 	const struct hw_gpio_pin *ptr,
305 	uint32_t *value)
306 {
307 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
308 	enum gpio_result result = GPIO_RESULT_OK;
309 
310 	switch (ptr->mode) {
311 	case GPIO_MODE_INPUT:
312 	case GPIO_MODE_OUTPUT:
313 	case GPIO_MODE_HARDWARE:
314 	case GPIO_MODE_FAST_OUTPUT:
315 		switch (ddc->base.base.id) {
316 		case GPIO_ID_DDC_DATA:
317 			REG_GET(dc_i3cpad_control0, DC_I3CPAD_DATA_Y, value);
318 			break;
319 		case GPIO_ID_DDC_CLOCK:
320 			REG_GET(dc_i3cpad_control0, DC_I3CPAD_CLK_Y, value);
321 			break;
322 		default:
323 			break;
324 		}
325 		break;
326 	default:
327 		result = GPIO_RESULT_NON_SPECIFIC_ERROR;
328 	}
329 	return result;
330 }
331 
332 enum gpio_result dal_hw_ddc_set_value_i3cpad(
333 	const struct hw_gpio_pin *ptr,
334 	uint32_t value)
335 {
336 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
337 
338 	switch (ptr->mode) {
339 	case GPIO_MODE_OUTPUT:
340 		switch (ddc->base.base.id) {
341 		case GPIO_ID_DDC_DATA:
342 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_A, value);
343 			break;
344 		case GPIO_ID_DDC_CLOCK:
345 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_A, value);
346 			break;
347 		default:
348 			break;
349 		}
350 		return GPIO_RESULT_OK;
351 	case GPIO_MODE_FAST_OUTPUT:
352 		/* We use (EN) to faster switch (used in DDC GPIO).
353 		 * So (A) is grounded, output is driven by (EN = 0)
354 		 * to pull the line down (output == 0) and (EN=1)
355 		 * then output is tri-state */
356 		switch (ddc->base.base.id) {
357 		case GPIO_ID_DDC_DATA:
358 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_EN, value);
359 			break;
360 		case GPIO_ID_DDC_CLOCK:
361 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_EN, value);
362 			break;
363 		default:
364 			break;
365 		}
366 		return GPIO_RESULT_OK;
367 	default:
368 		return GPIO_RESULT_NON_SPECIFIC_ERROR;
369 	}
370 }
371 
372 enum gpio_result dal_hw_ddc_change_mode_i3cpad(
373 	struct hw_gpio_pin *ptr,
374 	enum gpio_mode mode)
375 {
376 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
377 
378 	return dal_hw_ddc_config_mode_i3cpad(ddc, mode);
379 }
380 
381 enum gpio_result dal_hw_ddc_config_mode_i3cpad(
382 	struct hw_ddc *ddc,
383 	enum gpio_mode mode)
384 {
385 	ddc->base.base.mode = mode;
386 
387 	switch (mode) {
388 	case GPIO_MODE_INPUT:
389 		/* turn off output enable, act as input pin;
390 		 * program the pin as GPIO, mask out signal driven by HW
391 		 */
392 		switch (ddc->base.base.id) {
393 		case GPIO_ID_DDC_DATA:
394 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_EN, 0);
395 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, 1);
396 			break;
397 		case GPIO_ID_DDC_CLOCK:
398 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_EN, 0);
399 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, 1);
400 			break;
401 		default:
402 			break;
403 		}
404 		return GPIO_RESULT_OK;
405 
406 	case GPIO_MODE_OUTPUT:
407 		/* turn on output enable, act as output pin;
408 		 * program the pin as GPIO, mask out signal driven by HW
409 		 */
410 		switch (ddc->base.base.id) {
411 		case GPIO_ID_DDC_DATA:
412 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_A, 0);
413 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, 1);
414 			break;
415 		case GPIO_ID_DDC_CLOCK:
416 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_A, 0);
417 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, 1);
418 			break;
419 		default:
420 			break;
421 		}
422 		return GPIO_RESULT_OK;
423 
424 	case GPIO_MODE_FAST_OUTPUT:
425 		/* grounding the A register then use the EN register bit
426 		 * will have faster effect on the rise time
427 		 */
428 		switch (ddc->base.base.id) {
429 		case GPIO_ID_DDC_DATA:
430 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DATA_A, 0);
431 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, 1);
432 			break;
433 		case GPIO_ID_DDC_CLOCK:
434 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_CLK_A, 0);
435 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, 1);
436 			break;
437 		default:
438 			break;
439 		}
440 		return GPIO_RESULT_OK;
441 
442 	case GPIO_MODE_HARDWARE:
443 		/* program the pin as tri-state, pin is driven by HW */
444 		switch (ddc->base.base.id) {
445 		case GPIO_ID_DDC_DATA:
446 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCDATA_MASK, 0);
447 			break;
448 		case GPIO_ID_DDC_CLOCK:
449 			REG_UPDATE(dc_i3cpad_control0, DC_I3CPAD_DDCCLK_MASK, 0);
450 			break;
451 		default:
452 			break;
453 		}
454 		return GPIO_RESULT_OK;
455 
456 	default:
457 	case GPIO_MODE_INTERRUPT:
458 	/* Interrupt mode supported only by HPD (IrqGpio) old pins. */
459 		return GPIO_RESULT_NON_SPECIFIC_ERROR;
460 	}
461 }
462 
463 static enum gpio_result dal_hw_ddc_set_config_i3cpad(
464 	struct hw_gpio_pin *ptr,
465 	const struct gpio_config_data *config_data)
466 {
467 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
468 
469 	switch (config_data->config.ddc.type) {
470 	/* For ASICs with i3cpad module there is no dual pad mode for i3cpads */
471 	case GPIO_DDC_CONFIG_TYPE_MODE_I2C:
472 		/* Enable the RX for the PAD (it is disabled by default). */
473 		REG_UPDATE(dc_i3cpad_control1, DC_I3CPAD_RXSEL, 0);
474 		return GPIO_RESULT_OK;
475 	case GPIO_DDC_CONFIG_TYPE_MODE_AUX:
476 		return GPIO_RESULT_OK;
477 
478 	case GPIO_DDC_CONFIG_TYPE_POLL_FOR_CONNECT:
479 		REG_UPDATE_3(ddc_setup,
480 			DC_I2C_DDC1_ENABLE, 1,
481 			DC_I2C_DDC1_EDID_DETECT_ENABLE, 1,
482 			DC_I2C_DDC1_EDID_DETECT_MODE, 0);
483 		return GPIO_RESULT_OK;
484 
485 	case GPIO_DDC_CONFIG_TYPE_POLL_FOR_DISCONNECT:
486 		REG_UPDATE_3(ddc_setup,
487 			DC_I2C_DDC1_ENABLE, 1,
488 			DC_I2C_DDC1_EDID_DETECT_ENABLE, 1,
489 			DC_I2C_DDC1_EDID_DETECT_MODE, 1);
490 		return GPIO_RESULT_OK;
491 
492 	case GPIO_DDC_CONFIG_TYPE_DISABLE_POLLING:
493 		REG_UPDATE_2(ddc_setup,
494 			DC_I2C_DDC1_ENABLE, 0,
495 			DC_I2C_DDC1_EDID_DETECT_ENABLE, 0);
496 		return GPIO_RESULT_OK;
497 	}
498 
499 	BREAK_TO_DEBUGGER();
500 	return GPIO_RESULT_NON_SPECIFIC_ERROR;
501 }
502 
503 void dal_hw_ddc_close_i3cpad(
504 	struct hw_gpio_pin *ptr)
505 {
506 	struct hw_ddc *ddc = HW_DDC_FROM_BASE(ptr);
507 
508 	restore_registers_ddc_i3cpad(ddc);
509 
510 	ptr->mode = GPIO_MODE_UNKNOWN;
511 	ptr->opened = false;
512 }
513 
514 static const struct hw_gpio_pin_funcs funcs_i3cpad = {
515 	.destroy = dal_hw_ddc_destroy,
516 	.open = dal_hw_ddc_open_i3cpad,
517 	.get_value = dal_hw_ddc_get_value_i3cpad,
518 	.set_value = dal_hw_ddc_set_value_i3cpad,
519 	.set_config = dal_hw_ddc_set_config_i3cpad,
520 	.change_mode = dal_hw_ddc_change_mode_i3cpad,
521 	.close = dal_hw_ddc_close_i3cpad,
522 };
523 
524 static void dal_hw_ddc_construct_i3cpad(
525 	struct hw_ddc *ddc,
526 	enum gpio_id id,
527 	uint32_t en,
528 	struct dc_context *ctx)
529 {
530 	dal_hw_gpio_construct(&ddc->base, id, en, ctx);
531 	ddc->base.base.funcs = &funcs_i3cpad;
532 }
533 
534 void dal_hw_ddc_init_i3cpad(
535 	struct hw_ddc **hw_ddc,
536 	struct dc_context *ctx,
537 	enum gpio_id id,
538 	uint32_t en)
539 {
540 	if (en > GPIO_DDC_LINE_MAX) {
541 		ASSERT_CRITICAL(false);
542 		*hw_ddc = NULL;
543 	}
544 
545 	*hw_ddc = kzalloc_obj(struct hw_ddc);
546 	if (!*hw_ddc) {
547 		ASSERT_CRITICAL(false);
548 		return;
549 	}
550 
551 	dal_hw_ddc_construct_i3cpad(*hw_ddc, id, en, ctx);
552 }
553