123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239 |
- /* Copyright (c) 2007, 2013 The Linux Foundation. All rights reserved.
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/file.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/major.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <linux/uaccess.h>
- #include <linux/sched.h>
- #include <linux/mutex.h>
- #include <linux/sync.h>
- #include <linux/sw_sync.h>
- #include "linux/proc_fs.h"
- #include <linux/delay.h>
- #include "mdss_fb.h"
- #include "mdp3_ppp.h"
- #include "mdp3_hwio.h"
- #include "mdp3.h"
- #include "mdss_debug.h"
- #define MDP_IS_IMGTYPE_BAD(x) ((x) >= MDP_IMGTYPE_LIMIT)
- #define MDP_RELEASE_BW_TIMEOUT 50
- #define MDP_BLIT_CLK_RATE 200000000
- #define MDP_PPP_MAX_BPP 4
- #define MDP_PPP_DYNAMIC_FACTOR 3
- #define MDP_PPP_MAX_READ_WRITE 3
- #define ENABLE_SOLID_FILL 0x2
- #define DISABLE_SOLID_FILL 0x0
- static const bool valid_fmt[MDP_IMGTYPE_LIMIT] = {
- [MDP_RGB_565] = true,
- [MDP_BGR_565] = true,
- [MDP_RGB_888] = true,
- [MDP_BGR_888] = true,
- [MDP_BGRA_8888] = true,
- [MDP_RGBA_8888] = true,
- [MDP_ARGB_8888] = true,
- [MDP_XRGB_8888] = true,
- [MDP_RGBX_8888] = true,
- [MDP_Y_CRCB_H2V2] = true,
- [MDP_Y_CBCR_H2V2] = true,
- [MDP_Y_CBCR_H2V2_ADRENO] = true,
- [MDP_Y_CBCR_H2V2_VENUS] = true,
- [MDP_YCRYCB_H2V1] = true,
- [MDP_Y_CBCR_H2V1] = true,
- [MDP_Y_CRCB_H2V1] = true,
- [MDP_BGRX_8888] = true,
- };
- #define MAX_LIST_WINDOW 16
- #define MDP3_PPP_MAX_LIST_REQ 8
- struct blit_req_list {
- int count;
- struct mdp_blit_req req_list[MAX_LIST_WINDOW];
- struct mdp3_img_data src_data[MAX_LIST_WINDOW];
- struct mdp3_img_data dst_data[MAX_LIST_WINDOW];
- struct sync_fence *acq_fen[MDP_MAX_FENCE_FD];
- u32 acq_fen_cnt;
- int cur_rel_fen_fd;
- struct sync_pt *cur_rel_sync_pt;
- struct sync_fence *cur_rel_fence;
- struct sync_fence *last_rel_fence;
- };
- struct blit_req_queue {
- struct blit_req_list req[MDP3_PPP_MAX_LIST_REQ];
- int count;
- int push_idx;
- int pop_idx;
- };
- struct ppp_status {
- bool wait_for_pop;
- struct completion ppp_comp;
- struct completion pop_q_comp;
- struct mutex req_mutex; /* Protect request queue */
- struct mutex config_ppp_mutex; /* Only one client configure register */
- struct msm_fb_data_type *mfd;
- struct work_struct blit_work;
- struct blit_req_queue req_q;
- struct sw_sync_timeline *timeline;
- int timeline_value;
- struct timer_list free_bw_timer;
- struct work_struct free_bw_work;
- bool bw_on;
- bool bw_optimal;
- };
- static struct ppp_status *ppp_stat;
- int ppp_get_bpp(uint32_t format, uint32_t fb_format)
- {
- int bpp = -EINVAL;
- if (format == MDP_FB_FORMAT)
- format = fb_format;
- bpp = ppp_bpp(format);
- if (bpp <= 0)
- pr_err("%s incorrect format %d\n", __func__, format);
- return bpp;
- }
- int mdp3_ppp_get_img(struct mdp_img *img, struct mdp_blit_req *req,
- struct mdp3_img_data *data)
- {
- struct msmfb_data fb_data;
- uint32_t stride;
- int bpp = ppp_bpp(img->format);
- if (bpp <= 0) {
- pr_err("%s incorrect format %d\n", __func__, img->format);
- return -EINVAL;
- }
- fb_data.flags = img->priv;
- fb_data.memory_id = img->memory_id;
- fb_data.offset = 0;
- stride = img->width * bpp;
- data->padding = 16 * stride;
- return mdp3_get_img(&fb_data, data, MDP3_CLIENT_PPP);
- }
- /* Check format */
- int mdp3_ppp_verify_fmt(struct mdp_blit_req *req)
- {
- if (MDP_IS_IMGTYPE_BAD(req->src.format) ||
- MDP_IS_IMGTYPE_BAD(req->dst.format)) {
- pr_err("%s: Color format out of range\n", __func__);
- return -EINVAL;
- }
- if (!valid_fmt[req->src.format] ||
- !valid_fmt[req->dst.format]) {
- pr_err("%s: Color format not supported\n", __func__);
- return -EINVAL;
- }
- return 0;
- }
- /* Check resolution */
- int mdp3_ppp_verify_res(struct mdp_blit_req *req)
- {
- if ((req->src.width == 0) || (req->src.height == 0) ||
- (req->src_rect.w == 0) || (req->src_rect.h == 0) ||
- (req->dst.width == 0) || (req->dst.height == 0) ||
- (req->dst_rect.w == 0) || (req->dst_rect.h == 0)) {
- pr_err("%s: Height/width can't be 0\n", __func__);
- return -EINVAL;
- }
- if (((req->src_rect.x + req->src_rect.w) > req->src.width) ||
- ((req->src_rect.y + req->src_rect.h) > req->src.height)) {
- pr_err("%s: src roi larger than boundary\n", __func__);
- return -EINVAL;
- }
- if (((req->dst_rect.x + req->dst_rect.w) > req->dst.width) ||
- ((req->dst_rect.y + req->dst_rect.h) > req->dst.height)) {
- pr_err("%s: dst roi larger than boundary\n", __func__);
- return -EINVAL;
- }
- return 0;
- }
- /* scaling range check */
- int mdp3_ppp_verify_scale(struct mdp_blit_req *req)
- {
- u32 src_width, src_height, dst_width, dst_height;
- src_width = req->src_rect.w;
- src_height = req->src_rect.h;
- if (req->flags & MDP_ROT_90) {
- dst_width = req->dst_rect.h;
- dst_height = req->dst_rect.w;
- } else {
- dst_width = req->dst_rect.w;
- dst_height = req->dst_rect.h;
- }
- switch (req->dst.format) {
- case MDP_Y_CRCB_H2V2:
- case MDP_Y_CBCR_H2V2:
- src_width = (src_width / 2) * 2;
- src_height = (src_height / 2) * 2;
- dst_width = (dst_width / 2) * 2;
- dst_height = (dst_height / 2) * 2;
- break;
- case MDP_Y_CRCB_H2V1:
- case MDP_Y_CBCR_H2V1:
- case MDP_YCRYCB_H2V1:
- src_width = (src_width / 2) * 2;
- dst_width = (dst_width / 2) * 2;
- break;
- default:
- break;
- }
- if (((MDP_SCALE_Q_FACTOR * dst_width) / src_width >
- MDP_MAX_X_SCALE_FACTOR)
- || ((MDP_SCALE_Q_FACTOR * dst_width) / src_width <
- MDP_MIN_X_SCALE_FACTOR)) {
- pr_err("%s: x req scale factor beyond capability\n", __func__);
- return -EINVAL;
- }
- if (((MDP_SCALE_Q_FACTOR * dst_height) / src_height >
- MDP_MAX_Y_SCALE_FACTOR)
- || ((MDP_SCALE_Q_FACTOR * dst_height) / src_height <
- MDP_MIN_Y_SCALE_FACTOR)) {
- pr_err("%s: y req scale factor beyond capability\n", __func__);
- return -EINVAL;
- }
- return 0;
- }
- /* operation check */
- int mdp3_ppp_verify_op(struct mdp_blit_req *req)
- {
- if (req->flags & MDP_DEINTERLACE) {
- pr_err("\n%s(): deinterlace not supported", __func__);
- return -EINVAL;
- }
- if (req->flags & MDP_SHARPENING) {
- pr_err("\n%s(): sharpening not supported", __func__);
- return -EINVAL;
- }
- return 0;
- }
- int mdp3_ppp_verify_req(struct mdp_blit_req *req)
- {
- int rc;
- if (req == NULL) {
- pr_err("%s: req == null\n", __func__);
- return -EINVAL;
- }
- rc = mdp3_ppp_verify_fmt(req);
- rc |= mdp3_ppp_verify_res(req);
- rc |= mdp3_ppp_verify_scale(req);
- rc |= mdp3_ppp_verify_op(req);
- return rc;
- }
- int mdp3_ppp_pipe_wait(void)
- {
- int ret = 1;
- /*
- * wait 200 ms for ppp operation to complete before declaring
- * the MDP hung
- */
- ret = wait_for_completion_timeout(
- &ppp_stat->ppp_comp, msecs_to_jiffies(200));
- if (!ret)
- pr_err("%s: Timed out waiting for the MDP.\n",
- __func__);
- return ret;
- }
- uint32_t mdp3_calc_tpval(struct ppp_img_desc *img, uint32_t old_tp)
- {
- uint32_t tpVal;
- uint8_t plane_tp;
- tpVal = 0;
- if ((img->color_fmt == MDP_RGB_565)
- || (img->color_fmt == MDP_BGR_565)) {
- /* transparent color conversion into 24 bpp */
- plane_tp = (uint8_t) ((old_tp & 0xF800) >> 11);
- tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 16;
- plane_tp = (uint8_t) (old_tp & 0x1F);
- tpVal |= ((plane_tp << 3) | ((plane_tp & 0x1C) >> 2)) << 8;
- plane_tp = (uint8_t) ((old_tp & 0x7E0) >> 5);
- tpVal |= ((plane_tp << 2) | ((plane_tp & 0x30) >> 4));
- } else {
- /* 24bit RGB to RBG conversion */
- tpVal = (old_tp & 0xFF00) >> 8;
- tpVal |= (old_tp & 0xFF) << 8;
- tpVal |= (old_tp & 0xFF0000);
- }
- return tpVal;
- }
- static void mdp3_ppp_intr_handler(int type, void *arg)
- {
- complete(&ppp_stat->ppp_comp);
- }
- static int mdp3_ppp_callback_setup(void)
- {
- int rc;
- struct mdp3_intr_cb ppp_done_cb = {
- .cb = mdp3_ppp_intr_handler,
- .data = NULL,
- };
- rc = mdp3_set_intr_callback(MDP3_PPP_DONE, &ppp_done_cb);
- return rc;
- }
- void mdp3_ppp_kickoff(void)
- {
- init_completion(&ppp_stat->ppp_comp);
- mdp3_irq_enable(MDP3_PPP_DONE);
- ppp_enable();
- ATRACE_BEGIN("mdp3_wait_for_ppp_comp");
- mdp3_ppp_pipe_wait();
- ATRACE_END("mdp3_wait_for_ppp_comp");
- mdp3_irq_disable(MDP3_PPP_DONE);
- }
- int mdp3_ppp_vote_update(struct msm_fb_data_type *mfd)
- {
- struct mdss_panel_info *panel_info = mfd->panel_info;
- uint64_t req_bw = 0, ab = 0, ib = 0;
- int rate = 0;
- int rc = 0;
- if (!ppp_stat->bw_on)
- pr_err("%s: PPP vote update in wrong state\n", __func__);
- rate = MDP_BLIT_CLK_RATE;
- req_bw = panel_info->xres * panel_info->yres *
- panel_info->mipi.frame_rate *
- MDP_PPP_MAX_BPP *
- MDP_PPP_DYNAMIC_FACTOR *
- MDP_PPP_MAX_READ_WRITE;
- ib = (req_bw * 3) / 2;
- if (ppp_stat->bw_optimal)
- ab = ib / 2;
- else
- ab = req_bw;
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib);
- if (rc < 0) {
- pr_err("%s: scale_set_quota failed\n", __func__);
- return rc;
- }
- return rc;
- }
- int mdp3_ppp_turnon(struct msm_fb_data_type *mfd, int on_off)
- {
- struct mdss_panel_info *panel_info = mfd->panel_info;
- uint64_t req_bw = 0, ab = 0, ib = 0;
- int rate = 0;
- int rc;
- if (on_off) {
- rate = MDP_BLIT_CLK_RATE;
- req_bw = panel_info->xres * panel_info->yres *
- panel_info->mipi.frame_rate *
- MDP_PPP_MAX_BPP *
- MDP_PPP_DYNAMIC_FACTOR *
- MDP_PPP_MAX_READ_WRITE;
- ib = (req_bw * 3) / 2;
- if (ppp_stat->bw_optimal)
- ab = ib / 2;
- else
- ab = req_bw;
- }
- mdp3_clk_set_rate(MDP3_CLK_CORE, rate, MDP3_CLIENT_PPP);
- rc = mdp3_res_update(on_off, 0, MDP3_CLIENT_PPP);
- if (rc < 0) {
- pr_err("%s: mdp3_clk_enable failed\n", __func__);
- return rc;
- }
- rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_PPP, ab, ib);
- if (rc < 0) {
- mdp3_res_update(!on_off, 0, MDP3_CLIENT_PPP);
- pr_err("%s: scale_set_quota failed\n", __func__);
- return rc;
- }
- ppp_stat->bw_on = on_off;
- return 0;
- }
- bool mdp3_optimal_bw(struct blit_req_list *req)
- {
- int i, solid_fill = 0;
- if (!req || (ppp_stat->req_q.count > 1))
- return false;
- for (i = 0; i < req->count; i++) {
- if (req->req_list[i].flags & MDP_SOLID_FILL)
- solid_fill++;
- }
- if ((req->count - solid_fill) <= 1)
- return true;
- return false;
- }
- void mdp3_start_ppp(struct ppp_blit_op *blit_op)
- {
- /* Wait for the pipe to clear */
- if (MDP3_REG_READ(MDP3_REG_DISPLAY_STATUS) &
- MDP3_PPP_ACTIVE) {
- pr_err("ppp core is hung up on previous request\n");
- return;
- }
- config_ppp_op_mode(blit_op);
- if (blit_op->solid_fill) {
- MDP3_REG_WRITE(0x10138, 0x10000000);
- MDP3_REG_WRITE(0x1014c, 0xffffffff);
- MDP3_REG_WRITE(0x101b8, 0);
- MDP3_REG_WRITE(0x101bc, 0);
- MDP3_REG_WRITE(0x1013c, 0);
- MDP3_REG_WRITE(0x10140, 0);
- MDP3_REG_WRITE(0x10144, 0);
- MDP3_REG_WRITE(0x10148, 0);
- MDP3_REG_WRITE(MDP3_TFETCH_FILL_COLOR,
- blit_op->solid_fill_color);
- MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL,
- ENABLE_SOLID_FILL);
- } else {
- MDP3_REG_WRITE(MDP3_TFETCH_SOLID_FILL,
- DISABLE_SOLID_FILL);
- }
- mdp3_ppp_kickoff();
- }
- static int solid_fill_workaround(struct mdp_blit_req *req,
- struct ppp_blit_op *blit_op)
- {
- /* Make width 2 when there is a solid fill of width 1, and make
- sure width does not become zero while trying to avoid odd width */
- if (blit_op->dst.roi.width == 1) {
- if (req->dst_rect.x + 2 > req->dst.width) {
- pr_err("%s: Unable to handle solid fill of width 1",
- __func__);
- return -EINVAL;
- }
- blit_op->dst.roi.width = 2;
- }
- if (blit_op->src.roi.width == 1) {
- if (req->src_rect.x + 2 > req->src.width) {
- pr_err("%s: Unable to handle solid fill of width 1",
- __func__);
- return -EINVAL;
- }
- blit_op->src.roi.width = 2;
- }
- /* Avoid odd width, as it could hang ppp during solid fill */
- blit_op->dst.roi.width = (blit_op->dst.roi.width / 2) * 2;
- blit_op->src.roi.width = (blit_op->src.roi.width / 2) * 2;
- /* Avoid RGBA format, as it could hang ppp during solid fill */
- if (blit_op->src.color_fmt == MDP_RGBA_8888)
- blit_op->src.color_fmt = MDP_RGBX_8888;
- if (blit_op->dst.color_fmt == MDP_RGBA_8888)
- blit_op->dst.color_fmt = MDP_RGBX_8888;
- return 0;
- }
- static int mdp3_ppp_process_req(struct ppp_blit_op *blit_op,
- struct mdp_blit_req *req, struct mdp3_img_data *src_data,
- struct mdp3_img_data *dst_data)
- {
- unsigned long srcp0_start, srcp0_len, dst_start, dst_len;
- uint32_t dst_width, dst_height;
- int ret = 0;
- srcp0_start = (unsigned long) src_data->addr;
- srcp0_len = (unsigned long) src_data->len;
- dst_start = (unsigned long) dst_data->addr;
- dst_len = (unsigned long) dst_data->len;
- blit_op->dst.prop.width = req->dst.width;
- blit_op->dst.prop.height = req->dst.height;
- blit_op->dst.color_fmt = req->dst.format;
- blit_op->dst.p0 = (void *) dst_start;
- blit_op->dst.p0 += req->dst.offset;
- blit_op->dst.roi.x = req->dst_rect.x;
- blit_op->dst.roi.y = req->dst_rect.y;
- blit_op->dst.roi.width = req->dst_rect.w;
- blit_op->dst.roi.height = req->dst_rect.h;
- blit_op->src.roi.x = req->src_rect.x;
- blit_op->src.roi.y = req->src_rect.y;
- blit_op->src.roi.width = req->src_rect.w;
- blit_op->src.roi.height = req->src_rect.h;
- blit_op->src.prop.width = req->src.width;
- blit_op->src.color_fmt = req->src.format;
- blit_op->src.p0 = (void *) (srcp0_start + req->src.offset);
- if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_ADRENO)
- blit_op->src.p1 =
- (void *) ((uint32_t) blit_op->src.p0 +
- ALIGN((ALIGN(req->src.width, 32) *
- ALIGN(req->src.height, 32)), 4096));
- else if (blit_op->src.color_fmt == MDP_Y_CBCR_H2V2_VENUS)
- blit_op->src.p1 =
- (void *) ((uint32_t) blit_op->src.p0 +
- ALIGN((ALIGN(req->src.width, 128) *
- ALIGN(req->src.height, 32)), 4096));
- else
- blit_op->src.p1 = (void *) ((uint32_t) blit_op->src.p0 +
- req->src.width * req->src.height);
- if (req->flags & MDP_IS_FG)
- blit_op->mdp_op |= MDPOP_LAYER_IS_FG;
- /* blending check */
- if (req->transp_mask != MDP_TRANSP_NOP) {
- blit_op->mdp_op |= MDPOP_TRANSP;
- blit_op->blend.trans_color =
- mdp3_calc_tpval(&blit_op->src, req->transp_mask);
- } else {
- blit_op->blend.trans_color = 0;
- }
- req->alpha &= 0xff;
- if (req->alpha < MDP_ALPHA_NOP) {
- blit_op->mdp_op |= MDPOP_ALPHAB;
- blit_op->blend.const_alpha = req->alpha;
- } else {
- blit_op->blend.const_alpha = 0xff;
- }
- /* rotation check */
- if (req->flags & MDP_FLIP_LR)
- blit_op->mdp_op |= MDPOP_LR;
- if (req->flags & MDP_FLIP_UD)
- blit_op->mdp_op |= MDPOP_UD;
- if (req->flags & MDP_ROT_90)
- blit_op->mdp_op |= MDPOP_ROT90;
- if (req->flags & MDP_DITHER)
- blit_op->mdp_op |= MDPOP_DITHER;
- if (req->flags & MDP_BLEND_FG_PREMULT)
- blit_op->mdp_op |= MDPOP_FG_PM_ALPHA;
- /* scale check */
- if (req->flags & MDP_ROT_90) {
- dst_width = req->dst_rect.h;
- dst_height = req->dst_rect.w;
- } else {
- dst_width = req->dst_rect.w;
- dst_height = req->dst_rect.h;
- }
- if ((blit_op->src.roi.width != dst_width) ||
- (blit_op->src.roi.height != dst_height))
- blit_op->mdp_op |= MDPOP_ASCALE;
- if (req->flags & MDP_BLUR)
- blit_op->mdp_op |= MDPOP_ASCALE | MDPOP_BLUR;
- if (req->flags & MDP_SOLID_FILL) {
- ret = solid_fill_workaround(req, blit_op);
- if (ret)
- return ret;
- blit_op->solid_fill_color = (req->const_color.g & 0xFF)|
- (req->const_color.r & 0xFF) << 8 |
- (req->const_color.b & 0xFF) << 16 |
- (req->const_color.alpha & 0xFF) << 24;
- blit_op->solid_fill = true;
- } else {
- blit_op->solid_fill = false;
- }
- return ret;
- }
- static void mdp3_ppp_tile_workaround(struct ppp_blit_op *blit_op,
- struct mdp_blit_req *req)
- {
- int dst_h, src_w, i;
- uint32_t mdp_op = blit_op->mdp_op;
- void *src_p0 = blit_op->src.p0;
- void *src_p1 = blit_op->src.p1;
- void *dst_p0 = blit_op->dst.p0;
- src_w = req->src_rect.w;
- dst_h = blit_op->dst.roi.height;
- /* bg tile fetching HW workaround */
- for (i = 0; i < (req->dst_rect.h / 16); i++) {
- /* this tile size */
- blit_op->dst.roi.height = 16;
- blit_op->src.roi.width =
- (16 * req->src_rect.w) / req->dst_rect.h;
- /* if it's out of scale range... */
- if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR)
- blit_op->src.roi.width =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MAX_X_SCALE_FACTOR;
- else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR)
- blit_op->src.roi.width =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MIN_X_SCALE_FACTOR;
- mdp3_start_ppp(blit_op);
- /* next tile location */
- blit_op->dst.roi.y += 16;
- blit_op->src.roi.x += blit_op->src.roi.width;
- /* this is for a remainder update */
- dst_h -= 16;
- src_w -= blit_op->src.roi.width;
- /* restore parameters that may have been overwritten */
- blit_op->mdp_op = mdp_op;
- blit_op->src.p0 = src_p0;
- blit_op->src.p1 = src_p1;
- blit_op->dst.p0 = dst_p0;
- }
- if ((dst_h < 0) || (src_w < 0))
- pr_err
- ("msm_fb: mdp_blt_ex() unexpected result! line:%d\n",
- __LINE__);
- /* remainder update */
- if ((dst_h > 0) && (src_w > 0)) {
- u32 tmp_v;
- blit_op->dst.roi.height = dst_h;
- blit_op->src.roi.width = src_w;
- if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) > MDP_MAX_X_SCALE_FACTOR) {
- tmp_v =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MAX_X_SCALE_FACTOR +
- ((MDP_SCALE_Q_FACTOR *
- blit_op->dst.roi.height) %
- MDP_MAX_X_SCALE_FACTOR ? 1 : 0);
- /* move x location as roi width gets bigger */
- blit_op->src.roi.x -= tmp_v - blit_op->src.roi.width;
- blit_op->src.roi.width = tmp_v;
- } else if (((MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- blit_op->src.roi.width) < MDP_MIN_X_SCALE_FACTOR) {
- tmp_v =
- (MDP_SCALE_Q_FACTOR * blit_op->dst.roi.height) /
- MDP_MIN_X_SCALE_FACTOR +
- ((MDP_SCALE_Q_FACTOR *
- blit_op->dst.roi.height) %
- MDP_MIN_X_SCALE_FACTOR ? 1 : 0);
- /*
- * we don't move x location for continuity of
- * source image
- */
- blit_op->src.roi.width = tmp_v;
- }
- mdp3_start_ppp(blit_op);
- }
- }
- static int mdp3_ppp_blit(struct msm_fb_data_type *mfd,
- struct mdp_blit_req *req, struct mdp3_img_data *src_data,
- struct mdp3_img_data *dst_data)
- {
- struct ppp_blit_op blit_op;
- int ret = 0;
- memset(&blit_op, 0, sizeof(blit_op));
- if (req->dst.format == MDP_FB_FORMAT)
- req->dst.format = mfd->fb_imgType;
- if (req->src.format == MDP_FB_FORMAT)
- req->src.format = mfd->fb_imgType;
- if (mdp3_ppp_verify_req(req)) {
- pr_err("%s: invalid image!\n", __func__);
- return -EINVAL;
- }
- ret = mdp3_ppp_process_req(&blit_op, req, src_data, dst_data);
- if (ret) {
- pr_err("%s: Failed to process the blit request", __func__);
- return ret;
- }
- if (((blit_op.mdp_op & (MDPOP_TRANSP | MDPOP_ALPHAB)) ||
- (req->src.format == MDP_ARGB_8888) ||
- (req->src.format == MDP_BGRA_8888) ||
- (req->src.format == MDP_RGBA_8888)) &&
- (blit_op.mdp_op & MDPOP_ROT90) && (req->dst_rect.w <= 16)) {
- mdp3_ppp_tile_workaround(&blit_op, req);
- } else {
- mdp3_start_ppp(&blit_op);
- }
- return 0;
- }
- static int mdp3_ppp_blit_workaround(struct msm_fb_data_type *mfd,
- struct mdp_blit_req *req, unsigned int remainder,
- struct mdp3_img_data *src_data,
- struct mdp3_img_data *dst_data)
- {
- int ret;
- struct mdp_blit_req splitreq;
- int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
- int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
- /* make new request as provide by user */
- splitreq = *req;
- /* break dest roi at width*/
- d_y_0 = d_y_1 = req->dst_rect.y;
- d_h_0 = d_h_1 = req->dst_rect.h;
- d_x_0 = req->dst_rect.x;
- if (remainder == 14 || remainder == 6)
- d_w_1 = req->dst_rect.w / 2;
- else
- d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
- d_w_0 = req->dst_rect.w - d_w_1;
- d_x_1 = d_x_0 + d_w_0;
- /* blit first region */
- if (((splitreq.flags & 0x07) == 0x07) ||
- ((splitreq.flags & 0x07) == 0x05) ||
- ((splitreq.flags & 0x07) == 0x02) ||
- ((splitreq.flags & 0x07) == 0x0)) {
- if (splitreq.flags & MDP_ROT_90) {
- s_x_0 = s_x_1 = req->src_rect.x;
- s_w_0 = s_w_1 = req->src_rect.w;
- s_y_0 = req->src_rect.y;
- s_h_1 = (req->src_rect.h * d_w_1) /
- req->dst_rect.w;
- s_h_0 = req->src_rect.h - s_h_1;
- s_y_1 = s_y_0 + s_h_0;
- if (d_w_1 >= 8 * s_h_1) {
- s_h_1++;
- s_y_1--;
- }
- } else {
- s_y_0 = s_y_1 = req->src_rect.y;
- s_h_0 = s_h_1 = req->src_rect.h;
- s_x_0 = req->src_rect.x;
- s_w_1 = (req->src_rect.w * d_w_1) /
- req->dst_rect.w;
- s_w_0 = req->src_rect.w - s_w_1;
- s_x_1 = s_x_0 + s_w_0;
- if (d_w_1 >= 8 * s_w_1) {
- s_w_1++;
- s_x_1--;
- }
- }
- splitreq.src_rect.h = s_h_0;
- splitreq.src_rect.y = s_y_0;
- splitreq.dst_rect.h = d_h_0;
- splitreq.dst_rect.y = d_y_0;
- splitreq.src_rect.x = s_x_0;
- splitreq.src_rect.w = s_w_0;
- splitreq.dst_rect.x = d_x_0;
- splitreq.dst_rect.w = d_w_0;
- } else {
- if (splitreq.flags & MDP_ROT_90) {
- s_x_0 = s_x_1 = req->src_rect.x;
- s_w_0 = s_w_1 = req->src_rect.w;
- s_y_0 = req->src_rect.y;
- s_h_1 = (req->src_rect.h * d_w_0) /
- req->dst_rect.w;
- s_h_0 = req->src_rect.h - s_h_1;
- s_y_1 = s_y_0 + s_h_0;
- if (d_w_0 >= 8 * s_h_1) {
- s_h_1++;
- s_y_1--;
- }
- } else {
- s_y_0 = s_y_1 = req->src_rect.y;
- s_h_0 = s_h_1 = req->src_rect.h;
- s_x_0 = req->src_rect.x;
- s_w_1 = (req->src_rect.w * d_w_0) /
- req->dst_rect.w;
- s_w_0 = req->src_rect.w - s_w_1;
- s_x_1 = s_x_0 + s_w_0;
- if (d_w_0 >= 8 * s_w_1) {
- s_w_1++;
- s_x_1--;
- }
- }
- splitreq.src_rect.h = s_h_0;
- splitreq.src_rect.y = s_y_0;
- splitreq.dst_rect.h = d_h_1;
- splitreq.dst_rect.y = d_y_1;
- splitreq.src_rect.x = s_x_0;
- splitreq.src_rect.w = s_w_0;
- splitreq.dst_rect.x = d_x_1;
- splitreq.dst_rect.w = d_w_1;
- }
- /* No need to split in height */
- ret = mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data);
- if (ret)
- return ret;
- /* blit second region */
- if (((splitreq.flags & 0x07) == 0x07) ||
- ((splitreq.flags & 0x07) == 0x05) ||
- ((splitreq.flags & 0x07) == 0x02) ||
- ((splitreq.flags & 0x07) == 0x0)) {
- splitreq.src_rect.h = s_h_1;
- splitreq.src_rect.y = s_y_1;
- splitreq.dst_rect.h = d_h_1;
- splitreq.dst_rect.y = d_y_1;
- splitreq.src_rect.x = s_x_1;
- splitreq.src_rect.w = s_w_1;
- splitreq.dst_rect.x = d_x_1;
- splitreq.dst_rect.w = d_w_1;
- } else {
- splitreq.src_rect.h = s_h_1;
- splitreq.src_rect.y = s_y_1;
- splitreq.dst_rect.h = d_h_0;
- splitreq.dst_rect.y = d_y_0;
- splitreq.src_rect.x = s_x_1;
- splitreq.src_rect.w = s_w_1;
- splitreq.dst_rect.x = d_x_0;
- splitreq.dst_rect.w = d_w_0;
- }
- /* No need to split in height ... just width */
- return mdp3_ppp_blit(mfd, &splitreq, src_data, dst_data);
- }
- int mdp3_ppp_start_blit(struct msm_fb_data_type *mfd,
- struct mdp_blit_req *req,
- struct mdp3_img_data *src_data,
- struct mdp3_img_data *dst_data)
- {
- int ret;
- unsigned int remainder = 0, is_bpp_4 = 0;
- if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) {
- pr_err("mdp_ppp: src img of zero size!\n");
- return -EINVAL;
- }
- if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0))
- return 0;
- /* MDP width split workaround */
- remainder = (req->dst_rect.w) % 16;
- ret = ppp_get_bpp(req->dst.format, mfd->fb_imgType);
- if (ret <= 0) {
- pr_err("mdp_ppp: incorrect bpp!\n");
- return -EINVAL;
- }
- is_bpp_4 = (ret == 4) ? 1 : 0;
- if ((is_bpp_4 && (remainder == 6 || remainder == 14)) &&
- !(req->flags & MDP_SOLID_FILL))
- ret = mdp3_ppp_blit_workaround(mfd, req, remainder,
- src_data, dst_data);
- else
- ret = mdp3_ppp_blit(mfd, req, src_data, dst_data);
- return ret;
- }
- void mdp3_ppp_wait_for_fence(struct blit_req_list *req)
- {
- int i, ret = 0;
- ATRACE_BEGIN(__func__);
- /* buf sync */
- for (i = 0; i < req->acq_fen_cnt; i++) {
- ret = sync_fence_wait(req->acq_fen[i],
- WAIT_FENCE_FINAL_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: sync_fence_wait failed! ret = %x\n",
- __func__, ret);
- break;
- }
- sync_fence_put(req->acq_fen[i]);
- }
- ATRACE_END(__func__);
- if (ret < 0) {
- while (i < req->acq_fen_cnt) {
- sync_fence_put(req->acq_fen[i]);
- i++;
- }
- }
- req->acq_fen_cnt = 0;
- }
- void mdp3_ppp_signal_timeline(struct blit_req_list *req)
- {
- sw_sync_timeline_inc(ppp_stat->timeline, 1);
- req->last_rel_fence = req->cur_rel_fence;
- req->cur_rel_fence = 0;
- }
- static void mdp3_ppp_deinit_buf_sync(struct blit_req_list *req)
- {
- int i;
- put_unused_fd(req->cur_rel_fen_fd);
- sync_fence_put(req->cur_rel_fence);
- req->cur_rel_fence = NULL;
- req->cur_rel_fen_fd = 0;
- ppp_stat->timeline_value--;
- for (i = 0; i < req->acq_fen_cnt; i++)
- sync_fence_put(req->acq_fen[i]);
- req->acq_fen_cnt = 0;
- }
- static int mdp3_ppp_handle_buf_sync(struct blit_req_list *req,
- struct mdp_buf_sync *buf_sync)
- {
- int i, fence_cnt = 0, ret = 0;
- int acq_fen_fd[MDP_MAX_FENCE_FD];
- struct sync_fence *fence;
- if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) ||
- (ppp_stat->timeline == NULL))
- return -EINVAL;
- if (buf_sync->acq_fen_fd_cnt)
- ret = copy_from_user(acq_fen_fd, buf_sync->acq_fen_fd,
- buf_sync->acq_fen_fd_cnt * sizeof(int));
- if (ret) {
- pr_err("%s: copy_from_user failed\n", __func__);
- return ret;
- }
- for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) {
- fence = sync_fence_fdget(acq_fen_fd[i]);
- if (fence == NULL) {
- pr_info("%s: null fence! i=%d fd=%d\n", __func__, i,
- acq_fen_fd[i]);
- ret = -EINVAL;
- break;
- }
- req->acq_fen[i] = fence;
- }
- fence_cnt = i;
- if (ret)
- goto buf_sync_err_1;
- req->acq_fen_cnt = fence_cnt;
- if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT)
- mdp3_ppp_wait_for_fence(req);
- req->cur_rel_sync_pt = sw_sync_pt_create(ppp_stat->timeline,
- ppp_stat->timeline_value++);
- if (req->cur_rel_sync_pt == NULL) {
- pr_err("%s: cannot create sync point\n", __func__);
- ret = -ENOMEM;
- goto buf_sync_err_2;
- }
- /* create fence */
- req->cur_rel_fence = sync_fence_create("ppp-fence",
- req->cur_rel_sync_pt);
- if (req->cur_rel_fence == NULL) {
- sync_pt_free(req->cur_rel_sync_pt);
- req->cur_rel_sync_pt = NULL;
- pr_err("%s: cannot create fence\n", __func__);
- ret = -ENOMEM;
- goto buf_sync_err_2;
- }
- /* create fd */
- return ret;
- buf_sync_err_2:
- ppp_stat->timeline_value--;
- buf_sync_err_1:
- for (i = 0; i < fence_cnt; i++)
- sync_fence_put(req->acq_fen[i]);
- req->acq_fen_cnt = 0;
- return ret;
- }
- void mdp3_ppp_req_push(struct blit_req_queue *req_q, struct blit_req_list *req)
- {
- int idx = req_q->push_idx;
- req_q->req[idx] = *req;
- req_q->count++;
- req_q->push_idx = (req_q->push_idx + 1) % MDP3_PPP_MAX_LIST_REQ;
- }
- struct blit_req_list *mdp3_ppp_next_req(struct blit_req_queue *req_q)
- {
- struct blit_req_list *req;
- if (req_q->count == 0)
- return NULL;
- req = &req_q->req[req_q->pop_idx];
- return req;
- }
- void mdp3_ppp_req_pop(struct blit_req_queue *req_q)
- {
- req_q->count--;
- req_q->pop_idx = (req_q->pop_idx + 1) % MDP3_PPP_MAX_LIST_REQ;
- }
- void mdp3_free_fw_timer_func(unsigned long arg)
- {
- schedule_work(&ppp_stat->free_bw_work);
- }
- static void mdp3_free_bw_wq_handler(struct work_struct *work)
- {
- struct msm_fb_data_type *mfd = ppp_stat->mfd;
- mutex_lock(&ppp_stat->config_ppp_mutex);
- if (ppp_stat->bw_on) {
- mdp3_ppp_turnon(mfd, 0);
- }
- mutex_unlock(&ppp_stat->config_ppp_mutex);
- }
- static void mdp3_ppp_blit_wq_handler(struct work_struct *work)
- {
- struct msm_fb_data_type *mfd = ppp_stat->mfd;
- struct blit_req_list *req;
- int i, rc = 0;
- mutex_lock(&ppp_stat->config_ppp_mutex);
- req = mdp3_ppp_next_req(&ppp_stat->req_q);
- if (!req) {
- mutex_unlock(&ppp_stat->config_ppp_mutex);
- return;
- }
- if (!ppp_stat->bw_on) {
- ppp_stat->bw_optimal = mdp3_optimal_bw(req);
- mdp3_ppp_turnon(mfd, 1);
- if (rc < 0) {
- mutex_unlock(&ppp_stat->config_ppp_mutex);
- pr_err("%s: Enable ppp resources failed\n", __func__);
- return;
- }
- }
- while (req) {
- mdp3_ppp_wait_for_fence(req);
- ATRACE_BEGIN("mdp3_ppp_start");
- for (i = 0; i < req->count; i++) {
- if (!(req->req_list[i].flags & MDP_NO_BLIT)) {
- /* Do the actual blit. */
- if (!rc) {
- rc = mdp3_ppp_start_blit(mfd,
- &(req->req_list[i]),
- &req->src_data[i],
- &req->dst_data[i]);
- }
- mdp3_put_img(&req->src_data[i],
- MDP3_CLIENT_PPP);
- mdp3_put_img(&req->dst_data[i],
- MDP3_CLIENT_PPP);
- }
- }
- ATRACE_END("mdp3_ppp_start");
- /* Signal to release fence */
- mutex_lock(&ppp_stat->req_mutex);
- mdp3_ppp_signal_timeline(req);
- mdp3_ppp_req_pop(&ppp_stat->req_q);
- req = mdp3_ppp_next_req(&ppp_stat->req_q);
- if (ppp_stat->wait_for_pop)
- complete(&ppp_stat->pop_q_comp);
- mutex_unlock(&ppp_stat->req_mutex);
- if (req && (ppp_stat->bw_optimal != mdp3_optimal_bw(req))) {
- ppp_stat->bw_optimal = !ppp_stat->bw_optimal;
- mdp3_ppp_vote_update(mfd);
- }
- }
- mod_timer(&ppp_stat->free_bw_timer, jiffies +
- msecs_to_jiffies(MDP_RELEASE_BW_TIMEOUT));
- mutex_unlock(&ppp_stat->config_ppp_mutex);
- }
- int mdp3_ppp_parse_req(void __user *p,
- struct mdp_async_blit_req_list *req_list_header,
- int async)
- {
- struct blit_req_list *req;
- struct blit_req_queue *req_q = &ppp_stat->req_q;
- struct sync_fence *fence = NULL;
- int count, rc, idx, i;
- count = req_list_header->count;
- mutex_lock(&ppp_stat->req_mutex);
- while (req_q->count >= MDP3_PPP_MAX_LIST_REQ) {
- ppp_stat->wait_for_pop = true;
- mutex_unlock(&ppp_stat->req_mutex);
- rc = wait_for_completion_timeout(
- &ppp_stat->pop_q_comp, 5 * HZ);
- if (rc == 0) {
- /* This will only occur if there is serious problem */
- pr_err("%s: timeout exiting queuing request\n",
- __func__);
- return -EBUSY;
- }
- mutex_lock(&ppp_stat->req_mutex);
- ppp_stat->wait_for_pop = false;
- }
- idx = req_q->push_idx;
- req = &req_q->req[idx];
- if (copy_from_user(&req->req_list, p,
- sizeof(struct mdp_blit_req) * count)) {
- mutex_unlock(&ppp_stat->req_mutex);
- return -EFAULT;
- }
- rc = mdp3_ppp_handle_buf_sync(req, &req_list_header->sync);
- if (rc < 0) {
- pr_err("%s: Failed create sync point\n", __func__);
- mutex_unlock(&ppp_stat->req_mutex);
- return rc;
- }
- req->count = count;
- /* We need to grab ion handle while running in client thread */
- for (i = 0; i < count; i++) {
- rc = mdp3_ppp_get_img(&req->req_list[i].src,
- &req->req_list[i], &req->src_data[i]);
- if (rc < 0 || req->src_data[i].len == 0) {
- pr_err("mdp_ppp: couldn't retrieve src img from mem\n");
- goto parse_err_1;
- }
- rc = mdp3_ppp_get_img(&req->req_list[i].dst,
- &req->req_list[i], &req->dst_data[i]);
- if (rc < 0 || req->dst_data[i].len == 0) {
- mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP);
- pr_err("mdp_ppp: couldn't retrieve dest img from mem\n");
- goto parse_err_1;
- }
- }
- if (async) {
- req->cur_rel_fen_fd = get_unused_fd_flags(0);
- if (req->cur_rel_fen_fd < 0) {
- pr_err("%s: get_unused_fd_flags failed\n", __func__);
- rc = -ENOMEM;
- goto parse_err_1;
- }
- sync_fence_install(req->cur_rel_fence, req->cur_rel_fen_fd);
- rc = copy_to_user(req_list_header->sync.rel_fen_fd,
- &req->cur_rel_fen_fd, sizeof(int));
- if (rc) {
- pr_err("%s:copy_to_user failed\n", __func__);
- goto parse_err_2;
- }
- } else {
- fence = req->cur_rel_fence;
- }
- mdp3_ppp_req_push(req_q, req);
- mutex_unlock(&ppp_stat->req_mutex);
- schedule_work(&ppp_stat->blit_work);
- if (!async) {
- /* wait for release fence */
- rc = sync_fence_wait(fence,
- 5 * MSEC_PER_SEC);
- if (rc < 0)
- pr_err("%s: sync blit! rc = %x\n", __func__, rc);
- sync_fence_put(fence);
- fence = NULL;
- }
- return 0;
- parse_err_2:
- put_unused_fd(req->cur_rel_fen_fd);
- parse_err_1:
- for (i--; i >= 0; i--) {
- mdp3_put_img(&req->src_data[i], MDP3_CLIENT_PPP);
- mdp3_put_img(&req->dst_data[i], MDP3_CLIENT_PPP);
- }
- mdp3_ppp_deinit_buf_sync(req);
- mutex_unlock(&ppp_stat->req_mutex);
- return rc;
- }
- int mdp3_ppp_res_init(struct msm_fb_data_type *mfd)
- {
- const char timeline_name[] = "mdp3_ppp";
- ppp_stat = kzalloc(sizeof(struct ppp_status), GFP_KERNEL);
- if (!ppp_stat) {
- pr_err("%s: kmalloc failed\n", __func__);
- return -ENOMEM;
- }
- /*Setup sync_pt timeline for ppp*/
- ppp_stat->timeline = sw_sync_timeline_create(timeline_name);
- if (ppp_stat->timeline == NULL) {
- pr_err("%s: cannot create time line\n", __func__);
- return -ENOMEM;
- } else {
- ppp_stat->timeline_value = 1;
- }
- INIT_WORK(&ppp_stat->blit_work, mdp3_ppp_blit_wq_handler);
- INIT_WORK(&ppp_stat->free_bw_work, mdp3_free_bw_wq_handler);
- init_completion(&ppp_stat->pop_q_comp);
- mutex_init(&ppp_stat->req_mutex);
- mutex_init(&ppp_stat->config_ppp_mutex);
- init_timer(&ppp_stat->free_bw_timer);
- ppp_stat->free_bw_timer.function = mdp3_free_fw_timer_func;
- ppp_stat->free_bw_timer.data = 0;
- ppp_stat->mfd = mfd;
- mdp3_ppp_callback_setup();
- return 0;
- }
|