.. | .. |
---|
146 | 146 | return true; |
---|
147 | 147 | } |
---|
148 | 148 | |
---|
| 149 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 150 | +static bool dce60_setup_scaling_configuration( |
---|
| 151 | + struct dce_transform *xfm_dce, |
---|
| 152 | + const struct scaler_data *data) |
---|
| 153 | +{ |
---|
| 154 | + REG_SET(SCL_BYPASS_CONTROL, 0, SCL_BYPASS_MODE, 0); |
---|
| 155 | + |
---|
| 156 | + if (data->taps.h_taps + data->taps.v_taps <= 2) { |
---|
| 157 | + /* Set bypass */ |
---|
| 158 | + |
---|
| 159 | + /* DCE6 has no SCL_MODE register, skip scale mode programming */ |
---|
| 160 | + |
---|
| 161 | + return false; |
---|
| 162 | + } |
---|
| 163 | + |
---|
| 164 | + REG_SET_2(SCL_TAP_CONTROL, 0, |
---|
| 165 | + SCL_H_NUM_OF_TAPS, data->taps.h_taps - 1, |
---|
| 166 | + SCL_V_NUM_OF_TAPS, data->taps.v_taps - 1); |
---|
| 167 | + |
---|
| 168 | + /* DCE6 has no SCL_MODE register, skip scale mode programming */ |
---|
| 169 | + |
---|
| 170 | + /* DCE6 has no SCL_BOUNDARY_MODE bit, skip replace out of bound pixels */ |
---|
| 171 | + |
---|
| 172 | + return true; |
---|
| 173 | +} |
---|
| 174 | +#endif |
---|
| 175 | + |
---|
149 | 176 | static void program_overscan( |
---|
150 | 177 | struct dce_transform *xfm_dce, |
---|
151 | 178 | const struct scaler_data *data) |
---|
.. | .. |
---|
279 | 306 | inits->v_init.fraction = dc_fixpt_u0d19(v_init) << 5; |
---|
280 | 307 | } |
---|
281 | 308 | |
---|
| 309 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 310 | +static void dce60_calculate_inits( |
---|
| 311 | + struct dce_transform *xfm_dce, |
---|
| 312 | + const struct scaler_data *data, |
---|
| 313 | + struct sclh_ratios_inits *inits) |
---|
| 314 | +{ |
---|
| 315 | + struct fixed31_32 v_init; |
---|
| 316 | + |
---|
| 317 | + inits->h_int_scale_ratio = |
---|
| 318 | + dc_fixpt_u2d19(data->ratios.horz) << 5; |
---|
| 319 | + inits->v_int_scale_ratio = |
---|
| 320 | + dc_fixpt_u2d19(data->ratios.vert) << 5; |
---|
| 321 | + |
---|
| 322 | + /* DCE6 h_init_luma setting inspired by DCE110 */ |
---|
| 323 | + inits->h_init_luma.integer = 1; |
---|
| 324 | + |
---|
| 325 | + /* DCE6 h_init_chroma setting inspired by DCE110 */ |
---|
| 326 | + inits->h_init_chroma.integer = 1; |
---|
| 327 | + |
---|
| 328 | + v_init = |
---|
| 329 | + dc_fixpt_div_int( |
---|
| 330 | + dc_fixpt_add( |
---|
| 331 | + data->ratios.vert, |
---|
| 332 | + dc_fixpt_from_int(data->taps.v_taps + 1)), |
---|
| 333 | + 2); |
---|
| 334 | + inits->v_init.integer = dc_fixpt_floor(v_init); |
---|
| 335 | + inits->v_init.fraction = dc_fixpt_u0d19(v_init) << 5; |
---|
| 336 | +} |
---|
| 337 | +#endif |
---|
| 338 | + |
---|
282 | 339 | static void program_scl_ratios_inits( |
---|
283 | 340 | struct dce_transform *xfm_dce, |
---|
284 | 341 | struct scl_ratios_inits *inits) |
---|
.. | .. |
---|
300 | 357 | |
---|
301 | 358 | REG_WRITE(SCL_AUTOMATIC_MODE_CONTROL, 0); |
---|
302 | 359 | } |
---|
| 360 | + |
---|
| 361 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 362 | +static void dce60_program_scl_ratios_inits( |
---|
| 363 | + struct dce_transform *xfm_dce, |
---|
| 364 | + struct sclh_ratios_inits *inits) |
---|
| 365 | +{ |
---|
| 366 | + |
---|
| 367 | + REG_SET(SCL_HORZ_FILTER_SCALE_RATIO, 0, |
---|
| 368 | + SCL_H_SCALE_RATIO, inits->h_int_scale_ratio); |
---|
| 369 | + |
---|
| 370 | + REG_SET(SCL_VERT_FILTER_SCALE_RATIO, 0, |
---|
| 371 | + SCL_V_SCALE_RATIO, inits->v_int_scale_ratio); |
---|
| 372 | + |
---|
| 373 | + /* DCE6 has SCL_HORZ_FILTER_INIT_RGB_LUMA register */ |
---|
| 374 | + REG_SET_2(SCL_HORZ_FILTER_INIT_RGB_LUMA, 0, |
---|
| 375 | + SCL_H_INIT_INT_RGB_Y, inits->h_init_luma.integer, |
---|
| 376 | + SCL_H_INIT_FRAC_RGB_Y, inits->h_init_luma.fraction); |
---|
| 377 | + |
---|
| 378 | + /* DCE6 has SCL_HORZ_FILTER_INIT_CHROMA register */ |
---|
| 379 | + REG_SET_2(SCL_HORZ_FILTER_INIT_CHROMA, 0, |
---|
| 380 | + SCL_H_INIT_INT_CBCR, inits->h_init_chroma.integer, |
---|
| 381 | + SCL_H_INIT_FRAC_CBCR, inits->h_init_chroma.fraction); |
---|
| 382 | + |
---|
| 383 | + REG_SET_2(SCL_VERT_FILTER_INIT, 0, |
---|
| 384 | + SCL_V_INIT_INT, inits->v_init.integer, |
---|
| 385 | + SCL_V_INIT_FRAC, inits->v_init.fraction); |
---|
| 386 | + |
---|
| 387 | + REG_WRITE(SCL_AUTOMATIC_MODE_CONTROL, 0); |
---|
| 388 | +} |
---|
| 389 | +#endif |
---|
303 | 390 | |
---|
304 | 391 | static const uint16_t *get_filter_coeffs_16p(int taps, struct fixed31_32 ratio) |
---|
305 | 392 | { |
---|
.. | .. |
---|
398 | 485 | |
---|
399 | 486 | REG_UPDATE(LB_DATA_FORMAT, ALPHA_EN, data->lb_params.alpha_en); |
---|
400 | 487 | } |
---|
| 488 | + |
---|
| 489 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 490 | +static void dce60_transform_set_scaler( |
---|
| 491 | + struct transform *xfm, |
---|
| 492 | + const struct scaler_data *data) |
---|
| 493 | +{ |
---|
| 494 | + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); |
---|
| 495 | + bool is_scaling_required; |
---|
| 496 | + bool filter_updated = false; |
---|
| 497 | + const uint16_t *coeffs_v, *coeffs_h; |
---|
| 498 | + |
---|
| 499 | + /*Use whole line buffer memory always*/ |
---|
| 500 | + REG_SET(DC_LB_MEMORY_SPLIT, 0, |
---|
| 501 | + DC_LB_MEMORY_CONFIG, 0); |
---|
| 502 | + |
---|
| 503 | + REG_SET(DC_LB_MEM_SIZE, 0, |
---|
| 504 | + DC_LB_MEM_SIZE, xfm_dce->lb_memory_size); |
---|
| 505 | + |
---|
| 506 | + /* Clear SCL_F_SHARP_CONTROL value to 0 */ |
---|
| 507 | + REG_WRITE(SCL_F_SHARP_CONTROL, 0); |
---|
| 508 | + |
---|
| 509 | + /* 1. Program overscan */ |
---|
| 510 | + program_overscan(xfm_dce, data); |
---|
| 511 | + |
---|
| 512 | + /* 2. Program taps and configuration */ |
---|
| 513 | + is_scaling_required = dce60_setup_scaling_configuration(xfm_dce, data); |
---|
| 514 | + |
---|
| 515 | + if (is_scaling_required) { |
---|
| 516 | + /* 3. Calculate and program ratio, DCE6 filter initialization */ |
---|
| 517 | + struct sclh_ratios_inits inits = { 0 }; |
---|
| 518 | + |
---|
| 519 | + /* DCE6 has specific calculate_inits() function */ |
---|
| 520 | + dce60_calculate_inits(xfm_dce, data, &inits); |
---|
| 521 | + |
---|
| 522 | + /* DCE6 has specific program_scl_ratios_inits() function */ |
---|
| 523 | + dce60_program_scl_ratios_inits(xfm_dce, &inits); |
---|
| 524 | + |
---|
| 525 | + coeffs_v = get_filter_coeffs_16p(data->taps.v_taps, data->ratios.vert); |
---|
| 526 | + coeffs_h = get_filter_coeffs_16p(data->taps.h_taps, data->ratios.horz); |
---|
| 527 | + |
---|
| 528 | + if (coeffs_v != xfm_dce->filter_v || coeffs_h != xfm_dce->filter_h) { |
---|
| 529 | + /* 4. Program vertical filters */ |
---|
| 530 | + if (xfm_dce->filter_v == NULL) |
---|
| 531 | + REG_SET(SCL_VERT_FILTER_CONTROL, 0, |
---|
| 532 | + SCL_V_2TAP_HARDCODE_COEF_EN, 0); |
---|
| 533 | + program_multi_taps_filter( |
---|
| 534 | + xfm_dce, |
---|
| 535 | + data->taps.v_taps, |
---|
| 536 | + coeffs_v, |
---|
| 537 | + FILTER_TYPE_RGB_Y_VERTICAL); |
---|
| 538 | + program_multi_taps_filter( |
---|
| 539 | + xfm_dce, |
---|
| 540 | + data->taps.v_taps, |
---|
| 541 | + coeffs_v, |
---|
| 542 | + FILTER_TYPE_ALPHA_VERTICAL); |
---|
| 543 | + |
---|
| 544 | + /* 5. Program horizontal filters */ |
---|
| 545 | + if (xfm_dce->filter_h == NULL) |
---|
| 546 | + REG_SET(SCL_HORZ_FILTER_CONTROL, 0, |
---|
| 547 | + SCL_H_2TAP_HARDCODE_COEF_EN, 0); |
---|
| 548 | + program_multi_taps_filter( |
---|
| 549 | + xfm_dce, |
---|
| 550 | + data->taps.h_taps, |
---|
| 551 | + coeffs_h, |
---|
| 552 | + FILTER_TYPE_RGB_Y_HORIZONTAL); |
---|
| 553 | + program_multi_taps_filter( |
---|
| 554 | + xfm_dce, |
---|
| 555 | + data->taps.h_taps, |
---|
| 556 | + coeffs_h, |
---|
| 557 | + FILTER_TYPE_ALPHA_HORIZONTAL); |
---|
| 558 | + |
---|
| 559 | + xfm_dce->filter_v = coeffs_v; |
---|
| 560 | + xfm_dce->filter_h = coeffs_h; |
---|
| 561 | + filter_updated = true; |
---|
| 562 | + } |
---|
| 563 | + } |
---|
| 564 | + |
---|
| 565 | + /* 6. Program the viewport */ |
---|
| 566 | + program_viewport(xfm_dce, &data->viewport); |
---|
| 567 | + |
---|
| 568 | + /* DCE6 has no SCL_COEF_UPDATE_COMPLETE bit to flip to new coefficient memory */ |
---|
| 569 | + |
---|
| 570 | + /* DCE6 DATA_FORMAT register does not support ALPHA_EN */ |
---|
| 571 | +} |
---|
| 572 | +#endif |
---|
401 | 573 | |
---|
402 | 574 | /***************************************************************************** |
---|
403 | 575 | * set_clamp |
---|
.. | .. |
---|
664 | 836 | bit_depth_params->flags.HIGHPASS_RANDOM); |
---|
665 | 837 | } |
---|
666 | 838 | |
---|
| 839 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 840 | +/***************************************************************************** |
---|
| 841 | + * dce60_transform_bit_depth_reduction program |
---|
| 842 | + * |
---|
| 843 | + * @brief |
---|
| 844 | + * Programs the DCP bit depth reduction registers (Clamp, Round/Truncate, |
---|
| 845 | + * Dither) for dce |
---|
| 846 | + * |
---|
| 847 | + * @param depth : bit depth to set the clamp to (should match denorm) |
---|
| 848 | + * |
---|
| 849 | + ******************************************************************************/ |
---|
| 850 | +static void dce60_program_bit_depth_reduction( |
---|
| 851 | + struct dce_transform *xfm_dce, |
---|
| 852 | + enum dc_color_depth depth, |
---|
| 853 | + const struct bit_depth_reduction_params *bit_depth_params) |
---|
| 854 | +{ |
---|
| 855 | + enum dcp_out_trunc_round_depth trunc_round_depth; |
---|
| 856 | + enum dcp_out_trunc_round_mode trunc_mode; |
---|
| 857 | + bool spatial_dither_enable; |
---|
| 858 | + |
---|
| 859 | + ASSERT(depth < COLOR_DEPTH_121212); /* Invalid clamp bit depth */ |
---|
| 860 | + |
---|
| 861 | + spatial_dither_enable = bit_depth_params->flags.SPATIAL_DITHER_ENABLED; |
---|
| 862 | + /* Default to 12 bit truncation without rounding */ |
---|
| 863 | + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; |
---|
| 864 | + trunc_mode = DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; |
---|
| 865 | + |
---|
| 866 | + if (bit_depth_params->flags.TRUNCATE_ENABLED) { |
---|
| 867 | + /* Don't enable dithering if truncation is enabled */ |
---|
| 868 | + spatial_dither_enable = false; |
---|
| 869 | + trunc_mode = bit_depth_params->flags.TRUNCATE_MODE ? |
---|
| 870 | + DCP_OUT_TRUNC_ROUND_MODE_ROUND : |
---|
| 871 | + DCP_OUT_TRUNC_ROUND_MODE_TRUNCATE; |
---|
| 872 | + |
---|
| 873 | + if (bit_depth_params->flags.TRUNCATE_DEPTH == 0 || |
---|
| 874 | + bit_depth_params->flags.TRUNCATE_DEPTH == 1) |
---|
| 875 | + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_8BIT; |
---|
| 876 | + else if (bit_depth_params->flags.TRUNCATE_DEPTH == 2) |
---|
| 877 | + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_10BIT; |
---|
| 878 | + else { |
---|
| 879 | + /* |
---|
| 880 | + * Invalid truncate/round depth. Setting here to 12bit |
---|
| 881 | + * to prevent use-before-initialize errors. |
---|
| 882 | + */ |
---|
| 883 | + trunc_round_depth = DCP_OUT_TRUNC_ROUND_DEPTH_12BIT; |
---|
| 884 | + BREAK_TO_DEBUGGER(); |
---|
| 885 | + } |
---|
| 886 | + } |
---|
| 887 | + |
---|
| 888 | + /* DCE6 has no OUT_CLAMP_CONTROL_* registers - set_clamp() is skipped */ |
---|
| 889 | + set_round(xfm_dce, trunc_mode, trunc_round_depth); |
---|
| 890 | + set_dither(xfm_dce, |
---|
| 891 | + spatial_dither_enable, |
---|
| 892 | + DCP_SPATIAL_DITHER_MODE_A_AA_A, |
---|
| 893 | + DCP_SPATIAL_DITHER_DEPTH_30BPP, |
---|
| 894 | + bit_depth_params->flags.FRAME_RANDOM, |
---|
| 895 | + bit_depth_params->flags.RGB_RANDOM, |
---|
| 896 | + bit_depth_params->flags.HIGHPASS_RANDOM); |
---|
| 897 | +} |
---|
| 898 | +#endif |
---|
| 899 | + |
---|
667 | 900 | static int dce_transform_get_max_num_of_supported_lines( |
---|
668 | 901 | struct dce_transform *xfm_dce, |
---|
669 | 902 | enum lb_pixel_depth depth, |
---|
.. | .. |
---|
778 | 1011 | color_depth = COLOR_DEPTH_101010; |
---|
779 | 1012 | pixel_depth = 0; |
---|
780 | 1013 | expan_mode = 1; |
---|
781 | | - BREAK_TO_DEBUGGER(); |
---|
| 1014 | + DC_LOG_DC("The pixel depth %d is not valid, set COLOR_DEPTH_101010 instead.", depth); |
---|
782 | 1015 | break; |
---|
783 | 1016 | } |
---|
784 | 1017 | |
---|
.. | .. |
---|
792 | 1025 | if (!(xfm_dce->lb_pixel_depth_supported & depth)) { |
---|
793 | 1026 | /*we should use unsupported capabilities |
---|
794 | 1027 | * unless it is required by w/a*/ |
---|
| 1028 | + DC_LOG_DC("%s: Capability not supported", __func__); |
---|
| 1029 | + } |
---|
| 1030 | +} |
---|
| 1031 | + |
---|
| 1032 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 1033 | +static void dce60_transform_set_pixel_storage_depth( |
---|
| 1034 | + struct transform *xfm, |
---|
| 1035 | + enum lb_pixel_depth depth, |
---|
| 1036 | + const struct bit_depth_reduction_params *bit_depth_params) |
---|
| 1037 | +{ |
---|
| 1038 | + struct dce_transform *xfm_dce = TO_DCE_TRANSFORM(xfm); |
---|
| 1039 | + int pixel_depth, expan_mode; |
---|
| 1040 | + enum dc_color_depth color_depth; |
---|
| 1041 | + |
---|
| 1042 | + switch (depth) { |
---|
| 1043 | + case LB_PIXEL_DEPTH_18BPP: |
---|
| 1044 | + color_depth = COLOR_DEPTH_666; |
---|
| 1045 | + pixel_depth = 2; |
---|
| 1046 | + expan_mode = 1; |
---|
| 1047 | + break; |
---|
| 1048 | + case LB_PIXEL_DEPTH_24BPP: |
---|
| 1049 | + color_depth = COLOR_DEPTH_888; |
---|
| 1050 | + pixel_depth = 1; |
---|
| 1051 | + expan_mode = 1; |
---|
| 1052 | + break; |
---|
| 1053 | + case LB_PIXEL_DEPTH_30BPP: |
---|
| 1054 | + color_depth = COLOR_DEPTH_101010; |
---|
| 1055 | + pixel_depth = 0; |
---|
| 1056 | + expan_mode = 1; |
---|
| 1057 | + break; |
---|
| 1058 | + case LB_PIXEL_DEPTH_36BPP: |
---|
| 1059 | + color_depth = COLOR_DEPTH_121212; |
---|
| 1060 | + pixel_depth = 3; |
---|
| 1061 | + expan_mode = 0; |
---|
| 1062 | + break; |
---|
| 1063 | + default: |
---|
| 1064 | + color_depth = COLOR_DEPTH_101010; |
---|
| 1065 | + pixel_depth = 0; |
---|
| 1066 | + expan_mode = 1; |
---|
| 1067 | + BREAK_TO_DEBUGGER(); |
---|
| 1068 | + break; |
---|
| 1069 | + } |
---|
| 1070 | + |
---|
| 1071 | + set_denormalization(xfm_dce, color_depth); |
---|
| 1072 | + dce60_program_bit_depth_reduction(xfm_dce, color_depth, bit_depth_params); |
---|
| 1073 | + |
---|
| 1074 | + /* DATA_FORMAT in DCE6 does not have PIXEL_DEPTH and PIXEL_EXPAN_MODE masks */ |
---|
| 1075 | + |
---|
| 1076 | + if (!(xfm_dce->lb_pixel_depth_supported & depth)) { |
---|
| 1077 | + /*we should use unsupported capabilities |
---|
| 1078 | + * unless it is required by w/a*/ |
---|
795 | 1079 | DC_LOG_WARNING("%s: Capability not supported", |
---|
796 | 1080 | __func__); |
---|
797 | 1081 | } |
---|
798 | 1082 | } |
---|
| 1083 | +#endif |
---|
799 | 1084 | |
---|
800 | 1085 | static void program_gamut_remap( |
---|
801 | 1086 | struct dce_transform *xfm_dce, |
---|
.. | .. |
---|
1335 | 1620 | .transform_get_optimal_number_of_taps = dce_transform_get_optimal_number_of_taps |
---|
1336 | 1621 | }; |
---|
1337 | 1622 | |
---|
| 1623 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 1624 | +static const struct transform_funcs dce60_transform_funcs = { |
---|
| 1625 | + .transform_reset = dce_transform_reset, |
---|
| 1626 | + .transform_set_scaler = dce60_transform_set_scaler, |
---|
| 1627 | + .transform_set_gamut_remap = dce_transform_set_gamut_remap, |
---|
| 1628 | + .opp_set_csc_adjustment = dce110_opp_set_csc_adjustment, |
---|
| 1629 | + .opp_set_csc_default = dce110_opp_set_csc_default, |
---|
| 1630 | + .opp_power_on_regamma_lut = dce110_opp_power_on_regamma_lut, |
---|
| 1631 | + .opp_program_regamma_pwl = dce110_opp_program_regamma_pwl, |
---|
| 1632 | + .opp_set_regamma_mode = dce110_opp_set_regamma_mode, |
---|
| 1633 | + .transform_set_pixel_storage_depth = dce60_transform_set_pixel_storage_depth, |
---|
| 1634 | + .transform_get_optimal_number_of_taps = dce_transform_get_optimal_number_of_taps |
---|
| 1635 | +}; |
---|
| 1636 | +#endif |
---|
| 1637 | + |
---|
1338 | 1638 | /*****************************************/ |
---|
1339 | 1639 | /* Constructor, Destructor */ |
---|
1340 | 1640 | /*****************************************/ |
---|
.. | .. |
---|
1365 | 1665 | xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; |
---|
1366 | 1666 | xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ |
---|
1367 | 1667 | } |
---|
| 1668 | + |
---|
| 1669 | +#if defined(CONFIG_DRM_AMD_DC_SI) |
---|
| 1670 | +void dce60_transform_construct( |
---|
| 1671 | + struct dce_transform *xfm_dce, |
---|
| 1672 | + struct dc_context *ctx, |
---|
| 1673 | + uint32_t inst, |
---|
| 1674 | + const struct dce_transform_registers *regs, |
---|
| 1675 | + const struct dce_transform_shift *xfm_shift, |
---|
| 1676 | + const struct dce_transform_mask *xfm_mask) |
---|
| 1677 | +{ |
---|
| 1678 | + xfm_dce->base.ctx = ctx; |
---|
| 1679 | + |
---|
| 1680 | + xfm_dce->base.inst = inst; |
---|
| 1681 | + xfm_dce->base.funcs = &dce60_transform_funcs; |
---|
| 1682 | + |
---|
| 1683 | + xfm_dce->regs = regs; |
---|
| 1684 | + xfm_dce->xfm_shift = xfm_shift; |
---|
| 1685 | + xfm_dce->xfm_mask = xfm_mask; |
---|
| 1686 | + |
---|
| 1687 | + xfm_dce->prescaler_on = true; |
---|
| 1688 | + xfm_dce->lb_pixel_depth_supported = |
---|
| 1689 | + LB_PIXEL_DEPTH_18BPP | |
---|
| 1690 | + LB_PIXEL_DEPTH_24BPP | |
---|
| 1691 | + LB_PIXEL_DEPTH_30BPP; |
---|
| 1692 | + |
---|
| 1693 | + xfm_dce->lb_bits_per_entry = LB_BITS_PER_ENTRY; |
---|
| 1694 | + xfm_dce->lb_memory_size = LB_TOTAL_NUMBER_OF_ENTRIES; /*0x6B0*/ |
---|
| 1695 | +} |
---|
| 1696 | +#endif |
---|