Qcom MDP 之一 -- 总体架构

本文深入探讨了 MDP DMA 数据结构及其在中断处理过程中的应用,包括资源初始化、时钟设置、中断响应及特定中断处理逻辑,如 LCD 下溢、帧开始和 DMA 完成等。

struct mdp_dma_data {
        boolean busy;
        boolean waiting;
        struct mutex ov_mutex;
        struct semaphore mutex;
        struct completion comp;
};

static struct mdp_dma_data dma2_data;
static struct mdp_dma_data dma_s_data;
static struct mdp_dma_data dma_e_data;
static struct mdp_dma_data dma3_data;


static int mdp_probe(struct platform_device *pdev)
+-- if ((pdev->id == 0) && (pdev->num_resources > 0)) {
        mdp_pdata = pdev->dev.platform_data;
        size =  resource_size(&pdev->resource[0]);
        msm_mdp_base = ioremap(pdev->resource[0].start, size);
        rc = mdp_irq_clk_setup(); //这里会注册INT_MDP中断
        mdp_hw_init();
        mdp_resource_initialized = 1;
        return 0;
    }
+-- ...

static int mdp_irq_clk_setup(void)

?-- ret = request_irq(INT_MDP, mdp4_isr, IRQF_DISABLED, "MDP", 0);
?-- ret = request_irq(INT_MDP, mdp_isr,  IRQF_DISABLED, "MDP", 0); //INT_MDP --> mdp_isr

+-- disable_irq(INT_MDP);

+-- mdp_clk  = clk_get(NULL, "mdp_clk");  //主时钟
+-- mdp_pclk = clk_get(NULL, "mdp_pclk"); //像素时钟

    //mdp_clk should greater than mdp_pclk always
+-- if (mdp_pdata && mdp_pdata->mdp_core_clk_rate)
        clk_set_rate(mdp_clk, mdp_pdata->mdp_core_clk_rate);



@arch/arm/mach-msm/include/mach/irqs-7x30.h
#define INT_MDP (64 + 16)



irqreturn_t 
mdp_isr(int irq, void *ptr)
{
        uint32 mdp_interrupt = 0;
        struct mdp_dma_data *dma;

        mdp_is_in_isr = TRUE;
        do {
                mdp_interrupt = inp32(MDP_INTR_STATUS);
                outp32(MDP_INTR_CLEAR, mdp_interrupt);

                mdp_interrupt &= mdp_intr_mask;

                if (mdp_interrupt & TV_ENC_UNDERRUN) {
                        mdp_interrupt &= ~(TV_ENC_UNDERRUN);
                        mdp_tv_underflow_cnt++;
                }

                if (!mdp_interrupt)
                        break;
                ...
                /* LCDC UnderFlow */
                if (mdp_interrupt & LCDC_UNDERFLOW) {
                        mdp_lcdc_underflow_cnt++;
                        /*when underflow happens HW resets all the histogram
                         registers that were set before so restore them back
                         to normal.*/
                        MDP_OUTP(MDP_BASE + 0x94010, 1);
                        MDP_OUTP(MDP_BASE + 0x9401c, 2);
                }
                /* LCDC Frame Start */
                if (mdp_interrupt & LCDC_FRAME_START) {
                        /* let's disable LCDC interrupt */
                        mdp_intr_mask &= ~LCDC_FRAME_START;
                        outp32(MDP_INTR_ENABLE, mdp_intr_mask);

                        dma = &dma2_data;
                        if (dma->waiting) {
                                dma->waiting = FALSE;
                                complete(&dma->comp);  //通知等待&dma->comp的进程
                        }
                }
                /* DMA2 LCD-Out Complete */
                if (mdp_interrupt & MDP_DMA_S_DONE) {
                        dma = &dma_s_data;
                        dma->busy = FALSE;
                        mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_OFF,
                                      TRUE);
                        complete(&dma->comp);   
                }
                ...
        } while (1);

        mdp_is_in_isr = FALSE;

        return IRQ_HANDLED;
}



//那么又是谁在等待&dma->comp这个completion呢? 可以在不同的文件中查找到下列函数:
//@drivers/video/msm/mdp_dma.c
static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd)
void mdp_dma2_update(struct msm_fb_data_type *mfd)
//@drivers/video/msm/mdp_dma_lcdc.c
void mdp_lcdc_update(struct msm_fb_data_type *mfd)
//@drivers/video/msm/mdp_dma_s.c
void mdp_dma_s_update(struct msm_fb_data_type *mfd)   
//@drivers/video/msm/mdp_dma_tv.c
void mdp_dma3_update(struct msm_fb_data_type *mfd)
//这些函数都调用了同一个函数来等待&dma->comp这个completion:
//wait_for_completion_killable(&mfd->dma->comp);

                 
//而在mdp_probe()中,会根据panel_type将mfd->dma_func()初始化为这些函数其中之一,其参数mfd->dma也将进行相应的初始化
static int 
mdp_probe(struct platform_device *pdev)
+-- switch (mfd->panel.type) {
        case EXT_MDDI_PANEL:
        case MDDI_PANEL:
        case EBI2_PANEL:
                INIT_WORK(&mfd->dma_update_worker,   mdp_lcd_update_workqueue_handler); //这里初始化dma_update_worker
                INIT_WORK(&mfd->vsync_resync_worker, mdp_vsync_resync_workqueue_handler);
                mfd->hw_refresh = FALSE;
                ...
                if (mfd->panel_info.pdest == DISPLAY_1) {
                        mfd->dma_fnc = mdp4_mddi_overlay;
                        ...
                        mfd->dma = &dma2_data;
                        ...
                } else {
                        mfd->dma_fnc = mdp_dma_s_update;
                        mfd->dma = &dma_s_data;
                }
                ...
                mdp_config_vsync(mfd);
                break;
        ...         
        case HDMI_PANEL:
        case LCDC_PANEL:
                ...
#ifdef CONFIG_FB_MSM_OVERLAY
                mfd->dma_fnc = mdp4_lcdc_overlay;
#else
                mfd->dma_fnc = mdp_lcdc_update;
#endif
                mfd->dma = &dma2_data;
                ...
        ...
    }
+-- ...

//由此综合可以得出结论,等待&dma->comp这个completion的函数对上的接口统一为:mfd->dma_func()
//而mfd->dma_func()只有两个函数调用,再往上走可以得到两个不同的调用路径
void mdp_lcd_update_workqueue_handler(struct work_struct *work) //EXT_MDDI&MDDI&EBI2_PANEL 
void mdp_dma_pan_update(struct fb_info *info)                   //HDMI&LCDC
+-- mfd->dma_fnc(mfd);

//这两个调用路径最终归结到fb_ops.fb_fillrect()和fb_ops.fb_ioctl()两个成员函数里面
static struct fb_ops msm_fb_ops = {
        .owner = THIS_MODULE,
        .fb_open = msm_fb_open,
        .fb_release = msm_fb_release,
        .fb_read = NULL,
        .fb_write = NULL,
        .fb_cursor = NULL,
        .fb_check_var = msm_fb_check_var,       /* vinfo check */
        .fb_set_par = msm_fb_set_par,           /* set the video mode according to info->var */
        .fb_setcolreg = NULL,                   /* set color register */
        .fb_blank = msm_fb_blank,               /* blank display */
        .fb_pan_display = msm_fb_pan_display,   /* pan display */
        .fb_fillrect = msm_fb_fillrect,         // Draws a rectangle ...
        .fb_copyarea = msm_fb_copyarea,         /* Copy data from area to another */
        .fb_imageblit = msm_fb_imageblit,       /* Draws a image to the display */
        .fb_rotate = NULL,
        .fb_sync = NULL,                        /* wait for blit idle, optional */
        .fb_ioctl = msm_fb_ioctl,               // perform fb specific ioctl (optional)...
        .fb_mmap = msm_fb_mmap,
};


//path 1 : fb_ops.fb_ioctl() >> msm_fb_ioctl()
static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+-- msm_fb_resume_sw_refresher(mfd); //MSMFB_RESUME_SW_REFRESHE
    +-- mdp_refresh_screen((unsigned long)mfd); //mdp_refresh_screen...

void mdp_refresh_screen(unsigned long data)
{
        struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;

        if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
                init_timer(&mfd->refresh_timer);
                mfd->refresh_timer.function = mdp_refresh_screen;  //递归调用,因此,此函数的执行是周期性的
                mfd->refresh_timer.data = data;
                //函数执行的周期为1ms或者refresh_timer_duration
                if (mfd->dma->busy)
                        /* come back in 1 msec */
                        mfd->refresh_timer.expires = jiffies + (HZ / 1000);
                else
                        mfd->refresh_timer.expires = jiffies + mfd->refresh_timer_duration;
                add_timer(&mfd->refresh_timer);

                if (!mfd->dma->busy) {
                        //将mfd->dma_update_worker这个work挂入mdp_dma_wq工作队列.
                        if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) {
                                MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \
                                              MDP/MDDI/LCD clock speed needs to be increased\n");
                        }
                }
        } else {
                if (!mfd->hw_refresh)
                        complete(&mfd->refresher_comp);
        }
}
//这个函数实际上就是在线程环境下周期性的执行&mfd->dma_update_worker这个work关联的函数.
//那么mfd->dma_update_worker这个work关联的函数做了什么呢? 看mdp_probe()中是如何初始化&mfd->dma_update_worker的

//mdp_probe()中,根据panel_type初始化mfd->dma_func()及其参数mfd->dma
static int mdp_probe(struct platform_device *pdev)
+-- switch (mfd->panel.type) {
        case EXT_MDDI_PANEL:
        case MDDI_PANEL:
        case EBI2_PANEL:
                INIT_WORK(&mfd->dma_update_worker,   mdp_lcd_update_workqueue_handler); //这里初始化dma_update_worker
                INIT_WORK(&mfd->vsync_resync_worker, mdp_vsync_resync_workqueue_handler);
                ...
                break;
        ...         
        case HDMI_PANEL:
        case LCDC_PANEL:
                ...
        ...
    }
+-- ...


void mdp_lcd_update_workqueue_handler(struct work_struct *work)
+-- mfd->dma_fnc(mfd);


//path 2 : fb_ops.fb_fillrect() >> msm_fb_fillrect() 

@drivers/video/msm/msm_fb.c
static void msm_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+-- cfb_fillrect(info, rect);
+-- var = info->var;
+-- var.reserved[0] = 0x54445055;
+-- var.reserved[1] = (rect->dy << 16) | (rect->dx);
+-- var.reserved[2] = ((rect->dy + rect->height) << 16) | (rect->dx + rect->width);
+-- msm_fb_pan_display(&var, info);
    +-- mdp_set_dma_pan_info(info, dirtyPtr,(var->activate == FB_ACTIVATE_VBL));
    +-- mdp_dma_pan_update(info);
        +-- if (mfd->sw_currently_refreshing) {
                /* we need to wait for the pending update */
                mfd->pan_waiting = TRUE;
                if (!mfd->ibuf_flushed) {
                        wait_for_completion_killable(&mfd->pan_comp);
                }
                /* waiting for this update to complete */
                mfd->pan_waiting = TRUE;
                wait_for_completion_killable(&mfd->pan_comp);
            } else
                mfd->dma_fnc(mfd);           


#include <dt-bindings/interrupt-controller/arm-gic.h> 2 3 &soc { 4 mdss_mdp: qcom,mdss_mdp@5e00000 { 5 compatible = "qcom,sde-kms"; 6 reg = <0x05e00000 0x8f030>, 7 <0x05eb0000 0x2008>, 8 <0x05e8f000 0x030>; 9 reg-names = "mdp_phys", 10 "vbif_phys", 11 "sid_phys"; 12 13 /* interrupt config */ 14 interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_HIGH>; 15 interrupt-controller; 16 #interrupt-cells = <1>; 17 18 /* Enable thermal cooling device */ 19 #cooling-cells = <2>; 20 21 /* hw blocks */ 22 qcom,sde-off = <0x1000>; 23 qcom,sde-len = <0x494>; 24 25 qcom,sde-ctl-off = <0x2000>; 26 qcom,sde-ctl-size = <0x1dc>; 27 qcom,sde-ctl-display-pref = "primary"; 28 29 qcom,sde-mixer-off = <0x45000>; 30 qcom,sde-mixer-size = <0x320>; 31 qcom,sde-mixer-display-pref = "primary"; 32 33 qcom,sde-dspp-top-off = <0x1300>; 34 qcom,sde-dspp-top-size = <0x80>; 35 qcom,sde-dspp-off = <0x55000>; 36 qcom,sde-dspp-size = <0x1800>; 37 38 qcom,sde-dspp-rc-version = <0x00010000>; 39 qcom,sde-dspp-rc-off = <0x15800>; 40 qcom,sde-dspp-rc-size = <0x100>; 41 qcom,sde-dspp-rc-mem-size = <2720>; 42 qcom,sde-dspp-rc-min-region-width = <4>; 43 44 qcom,sde-intf-off = <0x0 0x6b800>; 45 qcom,sde-intf-size = <0x2c0>; 46 qcom,sde-intf-type = "none", "dsi"; 47 qcom,sde-intf-tear-irq-off = <0 0x6e800>; 48 49 qcom,sde-pp-off = <0x71000>; 50 qcom,sde-pp-size = <0xd4>; 51 52 qcom,sde-dsc-off = <0x81000>; 53 qcom,sde-dsc-size = <0x140>; 54 qcom,sde-dsc-hw-rev = "dsc_1_1"; 55 56 qcom,sde-dither-off = <0x30e0>; 57 qcom,sde-dither-version = <0x00010000>; 58 qcom,sde-dither-size = <0x20>; 59 60 qcom,sde-sspp-type = "vig", "dma"; 61 62 qcom,sde-sspp-off = <0x5000 0x25000>; 63 qcom,sde-sspp-src-size = <0x1f8>; 64 65 qcom,sde-sspp-xin-id = <0 1>; 66 qcom,sde-sspp-excl-rect = <1 1>; 67 qcom,sde-sspp-smart-dma-priority = <2 1>; 68 qcom,sde-smart-dma-rev = "smart_dma_v2p5"; 69 70 qcom,sde-mixer-pair-mask = <0>; 71 qcom,sde-mixer-stage-base-layer; 72 73 qcom,sde-mixer-blend-op-off = <0x20 0x38 0x50 0x68 0x80 0x98 74 0xb0 0xc8 0xe0 0xf8 0x110>; 75 76 qcom,sde-max-per-pipe-bw-kbps = <4100000 4100000>; 77 78 qcom,sde-max-per-pipe-bw-high-kbps = <4100000 4100000>; 79 80 /* offsets are relative to "mdp_phys + qcom,sde-off */ 81 qcom,sde-sspp-clk-ctrl = <0x2ac 0>, <0x2ac 8>; 82 qcom,sde-sspp-clk-status = <0x2b0 0>, <0x2b0 12>; 83 qcom,sde-sspp-csc-off = <0x1a00>; 84 qcom,sde-csc-type = "csc-10bit"; 85 qcom,sde-qseed-sw-lib-rev = "qseedv3lite"; 86 qcom,sde-qseed-scalar-version = <0x3000>; 87 qcom,sde-sspp-qseed-off = <0xa00>; 88 qcom,sde-sspp-linewidth = <2160>; 89 qcom,sde-vig-sspp-linewidth = <4096>; 90 qcom,sde-scaling-linewidth = <2560>; 91 qcom,sde-mixer-linewidth = <2048>; 92 qcom,sde-mixer-blendstages = <0x4>; 93 qcom,sde-highest-bank-bit = <0x0 0x1>; 94 qcom,sde-ubwc-version = <0x20000000>; 95 qcom,sde-ubwc-swizzle = <0x6>; 96 qcom,sde-ubwc-bw-calc-version = <0x1>; 97 qcom,sde-ubwc-static = <0x1e>; 98 qcom,sde-macrotile-mode = <0x0>; 99 qcom,sde-panic-per-pipe; 100 qcom,sde-has-cdp; 101 qcom,sde-has-dim-layer; 102 qcom,sde-max-bw-low-kbps = <5200000>; 103 qcom,sde-max-bw-high-kbps = <6200000>; 104 qcom,sde-min-core-ib-kbps = <2500000>; 105 qcom,sde-min-llcc-ib-kbps = <0>; 106 qcom,sde-min-dram-ib-kbps = <1600000>; 107 qcom,sde-dram-channels = <1>; 108 qcom,sde-num-nrt-paths = <0>; 109 110 qcom,sde-vbif-off = <0>; 111 qcom,sde-vbif-size = <0x2008>; 112 qcom,sde-vbif-id = <0>; 113 qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>; 114 qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>; 115 116 qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6 3 3 4 4 5 5 6 6>; 117 qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3>; 118 119 qcom,sde-qos-refresh-rates = <60 120>; 120 qcom,sde-danger-lut = <0xffff 0xffff 0xffff 0xffff 0x0 0x0 0x0 0x0 0x0 121 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>, 122 <0x3ffff 0x3ffff 0x3ffff 0x3ffff 0x0 0x0 0x0 0x0 0x0 123 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; 124 125 qcom,sde-safe-lut = <0xff00 0xff00 0xff00 0xff00 0xffff 0xffff 0x0 0x0 0x0 126 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>, 127 <0xfe00 0xfe00 0xfe00 0xfe00 0xffff 0xffff 0x0 0x0 0x0 128 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; 129 130 qcom,sde-creq-lut = <0x00112233 0x44556677 0x00112233 0x66777777 131 0x00112233 0x44556677 0x00112233 0x66777777 132 0x0 0x0 0x0 0x0 133 0x0 0x0 0x0 0x0 134 0x0 0x0 0x0 0x0 135 0x0 0x0 0x0 0x0 136 0x0 0x0 0x0 0x0 137 0x0 0x0 0x0 0x0 138 0x0 0x0 0x0 0x0>, 139 <0x00112234 0x45566777 0x00112236 0x67777777 140 0x00112234 0x45566777 0x00112236 0x67777777 141 0x0 0x0 0x0 0x0 142 0x0 0x0 0x0 0x0 143 0x0 0x0 0x0 0x0 144 0x0 0x0 0x0 0x0 145 0x0 0x0 0x0 0x0 146 0x0 0x0 0x0 0x0 147 0x0 0x0 0x0 0x0>; 148 149 qcom,sde-cdp-setting = <1 1>, <1 0>; 150 151 qcom,sde-qos-cpu-mask = <0x3>; 152 qcom,sde-qos-cpu-mask-performance = <0x3f>; 153 qcom,sde-qos-cpu-dma-latency = <300>; 154 qcom,sde-qos-cpu-irq-latency = <300>; 155 156 qcom,sde-secure-sid-mask = <0x821>; 157 qcom,sde-num-mnoc-ports = <1>; 158 qcom,sde-axi-bus-width = <32>; 159 160 qcom,sde-reg-bus,vectors-KBps = <0 0>, 161 <0 76800>, 162 <0 150000>, 163 <0 300000>; 164 165 qcom,sde-sspp-vig-blocks { 166 qcom,sde-vig-csc-off = <0x1a00>; 167 qcom,sde-vig-qseed-off = <0xa00>; 168 qcom,sde-vig-qseed-size = <0xa0>; 169 }; 170 171 qcom,sde-dspp-blocks { 172 qcom,sde-dspp-igc = <0x0 0x00030001>; 173 qcom,sde-dspp-hsic = <0x800 0x00010007>; 174 qcom,sde-dspp-memcolor = <0x880 0x00010007>; 175 qcom,sde-dspp-hist = <0x800 0x00010007>; 176 qcom,sde-dspp-sixzone= <0x900 0x00010007>; 177 qcom,sde-dspp-vlut = <0xa00 0x00010008>; 178 qcom,sde-dspp-pcc = <0x1700 0x00040000>; 179 qcom,sde-dspp-gc = <0x17c0 0x00010008>; 180 qcom,sde-dspp-dither = <0x82c 0x00010007>; 181 }; 182 183 }; 184 185 mdss_dsi0: qcom,mdss_dsi_ctrl0@5e94000 { 186 compatible = "qcom,dsi-ctrl-hw-v2.4"; 187 label = "dsi-ctrl-0"; 188 cell-index = <0>; 189 frame-threshold-time-us = <1000>; 190 reg = <0x05e94000 0x400>, 191 <0x05f08000 0x4>, 192 <0x05e6b800 0x300>; 193 reg-names = "dsi_ctrl", "disp_cc_base", "mdp_intf_base"; 194 interrupt-parent = <&mdss_mdp>; 195 interrupts = <4 0>; 196 197 qcom,ctrl-supply-entries { 198 #address-cells = <1>; 199 #size-cells = <0>; 200 201 qcom,ctrl-supply-entry@0 { 202 reg = <0>; 203 qcom,supply-name = "vdda-1p2"; 204 qcom,supply-min-voltage = <1200000>; 205 qcom,supply-max-voltage = <1200000>; 206 qcom,supply-enable-load = <21800>; 207 qcom,supply-disable-load = <0>; 208 }; 209 }; 210 211 qcom,core-supply-entries { 212 #address-cells = <1>; 213 #size-cells = <0>; 214 215 qcom,core-supply-entry@0 { 216 reg = <0>; 217 qcom,supply-name = "refgen"; 218 qcom,supply-min-voltage = <0>; 219 qcom,supply-max-voltage = <0>; 220 qcom,supply-enable-load = <0>; 221 qcom,supply-disable-load = <0>; 222 }; 223 }; 224 }; 225 226 mdss_dsi_phy0: qcom,mdss_dsi_phy0@5e94900 { 227 compatible = "qcom,dsi-phy-v4.1"; 228 label = "dsi-phy-0"; 229 cell-index = <0>; 230 #clock-cells = <1>; 231 reg = <0x05e94400 0x800>, 232 <0x05e94900 0x264>, 233 <0x05f01004 0x8>, 234 <0x05e94200 0x100>; 235 reg-names = "dsi_phy", "pll_base", "gdsc_base", "dyn_refresh_base"; 236 pll-label = "dsi_pll_5nm"; 237 238 qcom,platform-strength-ctrl = [55 03 239 55 03 240 55 03 241 55 03 242 55 00]; 243 qcom,platform-lane-config = [00 00 0a 0a 244 00 00 0a 0a 245 00 00 0a 0a 246 00 00 0a 0a 247 00 00 8a 8a]; 248 qcom,platform-regulator-settings = [1d 1d 1d 1d 1d]; 249 qcom,phy-supply-entries { 250 #address-cells = <1>; 251 #size-cells = <0>; 252 qcom,phy-supply-entry@0 { 253 reg = <0>; 254 qcom,supply-name = "vdda-0p9"; 255 qcom,supply-min-voltage = 256 <RPMH_REGULATOR_LEVEL_SVS_L1>; 257 qcom,supply-max-voltage = 258 <RPMH_REGULATOR_LEVEL_TURBO_L1>; 259 qcom,supply-off-min-voltage = 260 <RPMH_REGULATOR_LEVEL_RETENTION>; 261 qcom,supply-enable-load = <0>; 262 qcom,supply-disable-load = <0>; 263 }; 264 }; 265 }; 266 };
最新发布
09-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值