18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics Co.Ltd 48c2ecf20Sopenharmony_ci * Authors: Joonyoung Shim <jy0922.shim@samsung.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "exynos_drm_crtc.h" 148c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h" 158c2ecf20Sopenharmony_ci#include "exynos_drm_fb.h" 168c2ecf20Sopenharmony_ci#include "exynos_drm_gem.h" 178c2ecf20Sopenharmony_ci#include "exynos_drm_plane.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * This function is to get X or Y size shown via screen. This needs length and 218c2ecf20Sopenharmony_ci * start position of CRTC. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * <--- length ---> 248c2ecf20Sopenharmony_ci * CRTC ---------------- 258c2ecf20Sopenharmony_ci * ^ start ^ end 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * There are six cases from a to f. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * <----- SCREEN -----> 308c2ecf20Sopenharmony_ci * 0 last 318c2ecf20Sopenharmony_ci * ----------|------------------|---------- 328c2ecf20Sopenharmony_ci * CRTCs 338c2ecf20Sopenharmony_ci * a ------- 348c2ecf20Sopenharmony_ci * b ------- 358c2ecf20Sopenharmony_ci * c -------------------------- 368c2ecf20Sopenharmony_ci * d -------- 378c2ecf20Sopenharmony_ci * e ------- 388c2ecf20Sopenharmony_ci * f ------- 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic int exynos_plane_get_size(int start, unsigned length, unsigned last) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int end = start + length; 438c2ecf20Sopenharmony_ci int size = 0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (start <= 0) { 468c2ecf20Sopenharmony_ci if (end > 0) 478c2ecf20Sopenharmony_ci size = min_t(unsigned, end, last); 488c2ecf20Sopenharmony_ci } else if (start <= last) { 498c2ecf20Sopenharmony_ci size = min_t(unsigned, last - start, length); 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return size; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct drm_plane_state *state = &exynos_state->base; 588c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->crtc; 598c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state = 608c2ecf20Sopenharmony_ci drm_atomic_get_existing_crtc_state(state->state, crtc); 618c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->adjusted_mode; 628c2ecf20Sopenharmony_ci int crtc_x, crtc_y; 638c2ecf20Sopenharmony_ci unsigned int crtc_w, crtc_h; 648c2ecf20Sopenharmony_ci unsigned int src_x, src_y; 658c2ecf20Sopenharmony_ci unsigned int src_w, src_h; 668c2ecf20Sopenharmony_ci unsigned int actual_w; 678c2ecf20Sopenharmony_ci unsigned int actual_h; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * The original src/dest coordinates are stored in exynos_state->base, 718c2ecf20Sopenharmony_ci * but we want to keep another copy internal to our driver that we can 728c2ecf20Sopenharmony_ci * clip/modify ourselves. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci crtc_x = state->crtc_x; 768c2ecf20Sopenharmony_ci crtc_y = state->crtc_y; 778c2ecf20Sopenharmony_ci crtc_w = state->crtc_w; 788c2ecf20Sopenharmony_ci crtc_h = state->crtc_h; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci src_x = state->src_x >> 16; 818c2ecf20Sopenharmony_ci src_y = state->src_y >> 16; 828c2ecf20Sopenharmony_ci src_w = state->src_w >> 16; 838c2ecf20Sopenharmony_ci src_h = state->src_h >> 16; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* set ratio */ 868c2ecf20Sopenharmony_ci exynos_state->h_ratio = (src_w << 16) / crtc_w; 878c2ecf20Sopenharmony_ci exynos_state->v_ratio = (src_h << 16) / crtc_h; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* clip to visible area */ 908c2ecf20Sopenharmony_ci actual_w = exynos_plane_get_size(crtc_x, crtc_w, mode->hdisplay); 918c2ecf20Sopenharmony_ci actual_h = exynos_plane_get_size(crtc_y, crtc_h, mode->vdisplay); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (crtc_x < 0) { 948c2ecf20Sopenharmony_ci if (actual_w) 958c2ecf20Sopenharmony_ci src_x += ((-crtc_x) * exynos_state->h_ratio) >> 16; 968c2ecf20Sopenharmony_ci crtc_x = 0; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (crtc_y < 0) { 1008c2ecf20Sopenharmony_ci if (actual_h) 1018c2ecf20Sopenharmony_ci src_y += ((-crtc_y) * exynos_state->v_ratio) >> 16; 1028c2ecf20Sopenharmony_ci crtc_y = 0; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* set drm framebuffer data. */ 1068c2ecf20Sopenharmony_ci exynos_state->src.x = src_x; 1078c2ecf20Sopenharmony_ci exynos_state->src.y = src_y; 1088c2ecf20Sopenharmony_ci exynos_state->src.w = (actual_w * exynos_state->h_ratio) >> 16; 1098c2ecf20Sopenharmony_ci exynos_state->src.h = (actual_h * exynos_state->v_ratio) >> 16; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* set plane range to be displayed. */ 1128c2ecf20Sopenharmony_ci exynos_state->crtc.x = crtc_x; 1138c2ecf20Sopenharmony_ci exynos_state->crtc.y = crtc_y; 1148c2ecf20Sopenharmony_ci exynos_state->crtc.w = actual_w; 1158c2ecf20Sopenharmony_ci exynos_state->crtc.h = actual_h; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(crtc->dev->dev, 1188c2ecf20Sopenharmony_ci "plane : offset_x/y(%d,%d), width/height(%d,%d)", 1198c2ecf20Sopenharmony_ci exynos_state->crtc.x, exynos_state->crtc.y, 1208c2ecf20Sopenharmony_ci exynos_state->crtc.w, exynos_state->crtc.h); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void exynos_drm_plane_reset(struct drm_plane *plane) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 1268c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *exynos_state; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (plane->state) { 1298c2ecf20Sopenharmony_ci exynos_state = to_exynos_plane_state(plane->state); 1308c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_destroy_state(plane->state); 1318c2ecf20Sopenharmony_ci kfree(exynos_state); 1328c2ecf20Sopenharmony_ci plane->state = NULL; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci exynos_state = kzalloc(sizeof(*exynos_state), GFP_KERNEL); 1368c2ecf20Sopenharmony_ci if (exynos_state) { 1378c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_reset(plane, &exynos_state->base); 1388c2ecf20Sopenharmony_ci plane->state->zpos = exynos_plane->config->zpos; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct drm_plane_state * 1438c2ecf20Sopenharmony_ciexynos_drm_plane_duplicate_state(struct drm_plane *plane) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *exynos_state; 1468c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *copy; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci exynos_state = to_exynos_plane_state(plane->state); 1498c2ecf20Sopenharmony_ci copy = kzalloc(sizeof(*exynos_state), GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!copy) 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_duplicate_state(plane, ©->base); 1548c2ecf20Sopenharmony_ci return ©->base; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void exynos_drm_plane_destroy_state(struct drm_plane *plane, 1588c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *old_exynos_state = 1618c2ecf20Sopenharmony_ci to_exynos_plane_state(old_state); 1628c2ecf20Sopenharmony_ci __drm_atomic_helper_plane_destroy_state(old_state); 1638c2ecf20Sopenharmony_ci kfree(old_exynos_state); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct drm_plane_funcs exynos_plane_funcs = { 1678c2ecf20Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 1688c2ecf20Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 1698c2ecf20Sopenharmony_ci .destroy = drm_plane_cleanup, 1708c2ecf20Sopenharmony_ci .reset = exynos_drm_plane_reset, 1718c2ecf20Sopenharmony_ci .atomic_duplicate_state = exynos_drm_plane_duplicate_state, 1728c2ecf20Sopenharmony_ci .atomic_destroy_state = exynos_drm_plane_destroy_state, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int 1768c2ecf20Sopenharmony_ciexynos_drm_plane_check_format(const struct exynos_drm_plane_config *config, 1778c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *state) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = state->base.fb; 1808c2ecf20Sopenharmony_ci struct drm_device *dev = fb->dev; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci switch (fb->modifier) { 1838c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 1848c2ecf20Sopenharmony_ci if (!(config->capabilities & EXYNOS_DRM_PLANE_CAP_TILE)) 1858c2ecf20Sopenharmony_ci return -ENOTSUPP; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci case DRM_FORMAT_MOD_LINEAR: 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci default: 1928c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "unsupported pixel format modifier"); 1938c2ecf20Sopenharmony_ci return -ENOTSUPP; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int 2008c2ecf20Sopenharmony_ciexynos_drm_plane_check_size(const struct exynos_drm_plane_config *config, 2018c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *state) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct drm_crtc *crtc = state->base.crtc; 2048c2ecf20Sopenharmony_ci bool width_ok = false, height_ok = false; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (config->capabilities & EXYNOS_DRM_PLANE_CAP_SCALE) 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (state->src.w == state->crtc.w) 2108c2ecf20Sopenharmony_ci width_ok = true; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (state->src.h == state->crtc.h) 2138c2ecf20Sopenharmony_ci height_ok = true; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && 2168c2ecf20Sopenharmony_ci state->h_ratio == (1 << 15)) 2178c2ecf20Sopenharmony_ci width_ok = true; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if ((config->capabilities & EXYNOS_DRM_PLANE_CAP_DOUBLE) && 2208c2ecf20Sopenharmony_ci state->v_ratio == (1 << 15)) 2218c2ecf20Sopenharmony_ci height_ok = true; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (width_ok && height_ok) 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(crtc->dev->dev, "scaling mode is not supported"); 2278c2ecf20Sopenharmony_ci return -ENOTSUPP; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int exynos_plane_atomic_check(struct drm_plane *plane, 2318c2ecf20Sopenharmony_ci struct drm_plane_state *state) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 2348c2ecf20Sopenharmony_ci struct exynos_drm_plane_state *exynos_state = 2358c2ecf20Sopenharmony_ci to_exynos_plane_state(state); 2368c2ecf20Sopenharmony_ci int ret = 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!state->crtc || !state->fb) 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* translate state into exynos_state */ 2428c2ecf20Sopenharmony_ci exynos_plane_mode_set(exynos_state); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ret = exynos_drm_plane_check_format(exynos_plane->config, exynos_state); 2458c2ecf20Sopenharmony_ci if (ret) 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = exynos_drm_plane_check_size(exynos_plane->config, exynos_state); 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void exynos_plane_atomic_update(struct drm_plane *plane, 2538c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct drm_plane_state *state = plane->state; 2568c2ecf20Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(state->crtc); 2578c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!state->crtc) 2608c2ecf20Sopenharmony_ci return; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (exynos_crtc->ops->update_plane) 2638c2ecf20Sopenharmony_ci exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void exynos_plane_atomic_disable(struct drm_plane *plane, 2678c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane); 2708c2ecf20Sopenharmony_ci struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(old_state->crtc); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!old_state->crtc) 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (exynos_crtc->ops->disable_plane) 2768c2ecf20Sopenharmony_ci exynos_crtc->ops->disable_plane(exynos_crtc, exynos_plane); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs plane_helper_funcs = { 2808c2ecf20Sopenharmony_ci .atomic_check = exynos_plane_atomic_check, 2818c2ecf20Sopenharmony_ci .atomic_update = exynos_plane_atomic_update, 2828c2ecf20Sopenharmony_ci .atomic_disable = exynos_plane_atomic_disable, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void exynos_plane_attach_zpos_property(struct drm_plane *plane, 2868c2ecf20Sopenharmony_ci int zpos, bool immutable) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (immutable) 2898c2ecf20Sopenharmony_ci drm_plane_create_zpos_immutable_property(plane, zpos); 2908c2ecf20Sopenharmony_ci else 2918c2ecf20Sopenharmony_ci drm_plane_create_zpos_property(plane, zpos, 0, MAX_PLANE - 1); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciint exynos_plane_init(struct drm_device *dev, 2958c2ecf20Sopenharmony_ci struct exynos_drm_plane *exynos_plane, unsigned int index, 2968c2ecf20Sopenharmony_ci const struct exynos_drm_plane_config *config) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int err; 2998c2ecf20Sopenharmony_ci unsigned int supported_modes = BIT(DRM_MODE_BLEND_PIXEL_NONE) | 3008c2ecf20Sopenharmony_ci BIT(DRM_MODE_BLEND_PREMULTI) | 3018c2ecf20Sopenharmony_ci BIT(DRM_MODE_BLEND_COVERAGE); 3028c2ecf20Sopenharmony_ci struct drm_plane *plane = &exynos_plane->base; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci err = drm_universal_plane_init(dev, &exynos_plane->base, 3058c2ecf20Sopenharmony_ci 1 << dev->mode_config.num_crtc, 3068c2ecf20Sopenharmony_ci &exynos_plane_funcs, 3078c2ecf20Sopenharmony_ci config->pixel_formats, 3088c2ecf20Sopenharmony_ci config->num_pixel_formats, 3098c2ecf20Sopenharmony_ci NULL, config->type, NULL); 3108c2ecf20Sopenharmony_ci if (err) { 3118c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to initialize plane\n"); 3128c2ecf20Sopenharmony_ci return err; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci drm_plane_helper_add(&exynos_plane->base, &plane_helper_funcs); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci exynos_plane->index = index; 3188c2ecf20Sopenharmony_ci exynos_plane->config = config; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci exynos_plane_attach_zpos_property(&exynos_plane->base, config->zpos, 3218c2ecf20Sopenharmony_ci !(config->capabilities & EXYNOS_DRM_PLANE_CAP_ZPOS)); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (config->capabilities & EXYNOS_DRM_PLANE_CAP_PIX_BLEND) 3248c2ecf20Sopenharmony_ci drm_plane_create_blend_mode_property(plane, supported_modes); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (config->capabilities & EXYNOS_DRM_PLANE_CAP_WIN_BLEND) 3278c2ecf20Sopenharmony_ci drm_plane_create_alpha_property(plane); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 331