提交 c71716b1 编写于 作者: H Hai Li 提交者: Rob Clark

drm/msm/mdp5: Allocate CTL for each display interface

In MDP5, CTL contains information of the whole pipeline whose
output goes down to a display interface. In various cases, one
interface may require 2 CRTCs, but only one CTL. Some interfaces
also require to use certain CTLs.

Instead of allocating CTL for each active CRTC, this change is to
associate a CTL with each interface.
Signed-off-by: NHai Li <hali@codeaurora.org>
Signed-off-by: NRob Clark <robdclark@gmail.com>
上级 12987781
master alk-4.19.24 alk-4.19.30 alk-4.19.34 alk-4.19.36 alk-4.19.43 alk-4.19.48 alk-4.19.57 ck-4.19.67 ck-4.19.81 ck-4.19.91 github/fork/deepanshu1422/fix-typo-in-comment github/fork/haosdent/fix-typo linux-next v4.19.91 v4.19.90 v4.19.89 v4.19.88 v4.19.87 v4.19.86 v4.19.85 v4.19.84 v4.19.83 v4.19.82 v4.19.81 v4.19.80 v4.19.79 v4.19.78 v4.19.77 v4.19.76 v4.19.75 v4.19.74 v4.19.73 v4.19.72 v4.19.71 v4.19.70 v4.19.69 v4.19.68 v4.19.67 v4.19.66 v4.19.65 v4.19.64 v4.19.63 v4.19.62 v4.19.61 v4.19.60 v4.19.59 v4.19.58 v4.19.57 v4.19.56 v4.19.55 v4.19.54 v4.19.53 v4.19.52 v4.19.51 v4.19.50 v4.19.49 v4.19.48 v4.19.47 v4.19.46 v4.19.45 v4.19.44 v4.19.43 v4.19.42 v4.19.41 v4.19.40 v4.19.39 v4.19.38 v4.19.37 v4.19.36 v4.19.35 v4.19.34 v4.19.33 v4.19.32 v4.19.31 v4.19.30 v4.19.29 v4.19.28 v4.19.27 v4.19.26 v4.19.25 v4.19.24 v4.19.23 v4.19.22 v4.19.21 v4.19.20 v4.19.19 v4.19.18 v4.19.17 v4.19.16 v4.19.15 v4.19.14 v4.19.13 v4.19.12 v4.19.11 v4.19.10 v4.19.9 v4.19.8 v4.19.7 v4.19.6 v4.19.5 v4.19.4 v4.19.3 v4.19.2 v4.19.1 v4.19 v4.19-rc8 v4.19-rc7 v4.19-rc6 v4.19-rc5 v4.19-rc4 v4.19-rc3 v4.19-rc2 v4.19-rc1 ck-release-21 ck-release-20 ck-release-19.2 ck-release-19.1 ck-release-19 ck-release-18 ck-release-17.2 ck-release-17.1 ck-release-17 ck-release-16 ck-release-15.1 ck-release-15 ck-release-14 ck-release-13.2 ck-release-13 ck-release-12 ck-release-11 ck-release-10 ck-release-9 ck-release-7 alk-release-15 alk-release-14 alk-release-13.2 alk-release-13 alk-release-12 alk-release-11 alk-release-10 alk-release-9 alk-release-7
无相关合并请求
......@@ -21,6 +21,8 @@ struct mdp5_cmd_encoder {
struct mdp5_interface intf;
bool enabled;
uint32_t bsc;
struct mdp5_ctl *ctl;
};
#define to_mdp5_cmd_encoder(x) container_of(x, struct mdp5_cmd_encoder, base)
......@@ -210,13 +212,14 @@ static void mdp5_cmd_encoder_mode_set(struct drm_encoder *encoder,
mode->vsync_end, mode->vtotal,
mode->type, mode->flags);
pingpong_tearcheck_setup(encoder, mode);
mdp5_crtc_set_intf(encoder->crtc, &mdp5_cmd_enc->intf);
mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_cmd_enc->intf,
mdp5_cmd_enc->ctl);
}
static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
{
struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
if (WARN_ON(!mdp5_cmd_enc->enabled))
......@@ -235,7 +238,7 @@ static void mdp5_cmd_encoder_disable(struct drm_encoder *encoder)
static void mdp5_cmd_encoder_enable(struct drm_encoder *encoder)
{
struct mdp5_cmd_encoder *mdp5_cmd_enc = to_mdp5_cmd_encoder(encoder);
struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
struct mdp5_ctl *ctl = mdp5_cmd_enc->ctl;
struct mdp5_interface *intf = &mdp5_cmd_enc->intf;
if (WARN_ON(mdp5_cmd_enc->enabled))
......@@ -300,7 +303,7 @@ int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
/* initialize command mode encoder */
struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
struct mdp5_interface *intf)
struct mdp5_interface *intf, struct mdp5_ctl *ctl)
{
struct drm_encoder *encoder = NULL;
struct mdp5_cmd_encoder *mdp5_cmd_enc;
......@@ -320,6 +323,7 @@ struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
memcpy(&mdp5_cmd_enc->intf, intf, sizeof(mdp5_cmd_enc->intf));
encoder = &mdp5_cmd_enc->base;
mdp5_cmd_enc->ctl = ctl;
drm_encoder_init(dev, encoder, &mdp5_cmd_encoder_funcs,
DRM_MODE_ENCODER_DSI);
......
......@@ -160,8 +160,7 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
if (mdp5_crtc->ctl && !crtc->state->enable) {
/* set STAGE_UNUSED for all layers */
mdp5_ctl_blend(mdp5_crtc->ctl, mdp5_crtc->lm, NULL, 0, 0);
mdp5_ctl_release(mdp5_crtc->ctl);
mdp5_ctl_blend(mdp5_crtc->ctl, NULL, 0, 0);
mdp5_crtc->ctl = NULL;
}
}
......@@ -289,7 +288,7 @@ static void blend_setup(struct drm_crtc *crtc)
blender(i)), bg_alpha);
}
mdp5_ctl_blend(mdp5_crtc->ctl, lm, stage, plane_cnt, ctl_blend_flags);
mdp5_ctl_blend(mdp5_crtc->ctl, stage, plane_cnt, ctl_blend_flags);
out:
spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
......@@ -386,13 +385,6 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
DBG("%s: check", mdp5_crtc->name);
/* request a free CTL, if none is already allocated for this CRTC */
if (state->enable && !mdp5_crtc->ctl) {
mdp5_crtc->ctl = mdp5_ctlm_request(mdp5_kms->ctlm, crtc);
if (WARN_ON(!mdp5_crtc->ctl))
return -EINVAL;
}
/* verify that there are not too many planes attached to crtc
* and that we don't have conflicting mixer stages:
*/
......@@ -735,8 +727,8 @@ void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file)
complete_flip(crtc, file);
}
/* set interface for routing crtc->encoder: */
void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf)
void mdp5_crtc_set_pipeline(struct drm_crtc *crtc,
struct mdp5_interface *intf, struct mdp5_ctl *ctl)
{
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
struct mdp5_kms *mdp5_kms = get_kms(crtc);
......@@ -759,7 +751,8 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf)
mdp_irq_update(&mdp5_kms->base);
mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
mdp5_crtc->ctl = ctl;
mdp5_ctl_set_pipeline(ctl, intf, lm);
}
int mdp5_crtc_get_lm(struct drm_crtc *crtc)
......@@ -768,12 +761,6 @@ int mdp5_crtc_get_lm(struct drm_crtc *crtc)
return WARN_ON(!crtc) ? -EINVAL : mdp5_crtc->lm;
}
struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc)
{
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
return WARN_ON(!crtc) ? NULL : mdp5_crtc->ctl;
}
void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc)
{
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
......
......@@ -60,8 +60,6 @@ struct mdp5_ctl {
u32 pending_ctl_trigger;
bool cursor_on;
struct drm_crtc *crtc;
};
struct mdp5_ctl_manager {
......@@ -168,11 +166,21 @@ static void set_ctl_op(struct mdp5_ctl *ctl, struct mdp5_interface *intf)
spin_unlock_irqrestore(&ctl->hw_lock, flags);
}
int mdp5_ctl_set_intf(struct mdp5_ctl *ctl, struct mdp5_interface *intf)
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl,
struct mdp5_interface *intf, int lm)
{
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr);
if (unlikely(WARN_ON(intf->num != ctl->pipeline.intf.num))) {
dev_err(mdp5_kms->dev->dev,
"CTL %d is allocated by INTF %d, but used by INTF %d\n",
ctl->id, ctl->pipeline.intf.num, intf->num);
return -EINVAL;
}
ctl->lm = lm;
memcpy(&ctl->pipeline.intf, intf, sizeof(*intf));
ctl->pipeline.start_mask = mdp_ctl_flush_mask_lm(ctl->lm) |
......@@ -335,7 +343,7 @@ static u32 mdp_ctl_blend_ext_mask(enum mdp5_pipe pipe,
}
}
int mdp5_ctl_blend(struct mdp5_ctl *ctl, u32 lm, u8 *stage, u32 stage_cnt,
int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt,
u32 ctl_blend_op_flags)
{
unsigned long flags;
......@@ -358,13 +366,13 @@ int mdp5_ctl_blend(struct mdp5_ctl *ctl, u32 lm, u8 *stage, u32 stage_cnt,
if (ctl->cursor_on)
blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT;
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg);
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, lm), blend_ext_cfg);
ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, ctl->lm), blend_cfg);
ctl_write(ctl, REG_MDP5_CTL_LAYER_EXT_REG(ctl->id, ctl->lm), blend_ext_cfg);
spin_unlock_irqrestore(&ctl->hw_lock, flags);
ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(lm);
ctl->pending_ctl_trigger = mdp_ctl_flush_mask_lm(ctl->lm);
DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", lm,
DBG("lm%d: blend config = 0x%08x. ext_cfg = 0x%08x", ctl->lm,
blend_cfg, blend_ext_cfg);
return 0;
......@@ -490,38 +498,18 @@ u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl)
return ctl_read(ctl, REG_MDP5_CTL_FLUSH(ctl->id));
}
void mdp5_ctl_release(struct mdp5_ctl *ctl)
{
struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
unsigned long flags;
if (unlikely(WARN_ON(ctl->id >= MAX_CTL) || !ctl->busy)) {
dev_err(ctl_mgr->dev->dev, "CTL %d in bad state (%d)",
ctl->id, ctl->busy);
return;
}
spin_lock_irqsave(&ctl_mgr->pool_lock, flags);
ctl->busy = false;
spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags);
DBG("CTL %d released", ctl->id);
}
int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl)
{
return WARN_ON(!ctl) ? -EINVAL : ctl->id;
}
/*
* mdp5_ctl_request() - CTL dynamic allocation
*
* Note: Current implementation considers that we can only have one CRTC per CTL
* mdp5_ctl_request() - CTL allocation
*
* @return first free CTL
*/
struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr,
struct drm_crtc *crtc)
int intf_num)
{
struct mdp5_ctl *ctl = NULL;
unsigned long flags;
......@@ -539,9 +527,8 @@ struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctl_mgr,
}
ctl = &ctl_mgr->ctls[c];
ctl->lm = mdp5_crtc_get_lm(crtc);
ctl->crtc = crtc;
ctl->pipeline.intf.num = intf_num;
ctl->lm = -1;
ctl->busy = true;
ctl->pending_ctl_trigger = 0;
DBG("CTL %d allocated", ctl->id);
......
......@@ -32,11 +32,12 @@ void mdp5_ctlm_destroy(struct mdp5_ctl_manager *ctlm);
* mdp5_ctl_request(ctlm, ...) returns a ctl (CTL resource) handler,
* which is then used to call the other mdp5_ctl_*(ctl, ...) functions.
*/
struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, struct drm_crtc *crtc);
struct mdp5_ctl *mdp5_ctlm_request(struct mdp5_ctl_manager *ctlm, int intf_num);
int mdp5_ctl_get_ctl_id(struct mdp5_ctl *ctl);
struct mdp5_interface;
int mdp5_ctl_set_intf(struct mdp5_ctl *ctl, struct mdp5_interface *intf);
int mdp5_ctl_set_pipeline(struct mdp5_ctl *ctl, struct mdp5_interface *intf,
int lm);
int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, bool enabled);
int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable);
......@@ -53,7 +54,7 @@ int mdp5_ctl_set_cursor(struct mdp5_ctl *ctl, int cursor_id, bool enable);
* (call mdp5_ctl_commit() with mdp_ctl_flush_mask_ctl() mask)
*/
#define MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT BIT(0)
int mdp5_ctl_blend(struct mdp5_ctl *ctl, u32 lm, u8 *stage, u32 stage_cnt,
int mdp5_ctl_blend(struct mdp5_ctl *ctl, u8 *stage, u32 stage_cnt,
u32 ctl_blend_op_flags);
/**
......@@ -71,8 +72,6 @@ u32 mdp_ctl_flush_mask_encoder(struct mdp5_interface *intf);
u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask);
u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl);
void mdp5_ctl_release(struct mdp5_ctl *ctl);
#endif /* __MDP5_CTL_H__ */
......@@ -27,6 +27,8 @@ struct mdp5_encoder {
spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */
bool enabled;
uint32_t bsc;
struct mdp5_ctl *ctl;
};
#define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base)
......@@ -222,14 +224,15 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
mdp5_crtc_set_intf(encoder->crtc, &mdp5_encoder->intf);
mdp5_crtc_set_pipeline(encoder->crtc, &mdp5_encoder->intf,
mdp5_encoder->ctl);
}
static void mdp5_encoder_disable(struct drm_encoder *encoder)
{
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
struct mdp5_kms *mdp5_kms = get_kms(encoder);
struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
struct mdp5_ctl *ctl = mdp5_encoder->ctl;
int lm = mdp5_crtc_get_lm(encoder->crtc);
struct mdp5_interface *intf = &mdp5_encoder->intf;
int intfn = mdp5_encoder->intf.num;
......@@ -264,7 +267,7 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder)
{
struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder);
struct mdp5_kms *mdp5_kms = get_kms(encoder);
struct mdp5_ctl *ctl = mdp5_crtc_get_ctl(encoder->crtc);
struct mdp5_ctl *ctl = mdp5_encoder->ctl;
struct mdp5_interface *intf = &mdp5_encoder->intf;
int intfn = mdp5_encoder->intf.num;
unsigned long flags;
......@@ -329,7 +332,7 @@ int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
/* initialize encoder */
struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
struct mdp5_interface *intf)
struct mdp5_interface *intf, struct mdp5_ctl *ctl)
{
struct drm_encoder *encoder = NULL;
struct mdp5_encoder *mdp5_encoder;
......@@ -345,6 +348,7 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
memcpy(&mdp5_encoder->intf, intf, sizeof(mdp5_encoder->intf));
encoder = &mdp5_encoder->base;
mdp5_encoder->ctl = ctl;
spin_lock_init(&mdp5_encoder->intf_lock);
......
......@@ -198,7 +198,7 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms)
static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
enum mdp5_intf_type intf_type, int intf_num,
enum mdp5_intf_mode intf_mode)
enum mdp5_intf_mode intf_mode, struct mdp5_ctl *ctl)
{
struct drm_device *dev = mdp5_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
......@@ -211,9 +211,9 @@ static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
if ((intf_type == INTF_DSI) &&
(intf_mode == MDP5_INTF_DSI_MODE_COMMAND))
encoder = mdp5_cmd_encoder_init(dev, &intf);
encoder = mdp5_cmd_encoder_init(dev, &intf, ctl);
else
encoder = mdp5_encoder_init(dev, &intf);
encoder = mdp5_encoder_init(dev, &intf, ctl);
if (IS_ERR(encoder)) {
dev_err(dev->dev, "failed to construct encoder\n");
......@@ -251,6 +251,8 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
const struct mdp5_cfg_hw *hw_cfg =
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num];
struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm;
struct mdp5_ctl *ctl;
struct drm_encoder *encoder;
int ret = 0;
......@@ -261,8 +263,14 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
if (!priv->edp)
break;
ctl = mdp5_ctlm_request(ctlm, intf_num);
if (!ctl) {
ret = -EINVAL;
break;
}
encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num,
MDP5_INTF_MODE_NONE);
MDP5_INTF_MODE_NONE, ctl);
if (IS_ERR(encoder)) {
ret = PTR_ERR(encoder);
break;
......@@ -274,8 +282,14 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
if (!priv->hdmi)
break;
ctl = mdp5_ctlm_request(ctlm, intf_num);
if (!ctl) {
ret = -EINVAL;
break;
}
encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num,
MDP5_INTF_MODE_NONE);
MDP5_INTF_MODE_NONE, ctl);
if (IS_ERR(encoder)) {
ret = PTR_ERR(encoder);
break;
......@@ -300,14 +314,20 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
if (!priv->dsi[dsi_id])
break;
ctl = mdp5_ctlm_request(ctlm, intf_num);
if (!ctl) {
ret = -EINVAL;
break;
}
for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
mode = (i == MSM_DSI_CMD_ENCODER_ID) ?
MDP5_INTF_DSI_MODE_COMMAND :
MDP5_INTF_DSI_MODE_VIDEO;
dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI,
intf_num, mode);
if (IS_ERR(dsi_encs)) {
ret = PTR_ERR(dsi_encs);
intf_num, mode, ctl);
if (IS_ERR(dsi_encs[i])) {
ret = PTR_ERR(dsi_encs[i]);
break;
}
}
......
......@@ -228,26 +228,26 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
int mdp5_crtc_get_lm(struct drm_crtc *crtc);
struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc);
void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf);
void mdp5_crtc_set_pipeline(struct drm_crtc *crtc,
struct mdp5_interface *intf, struct mdp5_ctl *ctl);
void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc);
struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
struct drm_plane *plane, int id);
struct drm_encoder *mdp5_encoder_init(struct drm_device *dev,
struct mdp5_interface *intf);
struct mdp5_interface *intf, struct mdp5_ctl *ctl);
int mdp5_encoder_set_split_display(struct drm_encoder *encoder,
struct drm_encoder *slave_encoder);
#ifdef CONFIG_DRM_MSM_DSI
struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
struct mdp5_interface *intf);
struct mdp5_interface *intf, struct mdp5_ctl *ctl);
int mdp5_cmd_encoder_set_split_display(struct drm_encoder *encoder,
struct drm_encoder *slave_encoder);
#else
static inline struct drm_encoder *mdp5_cmd_encoder_init(
struct drm_device *dev, struct mdp5_interface *intf)
static inline struct drm_encoder *mdp5_cmd_encoder_init(struct drm_device *dev,
struct mdp5_interface *intf, struct mdp5_ctl *ctl)
{
return ERR_PTR(-EINVAL);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册
反馈
建议
客服 返回
顶部