// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2025 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* media module used media/registers/cpu_version.h since kernel 5.4 */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_AMLOGIC_MEDIA_RDMA #include #endif #include #include "register.h" #include "register_nr4.h" #include "deinterlace.h" #include "deinterlace_dbg.h" #include "nr_downscale.h" #include "di_pps.h" #include "di_pqa.h" /*2018-07-18 add debugfs*/ #include #include /*2018-07-18 -----------*/ /* #undef TRACE_INCLUDE_PATH #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_PATH . #define TRACE_INCLUDE_FILE deinterlace_trace #include */ #define CREATE_TRACE_POINTS #include "deinterlace_trace.h" #ifdef DET3D #include "detect3d.h" #endif #define ENABLE_SPIN_LOCK_ALWAYS #include #define DEVICE_NAME "deinterlace" #define CLASS_NAME "deinterlace" static struct di_pre_stru_s di_pre_stru; static struct di_post_stru_s di_post_stru; static DEFINE_SPINLOCK(di_lock2); #define di_lock_irqfiq_save(irq_flag) \ spin_lock_irqsave(&di_lock2, irq_flag) #define di_unlock_irqfiq_restore(irq_flag) \ spin_unlock_irqrestore(&di_lock2, irq_flag) #ifdef SUPPORT_MPEG_TO_VDIN static int mpeg2vdin_flag; static int mpeg2vdin_en; #endif static bool mc_mem_alloc; static struct mutex di_event_mutex; static atomic_t di_flag_unreg; //ary 2019-05-27 static atomic_t di_trig_free_mem; static unsigned int di_pre_rdma_enable; static int di_reg_unreg_cnt = 1000; static unsigned int overturn; static bool check_start_drop_prog; static bool mcpre_en = true; unsigned int di_force_bit_mode = 10; bool force_prog = true; bool combing_fix_en = true; int cur_lev = 2; static int pps_dstw; static int pps_dsth; static bool pps_en; static unsigned int pps_position = 1; static unsigned int pre_enable_mask = 3;/*bit0:ma bit1:mc*/ static bool pre_hsc_down_en; static int pre_hsc_down_width = 480; #ifdef DET3D static unsigned int det3d_frame_cnt = 50; #endif static bool calc_mcinfo_en = 1; static unsigned int colcfd_thr = 128; static unsigned int post_blend; static unsigned int post_ei; static unsigned int post_cnt; static bool post_refresh; static bool nrds_en; static int bypass_all; static int bypass_trick_mode = 1; static int bypass_3d = 1; static int invert_top_bot; static int skip_top_bot;/*1or2: may affect atv when bypass di*/ static int force_width; static int force_height; static int prog_proc_config = (1 << 5) | (1 << 1) | 1; static int start_frame_drop_count = 2; static long same_field_top_count; static long same_field_bot_count; static unsigned int di_debug_flag;/* enable rdma even di bypassed */ static unsigned int di_log_flag; static unsigned int buf_state_log_threshold = 16; static int bypass_state = 1; static int di_vscale_skip_enable; /*1:enable bypass pre,ei only; * 2:debug force bypass pre,ei for post */ static int by_pass_pre; static int bypass_post; static bool post_wr_en; static unsigned int post_wr_support; static int bypass_post_state; /* 0, use di_wr_buf still; * 1, call pre_de_done_buf_clear to clear di_wr_buf; * 2, do nothing */ static int use_2_interlace_buff; /* prog_proc_config, * bit[2:1]: when two field buffers are used, * 0 use vpp for blending , * 1 use post_di module for blending * 2 debug mode, bob with top field * 3 debug mode, bot with bot field * bit[0]: * 0 "prog vdin" use two field buffers, * 1 "prog vdin" use single frame buffer * bit[4]: * 0 "prog frame from decoder/vdin" use two field buffers, * 1 use single frame buffer * bit[5]: * when two field buffers are used for decoder (bit[4] is 0): * 1,handle prog frame as two interlace frames * bit[6]:(bit[4] is 0,bit[5] is 0,use_2_interlace_buff is 0): 0, * process progress frame as field,blend by post; * 1, process progress frame as field,process by normal di */ /* * for source include both progressive and interlace pictures, * always use post_di module for blending */ static int frame_count; static int debug_blend_mode = -1; static unsigned int pldn_dly1 = 1; static int di_vscale_skip_count; static int di_vscale_skip_count_real; static int vpp_3d_mode; static bool det3d_en; static int post_hold_line = 8; /*2019-01-10: from VLSI feijun from 17 to 8*/ static int post_urgent = 1; static unsigned int di_printk_flag; /*pre process speed debug */ static int pre_process_time; static unsigned int force_recovery = 1; static unsigned int force_recovery_count; /* 0: not support nr10bit, 1: support nr10bit */ static unsigned int nr10bit_support; static unsigned int nr_done_check_cnt = 5; /************For Write register**********************/ static unsigned int di_stop_reg_flag; static unsigned int num_di_stop_reg_addr = 4; static unsigned int di_stop_reg_addr[4] = {0}; static unsigned int di_dbg_mask; static bool full_422_pack; static bool cma_print; static bool pulldown_enable = true; static unsigned int pldn_dly = 1; /* destroy unnecessary frames before display */ static unsigned int hold_video; static DEFINE_SPINLOCK(plist_lock); static di_dev_t *de_devp; static dev_t di_devno; static struct class *di_clsp; static const char version_s[] = "2019-06-20a: afbc switch from vpp"; /* add avoid vframe put/get error */ static int di_blocking; /* * bit[2]: enable bypass all when skip * bit[1:0]: enable bypass post when skip */ #ifdef RUN_DI_PROCESS_IN_IRQ /* * di_process() run in irq, * di_reg_process(), di_unreg_process() run in kernel thread * di_reg_process_irq(), di_unreg_process_irq() run in irq * di_vf_put(), di_vf_peek(), di_vf_get() run in irq * di_receiver_event_fun() run in task or irq */ /* * important: * to set input2pre, VFRAME_EVENT_PROVIDER_VFRAME_READY of * vdin should be sent in irq */ static int input2pre; /*false:process progress by field; * true: process progress by frame with 2 interlace buffer */ static int input2pre_buf_miss_count; static int input2pre_proc_miss_count; static int input2pre_throw_count; static int input2pre_miss_policy; /* 0, do not force pre_de_busy to 0, use di_wr_buf after de_irq happen; * 1, force pre_de_busy to 0 and call pre_de_done_buf_clear to clear di_wr_buf */ #endif /*false:process progress by field; * bit0: process progress by frame with 2 interlace buffer * bit1: temp add debug for 3d process FA,1:bit0 force to 1; */ #define is_handle_prog_frame_as_interlace(vframe) \ (((prog_proc_config & 0x30) == 0x20) && \ ((vframe->type & VIDTYPE_VIU_422) == 0)) static int disp_frame_count; /* static int start_frame_hold_count = 0; */ static int video_peek_cnt; static unsigned long reg_unreg_timeout_cnt; #ifdef DET3D static unsigned int det3d_mode; static void set3d_view(enum tvin_trans_fmt trans_fmt, struct vframe_s *vf); #endif int get_current_vscale_skip_count(struct vframe_s *vf); static void di_pq_parm_destroy(struct di_pq_parm_s *pq_ptr); static struct di_pq_parm_s *di_pq_parm_create(struct am_pq_parm_s *); static uint init_flag;/*flag for di buferr*/ static unsigned int reg_flag;/*flag for vframe reg/unreg*/ static unsigned int unreg_cnt;/*cnt for vframe unreg*/ static unsigned int reg_cnt;/*cnt for vframe reg*/ static unsigned char active_flag; static unsigned char recovery_flag; static unsigned int recovery_log_reason; static unsigned int recovery_log_queue_idx; static struct di_buf_s *recovery_log_di_buf; /* bit 0: * 0, keep 3 buffers in pre_ready_list for checking; * 1, keep 4 buffers in pre_ready_list for checking; */ static int di_receiver_event_fun(int type, void *data, void *arg); static void di_uninit_buf(unsigned int disable_mirror); static void log_buffer_state(unsigned char *tag); /* static void put_get_disp_buf(void); */ static unsigned int isbypass_flag = true; static unsigned int needbypass_flag = true; static const struct vframe_receiver_op_s di_vf_receiver = { .event_cb = di_receiver_event_fun }; static struct vframe_receiver_s di_vf_recv; static vframe_t *di_vf_peek(void *arg); static vframe_t *di_vf_get(void *arg); static void di_vf_put(vframe_t *vf, void *arg); static int di_event_cb(int type, void *data, void *private_data); static int di_vf_states(struct vframe_states *states, void *arg); static void di_process(void); static void di_reg_process(void); static void di_reg_process_irq(void); static void di_unreg_process(void); static void di_unreg_process_irq(void); static struct queue_s *get_queue_by_idx(int idx); static void dump_state(void); static void recycle_keep_buffer(void); static const struct vframe_operations_s deinterlace_vf_provider = { .peek = di_vf_peek, .get = di_vf_get, .put = di_vf_put, .event_cb = di_event_cb, .vf_states = di_vf_states, }; static struct vframe_provider_s di_vf_prov; static int di_sema_init_flag; static struct semaphore di_sema; static struct tasklet_struct di_pre_tasklet; extern unsigned long long notrace sched_clock(void); void trigger_pre_di_process(unsigned char idx) { if (di_sema_init_flag == 0) return; if (!active_flag) return; log_buffer_state((idx == 'i') ? "irq" : ((idx == 'p') ? "put" : ((idx == 'r') ? "rdy" : "oth"))); /* tasklet_hi_schedule(&di_pre_tasklet); */ tasklet_schedule(&di_pre_tasklet); de_devp->jiffy = jiffies_64; /* trigger di_reg_process and di_unreg_process */ if ((idx != 'p') && (idx != 'i')) up(&di_sema); } #define DI_PRE_INTERVAL (HZ / 100) /* * progressive frame process type config: * 0, process by field; * 1, process by frame (only valid for vdin source whose * width/height does not change) */ static vframe_t *vframe_in[MAX_IN_BUF_NUM]; static vframe_t vframe_in_dup[MAX_IN_BUF_NUM]; static vframe_t vframe_local[MAX_LOCAL_BUF_NUM * 2]; static vframe_t vframe_post[MAX_POST_BUF_NUM]; static struct di_buf_s *cur_post_ready_di_buf; static struct di_buf_s di_buf_local[MAX_LOCAL_BUF_NUM * 2]; static struct di_buf_s di_buf_in[MAX_IN_BUF_NUM]; static struct di_buf_s di_buf_post[MAX_POST_BUF_NUM]; unsigned int is_need_stop_reg(unsigned int addr) { int idx = 0; if (di_stop_reg_flag) { for (idx = 0; idx < num_di_stop_reg_addr; idx++) { if (addr == di_stop_reg_addr[idx]) { pr_dbg("stop write addr: %x\n", addr); return 1; } } } return 0; } void DI_Wr(unsigned int addr, unsigned int val) { if (is_need_stop_reg(addr)) return; Wr(addr, val); } void DI_Wr_reg_bits(unsigned int adr, unsigned int val, unsigned int start, unsigned int len) { if (is_need_stop_reg(adr)) return; Wr_reg_bits(adr, val, start, len); } void DI_VSYNC_WR_MPEG_REG(unsigned int addr, unsigned int val) { if (is_need_stop_reg(addr)) return; if (post_wr_en && post_wr_support) DI_Wr(addr, val); else VSYNC_WR_TABLE_REG(VIDEO_PARTITION_TABLE, addr, val); } void DI_VSYNC_WR_MPEG_REG_BITS(unsigned int addr, unsigned int val, unsigned int start, unsigned int len) { if (is_need_stop_reg(addr)) return; if (post_wr_en && post_wr_support) DI_Wr_reg_bits(addr, val, start, len); else VSYNC_WR_TABLE_REG_BITS(VIDEO_PARTITION_TABLE, addr, val, start, len); } #if 0 unsigned int DI_POST_REG_RD(unsigned int addr) { if (IS_ERR_OR_NULL(de_devp)) return 0; if (de_devp->flags & DI_SUSPEND_FLAG) { pr_err("[DI] REG 0x%x access prohibited.\n", addr); return 0; } return VSYNC_RD_TABLE_REG(VIDEO_PARTITION_TABLE, addr); } EXPORT_SYMBOL(DI_POST_REG_RD); int DI_POST_WR_REG_BITS(u32 adr, u32 val, u32 start, u32 len) { if (IS_ERR_OR_NULL(de_devp)) return 0; if (de_devp->flags & DI_SUSPEND_FLAG) { pr_err("[DI] REG 0x%x access prohibited.\n", adr); return -1; } return VSYNC_WR_TABLE_REG_BITS(VIDEO_PARTITION_TABLE, adr, val, start, len); } EXPORT_SYMBOL(DI_POST_WR_REG_BITS); #else static unsigned int lDI_POST_REG_RD(unsigned int addr) { if (IS_ERR_OR_NULL(de_devp)) return 0; if (de_devp->flags & DI_SUSPEND_FLAG) { pr_err("[DI] REG 0x%x access prohibited.\n", addr); return 0; } return VSYNC_RD_TABLE_REG(VIDEO_PARTITION_TABLE, addr); } static int lDI_POST_WR_REG_BITS(u32 adr, u32 val, u32 start, u32 len) { if (IS_ERR_OR_NULL(de_devp)) return 0; if (de_devp->flags & DI_SUSPEND_FLAG) { pr_err("[DI] REG 0x%x access prohibited.\n", adr); return -1; } return VSYNC_WR_TABLE_REG_BITS(VIDEO_PARTITION_TABLE, adr, val, start, len); } static const struct di_ext_ops di_ext = { .di_post_reg_rd = lDI_POST_REG_RD, .di_post_wr_reg_bits = lDI_POST_WR_REG_BITS, .post_update_mc = di_patch_post_update_mc, }; #endif /**********************************/ /***************************** * di attr management : * enable * mode * reg ******************************/ /*config attr*/ static ssize_t show_config(struct device *dev, struct device_attribute *attr, char *buf) { int pos = 0; return pos; } static ssize_t store_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static int run_flag = DI_RUN_FLAG_RUN; static int pre_run_flag = DI_RUN_FLAG_RUN; static int dump_state_flag; /*2018-08-17 add debugfs*/ /*add debugfs: get dump_state parameter*/ struct di_pre_stru_s *get_di_pre_stru(void) { return &di_pre_stru; } struct di_post_stru_s *get_di_post_stru(void) { return &di_post_stru; } struct di_dev_s *get_di_de_devp(void) { return de_devp; } const char *get_di_version_s(void) { return version_s; } int get_di_di_blocking(void) { return di_blocking; } int get_di_video_peek_cnt(void) { return video_peek_cnt; } unsigned long get_di_reg_unreg_timeout_cnt(void) { return reg_unreg_timeout_cnt; } uint get_di_init_flag(void) { return init_flag; } unsigned char get_di_recovery_flag(void) { return recovery_flag; } unsigned int get_di_recovery_log_reason(void) { return recovery_log_reason; } unsigned int get_di_recovery_log_queue_idx(void) { return recovery_log_queue_idx; } struct di_buf_s *get_di_recovery_log_di_buf(void) { return recovery_log_di_buf; } struct vframe_s **get_di_vframe_in(void) { return &vframe_in[0]; } int get_di_dump_state_flag(void) { return dump_state_flag; } /******************************************** * function: for releas mirror mem * need call this after unreg di ********************************************/ void di_trig_free_mirror_mem(void) { if (dil_get_diff_ver_flag()) return; if (atomic_read(&di_flag_unreg) && de_devp->flag_cma != 2 && de_devp->flag_cma != 0 && active_flag) { atomic_set(&di_trig_free_mem, 1); up(&di_sema); di_pr_info("%s\n", __func__); } if (atomic_read(&di_flag_unreg)) { diwr_set_power_control_pst(0); di_pr_info("disable pst pd\n"); } } EXPORT_SYMBOL(di_trig_free_mirror_mem); u32 di_api_get_instance_id(void) { u32 ret = 0; if (dil_get_diff_ver_flag()) return ret; if (de_devp) ret = de_devp->instance_id; return ret; } EXPORT_SYMBOL(di_api_get_instance_id); static bool di_free_mem_pre(void) { if (!atomic_read(&di_trig_free_mem)) return false; if (di_pre_stru.cma_alloc_done) return false; if (!di_post_stru.keep_buf || !atomic_read(&di_flag_unreg)) { atomic_set(&di_trig_free_mem, 0); return false; } /*free mirror memory*/ di_post_stru.keep_buf = NULL; return true; } /*--------------------------*/ static void parse_param_di(char *buf_orig, char **parm) { char *ps, *token; unsigned int n = 0; char delim1[3] = " "; char delim2[2] = "\n"; ps = buf_orig; strcat(delim1, delim2); while (1) { token = strsep(&ps, delim1); if (token == NULL) break; if (*token == '\0') continue; parm[n++] = token; } } static ssize_t store_dbg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { u32 val; char *buf_orig, *parm[8] = {NULL}; buf_orig = kstrdup(buf, GFP_KERNEL); parse_param_di(buf_orig, (char **)&parm); if (strncmp(buf, "buf", 3) == 0) { struct di_buf_s *di_buf_tmp = 0; if (kstrtoul(buf + 3, 16, (unsigned long *)&di_buf_tmp)) { kfree(buf_orig); return count; } dump_di_buf(di_buf_tmp); } else if (strncmp(buf, "vframe", 6) == 0) { vframe_t *vf = 0; if (kstrtoul(buf + 6, 16, (unsigned long *)&vf)) { kfree(buf_orig); return count; } dump_vframe(vf); } else if (strncmp(buf, "pool", 4) == 0) { unsigned long idx = 0; if (kstrtoul(buf + 4, 10, &idx)) { kfree(buf_orig); return count; } dump_pool(get_queue_by_idx(idx)); } else if (strncmp(buf, "state", 4) == 0) { dump_state(); pr_info("add new debugfs: cat /sys/kernel/debug/di/state\n"); } else if (strncmp(buf, "prog_proc_config", 16) == 0) { if (buf[16] == '1') prog_proc_config = 1; else prog_proc_config = 0; } else if (strncmp(buf, "init_flag", 9) == 0) { if (buf[9] == '1') init_flag = 1; else init_flag = 0; } else if (strncmp(buf, "run", 3) == 0) { /* timestamp_pcrscr_enable(1); */ run_flag = DI_RUN_FLAG_RUN; } else if (strncmp(buf, "pause", 5) == 0) { /* timestamp_pcrscr_enable(0); */ run_flag = DI_RUN_FLAG_PAUSE; } else if (strncmp(buf, "step", 4) == 0) { run_flag = DI_RUN_FLAG_STEP; } else if (strncmp(buf, "prun", 4) == 0) { pre_run_flag = DI_RUN_FLAG_RUN; } else if (strncmp(buf, "ppause", 6) == 0) { pre_run_flag = DI_RUN_FLAG_PAUSE; } else if (strncmp(buf, "pstep", 5) == 0) { pre_run_flag = DI_RUN_FLAG_STEP; } else if (strncmp(buf, "dumpreg", 7) == 0) { if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) { dump_di_reg_g12(); dump_afbcd_reg(); } else pr_info("add new debugfs: cat /sys/kernel/debug/di/dumpreg\n"); } else if (strncmp(buf, "dumpafbc", 8) == 0) { dump_afbcd_reg(); } else if (strncmp(buf, "dumpmif", 7) == 0) { dump_mif_size_state(&di_pre_stru, &di_post_stru); } else if (strncmp(buf, "dumppostmif", 11) == 0) { dump_post_mif_reg(); } else if (strncmp(buf, "recycle_buf", 11) == 0) { recycle_keep_buffer(); } else if (strncmp(buf, "free_mirror", 11) == 0) { di_trig_free_mirror_mem(); } else if (strncmp(buf, "recycle_post", 12) == 0) { if (di_vf_peek(NULL)) di_vf_put(di_vf_get(NULL), NULL); } else if (strncmp(buf, "mem_map", 7) == 0) { dump_buf_addr(di_buf_local, MAX_LOCAL_BUF_NUM * 2); } else if (strncmp(buf, "afbc_on", 7) == 0) { if (kstrtoint(parm[1], 10, &val) < 0) { kfree(buf_orig); return count; } if (!val) afbc_sw(false); afbc_disable_flag = val > 0 ? 0:1; pr_info("afbc_disable_flag:%d\n", afbc_disable_flag); } else if (strncmp(buf, "reqafbc", 7) == 0) { val = di_request_afbc(true); di_pre_stru.wait_afbc = false; pr_info("request_afbc(%d)\n", val); } else if (strncmp(buf, "rlsafbc", 7) == 0) { val = di_request_afbc(false); di_pre_stru.wait_afbc = false; pr_info("rlease_afbc(%d)\n", val); } else { pr_info("DI no support cmd %s\n", buf); pr_info("supported cmd list:\n"); pr_info("\t vframe\n"); pr_info("\t state\n"); pr_info("\t prog_proc_config 0/1\n"); pr_info("\t init_flag 0/1\n"); pr_info("\t run\n"); pr_info("\t pause\n"); pr_info("\t step\n"); pr_info("\t prun\n"); pr_info("\t ppause\n"); pr_info("\t pstep\n"); pr_info("\t dumpreg\n"); pr_info("\t dumpmif\n"); pr_info("\t dumppostmif\n"); pr_info("\t recycle_buf\n"); pr_info("\t recycle_post\n"); pr_info("\t mem_map\n"); pr_info("\t reqafbc\n"); pr_info("\t rlsafbc\n"); pr_info("\n trigger val\n"); } kfree(buf_orig); return count; } static int di_read_canvas_reverse(char *str) { unsigned char *ptr = str; pr_dbg("%s: bootargs is %s.\n", __func__, str); if (strstr(ptr, "1")) { invert_top_bot |= 0x1; overturn = true; } else { invert_top_bot &= (~0x1); overturn = false; } return 0; } __setup("video_reverse=", di_read_canvas_reverse); static unsigned char *di_log_buf; static unsigned int di_log_wr_pos; static unsigned int di_log_rd_pos; static unsigned int di_log_buf_size; static unsigned int di_printk_flag; static unsigned int buf_state_log_start; /* set to 1 by condition of "post_ready count < buf_state_log_threshold", * reset to 0 by set buf_state_log_threshold as 0 */ static DEFINE_SPINLOCK(di_print_lock); #define PRINT_TEMP_BUF_SIZE 128 int di_print_buf(char *buf, int len) { unsigned long flags; int pos; int di_log_rd_pos_; if (di_log_buf_size == 0) return 0; spin_lock_irqsave(&di_print_lock, flags); di_log_rd_pos_ = di_log_rd_pos; if (di_log_wr_pos >= di_log_rd_pos) di_log_rd_pos_ += di_log_buf_size; for (pos = 0; pos < len && di_log_wr_pos < (di_log_rd_pos_ - 1); pos++, di_log_wr_pos++) { if (di_log_wr_pos >= di_log_buf_size) di_log_buf[di_log_wr_pos - di_log_buf_size] = buf[pos]; else di_log_buf[di_log_wr_pos] = buf[pos]; } if (di_log_wr_pos >= di_log_buf_size) di_log_wr_pos -= di_log_buf_size; spin_unlock_irqrestore(&di_print_lock, flags); return pos; } static u64 cur_to_msecs(void) { u64 cur = sched_clock(); do_div(cur, NSEC_PER_MSEC); return cur; } /* static int log_seq = 0; */ int di_print(const char *fmt, ...) { va_list args; int avail = PRINT_TEMP_BUF_SIZE; char buf[PRINT_TEMP_BUF_SIZE]; int pos, len = 0; if (di_printk_flag & 1) { if (di_log_flag & DI_LOG_PRECISE_TIMESTAMP) pr_dbg("%llums:", cur_to_msecs()); va_start(args, fmt); vprintk(fmt, args); va_end(args); return 0; } if (di_log_buf_size == 0) return 0; /* len += snprintf(buf+len, avail-len, "%d:",log_seq++); */ if (di_log_flag & DI_LOG_TIMESTAMP) len += snprintf(buf + len, avail - len, "%u:", jiffies_to_msecs(jiffies_64)); va_start(args, fmt); len += vsnprintf(buf + len, avail - len, fmt, args); va_end(args); if ((avail - len) <= 0) buf[PRINT_TEMP_BUF_SIZE - 1] = '\0'; pos = di_print_buf(buf, len); /* pr_dbg("di_print:%d %d\n", di_log_wr_pos, di_log_rd_pos); */ return pos; } static ssize_t read_log(char *buf) { unsigned long flags; ssize_t read_size = 0; if (di_log_buf_size == 0) return 0; /* pr_dbg("show_log:%d %d\n", di_log_wr_pos, di_log_rd_pos); */ spin_lock_irqsave(&di_print_lock, flags); if (di_log_rd_pos < di_log_wr_pos) read_size = di_log_wr_pos - di_log_rd_pos; else if (di_log_rd_pos > di_log_wr_pos) read_size = di_log_buf_size - di_log_rd_pos; if (read_size > PAGE_SIZE) read_size = PAGE_SIZE; if (read_size > 0) memcpy(buf, di_log_buf + di_log_rd_pos, read_size); di_log_rd_pos += read_size; if (di_log_rd_pos >= di_log_buf_size) di_log_rd_pos = 0; spin_unlock_irqrestore(&di_print_lock, flags); return read_size; } static ssize_t show_log(struct device *dev, struct device_attribute *attr, char *buf) { return read_log(buf); } static ssize_t store_log(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long flags, tmp; if (strncmp(buf, "bufsize", 7) == 0) { if (kstrtoul(buf + 7, 10, &tmp)) return count; spin_lock_irqsave(&di_print_lock, flags); kfree(di_log_buf); di_log_buf = NULL; di_log_buf_size = 0; di_log_rd_pos = 0; di_log_wr_pos = 0; if (tmp >= 1024) { di_log_buf_size = 0; di_log_rd_pos = 0; di_log_wr_pos = 0; di_log_buf = kmalloc(tmp, GFP_KERNEL); if (di_log_buf) di_log_buf_size = tmp; } spin_unlock_irqrestore(&di_print_lock, flags); pr_dbg("di_store:set bufsize tmp %lu %u\n", tmp, di_log_buf_size); } else if (strncmp(buf, "printk", 6) == 0) { if (kstrtoul(buf + 6, 10, &tmp)) return count; di_printk_flag = tmp; } else { di_print("%s", buf); } return 16; } static ssize_t show_vframe_status(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; struct vframe_states states; ret = vf_get_states_by_name(VFM_NAME, &states); if (ret == 0) { ret += sprintf(buf + ret, "vframe_pool_size=%d\n", states.vf_pool_size); ret += sprintf(buf + ret, "vframe buf_free_num=%d\n", states.buf_free_num); ret += sprintf(buf + ret, "vframe buf_recycle_num=%d\n", states.buf_recycle_num); ret += sprintf(buf + ret, "vframe buf_avail_num=%d\n", states.buf_avail_num); } else { ret = 0; ret += sprintf(buf + ret, "vframe no states\n"); } return ret; } static ssize_t show_tvp_region(struct device *dev, struct device_attribute *attr, char *buff) { ssize_t len = 0; len = sprintf(buff, "segment DI:%lx - %lx (size:0x%x)\n", de_devp->mem_start, de_devp->mem_start + de_devp->mem_size - 1, de_devp->mem_size); return len; } static ssize_t store_dump_mem(struct device *dev, struct device_attribute *attr, const char *buf, size_t len); static DEVICE_ATTR(config, 0640, show_config, store_config); static DEVICE_ATTR(debug, 0200, NULL, store_dbg); static DEVICE_ATTR(dump_pic, 0200, NULL, store_dump_mem); static DEVICE_ATTR(log, 0640, show_log, store_log); static DEVICE_ATTR(provider_vframe_status, 0444, show_vframe_status, NULL); static DEVICE_ATTR(tvp_region, 0444, show_tvp_region, NULL); /*************************** * di buffer management ***************************/ static char *vframe_type_name[] = { "", "di_buf_in", "di_buf_loc", "di_buf_post" }; static unsigned int default_width = 1920; static unsigned int default_height = 1080; /* * all buffers are in * 1) list of local_free_list,in_free_list,pre_ready_list,recycle_list * 2) di_pre_stru.di_inp_buf * 3) di_pre_stru.di_wr_buf * 4) cur_post_ready_di_buf * 5) (struct di_buf_s*)(vframe->private_data)->di_buf[] * * 6) post_free_list_head * 8) (struct di_buf_s*)(vframe->private_data) */ #define queue_t struct queue_s static queue_t queue[QUEUE_NUM]; static struct di_buf_pool_s di_buf_pool[VFRAME_TYPE_NUM]; static struct queue_s *get_queue_by_idx(int idx) { if (idx < QUEUE_NUM) return &(queue[idx]); else return NULL; } static void queue_init(int local_buffer_num) { int i, j; for (i = 0; i < QUEUE_NUM; i++) { queue_t *q = &queue[i]; for (j = 0; j < MAX_QUEUE_POOL_SIZE; j++) q->pool[j] = 0; q->in_idx = 0; q->out_idx = 0; q->num = 0; q->type = 0; if ((i == QUEUE_RECYCLE) || (i == QUEUE_DISPLAY) || (i == QUEUE_TMP) || (i == QUEUE_POST_DOING)) q->type = 1; if ((i == QUEUE_LOCAL_FREE) && use_2_interlace_buff) q->type = 2; } if (local_buffer_num > 0) { di_buf_pool[VFRAME_TYPE_IN - 1].di_buf_ptr = &di_buf_in[0]; di_buf_pool[VFRAME_TYPE_IN - 1].size = MAX_IN_BUF_NUM; di_buf_pool[VFRAME_TYPE_LOCAL-1].di_buf_ptr = &di_buf_local[0]; di_buf_pool[VFRAME_TYPE_LOCAL - 1].size = local_buffer_num; di_buf_pool[VFRAME_TYPE_POST - 1].di_buf_ptr = &di_buf_post[0]; di_buf_pool[VFRAME_TYPE_POST - 1].size = MAX_POST_BUF_NUM; } } struct di_buf_s *get_di_buf(int queue_idx, int *start_pos) { queue_t *q = &(queue[queue_idx]); int idx = 0; unsigned int pool_idx, di_buf_idx; struct di_buf_s *di_buf = NULL; int start_pos_init = *start_pos; if (di_log_flag & DI_LOG_QUEUE) di_print("%s:<%d:%d,%d,%d> %d\n", __func__, queue_idx, q->num, q->in_idx, q->out_idx, *start_pos); if (q->type == 0) { if ((*start_pos) < q->num) { idx = q->out_idx + (*start_pos); if (idx >= MAX_QUEUE_POOL_SIZE) idx -= MAX_QUEUE_POOL_SIZE; (*start_pos)++; } else { idx = MAX_QUEUE_POOL_SIZE; } } else if ((q->type == 1) || (q->type == 2)) { for (idx = (*start_pos); idx < MAX_QUEUE_POOL_SIZE; idx++) { if (q->pool[idx] != 0) { *start_pos = idx + 1; break; } } } if (idx < MAX_QUEUE_POOL_SIZE) { pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1; di_buf_idx = q->pool[idx] & 0xff; if (pool_idx < VFRAME_TYPE_NUM) { if (di_buf_idx < di_buf_pool[pool_idx].size) di_buf = &(di_buf_pool[pool_idx].di_buf_ptr[ di_buf_idx]); } } if ((di_buf) && ((((pool_idx + 1) << 8) | di_buf_idx) != ((di_buf->type << 8) | (di_buf->index)))) { pr_dbg("%s: Error (%x,%x)\n", __func__, (((pool_idx + 1) << 8) | di_buf_idx), ((di_buf->type << 8) | (di_buf->index))); if (recovery_flag == 0) { recovery_log_reason = 1; recovery_log_queue_idx = (start_pos_init<<8)|queue_idx; recovery_log_di_buf = di_buf; } recovery_flag++; di_buf = NULL; } if (di_log_flag & DI_LOG_QUEUE) { if (di_buf) di_print("%s: 0x%p(%d,%d)\n", __func__, di_buf, pool_idx, di_buf_idx); else di_print("%s: 0x%p\n", __func__, di_buf); } return di_buf; } static struct di_buf_s *get_di_buf_head(int queue_idx) { queue_t *q = &(queue[queue_idx]); int idx; unsigned int pool_idx, di_buf_idx; struct di_buf_s *di_buf = NULL; if (di_log_flag & DI_LOG_QUEUE) di_print("%s:<%d:%d,%d,%d>\n", __func__, queue_idx, q->num, q->in_idx, q->out_idx); if (q->num > 0) { if (q->type == 0) { idx = q->out_idx; } else { for (idx = 0; idx < MAX_QUEUE_POOL_SIZE; idx++) if (q->pool[idx] != 0) break; } if (idx < MAX_QUEUE_POOL_SIZE) { pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1; di_buf_idx = q->pool[idx] & 0xff; if (pool_idx < VFRAME_TYPE_NUM) { if (di_buf_idx < di_buf_pool[pool_idx].size) di_buf = &(di_buf_pool[pool_idx]. di_buf_ptr[di_buf_idx]); } } } if ((di_buf) && ((((pool_idx + 1) << 8) | di_buf_idx) != ((di_buf->type << 8) | (di_buf->index)))) { pr_dbg("%s: Error (%x,%x)\n", __func__, (((pool_idx + 1) << 8) | di_buf_idx), ((di_buf->type << 8) | (di_buf->index))); if (recovery_flag == 0) { recovery_log_reason = 2; recovery_log_queue_idx = queue_idx; recovery_log_di_buf = di_buf; } recovery_flag++; di_buf = NULL; } if (di_log_flag & DI_LOG_QUEUE) { if (di_buf) di_print("%s: 0x%p(%d,%d)\n", __func__, di_buf, pool_idx, di_buf_idx); else di_print("%s: 0x%p\n", __func__, di_buf); } return di_buf; } static void queue_out(struct di_buf_s *di_buf) { int i; queue_t *q; if (di_buf == NULL) { pr_dbg("%s:Error\n", __func__); if (recovery_flag == 0) recovery_log_reason = 3; recovery_flag++; return; } if (di_buf->queue_index >= 0 && di_buf->queue_index < QUEUE_NUM) { q = &(queue[di_buf->queue_index]); if (di_log_flag & DI_LOG_QUEUE) di_print("%s:<%d:%d,%d,%d> 0x%p\n", __func__, di_buf->queue_index, q->num, q->in_idx, q->out_idx, di_buf); if (q->num > 0) { if (q->type == 0) { if (q->pool[q->out_idx] == ((di_buf->type << 8) | (di_buf->index))) { q->num--; q->pool[q->out_idx] = 0; q->out_idx++; if (q->out_idx >= MAX_QUEUE_POOL_SIZE) q->out_idx = 0; di_buf->queue_index = -1; } else { pr_dbg( "%s: Error (%d, %x,%x)\n", __func__, di_buf->queue_index, q->pool[q->out_idx], ((di_buf->type << 8) | (di_buf->index))); if (recovery_flag == 0) { recovery_log_reason = 4; recovery_log_queue_idx = di_buf->queue_index; recovery_log_di_buf = di_buf; } recovery_flag++; } } else if (q->type == 1) { int pool_val = (di_buf->type << 8) | (di_buf->index); for (i = 0; i < MAX_QUEUE_POOL_SIZE; i++) { if (q->pool[i] == pool_val) { q->num--; q->pool[i] = 0; di_buf->queue_index = -1; break; } } if (i == MAX_QUEUE_POOL_SIZE) { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) { recovery_log_reason = 5; recovery_log_queue_idx = di_buf->queue_index; recovery_log_di_buf = di_buf; } recovery_flag++; } } else if (q->type == 2) { int pool_val = (di_buf->type << 8) | (di_buf->index); if ((di_buf->index < MAX_QUEUE_POOL_SIZE) && (q->pool[di_buf->index] == pool_val)) { q->num--; q->pool[di_buf->index] = 0; di_buf->queue_index = -1; } else { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) { recovery_log_reason = 5; recovery_log_queue_idx = di_buf->queue_index; recovery_log_di_buf = di_buf; } recovery_flag++; } } } } else { pr_dbg("%s: Error, queue_index %d is not right\n", __func__, di_buf->queue_index); if (recovery_flag == 0) { recovery_log_reason = 6; recovery_log_queue_idx = 0; recovery_log_di_buf = di_buf; } recovery_flag++; } if (di_log_flag & DI_LOG_QUEUE) di_print("%s done\n", __func__); } static void queue_in(struct di_buf_s *di_buf, int queue_idx) { queue_t *q = NULL; if (di_buf == NULL) { pr_dbg("%s:Error\n", __func__); if (recovery_flag == 0) { recovery_log_reason = 7; recovery_log_queue_idx = queue_idx; recovery_log_di_buf = di_buf; } recovery_flag++; return; } if (di_buf->queue_index != -1) { pr_dbg("%s:%s[%d] Error, queue_index(%d) is not -1\n", __func__, vframe_type_name[di_buf->type], di_buf->index, di_buf->queue_index); if (recovery_flag == 0) { recovery_log_reason = 8; recovery_log_queue_idx = queue_idx; recovery_log_di_buf = di_buf; } recovery_flag++; return; } q = &(queue[queue_idx]); if (di_log_flag & DI_LOG_QUEUE) di_print("%s:<%d:%d,%d,%d> 0x%p\n", __func__, queue_idx, q->num, q->in_idx, q->out_idx, di_buf); if (q->type == 0) { q->pool[q->in_idx] = (di_buf->type << 8) | (di_buf->index); di_buf->queue_index = queue_idx; q->in_idx++; if (q->in_idx >= MAX_QUEUE_POOL_SIZE) q->in_idx = 0; q->num++; } else if (q->type == 1) { int i; for (i = 0; i < MAX_QUEUE_POOL_SIZE; i++) { if (q->pool[i] == 0) { q->pool[i] = (di_buf->type<<8)|(di_buf->index); di_buf->queue_index = queue_idx; q->num++; break; } } if (i == MAX_QUEUE_POOL_SIZE) { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) { recovery_log_reason = 9; recovery_log_queue_idx = queue_idx; } recovery_flag++; } } else if (q->type == 2) { if ((di_buf->index < MAX_QUEUE_POOL_SIZE) && (q->pool[di_buf->index] == 0)) { q->pool[di_buf->index] = (di_buf->type << 8) | (di_buf->index); di_buf->queue_index = queue_idx; q->num++; } else { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) { recovery_log_reason = 9; recovery_log_queue_idx = queue_idx; } recovery_flag++; } } if (di_log_flag & DI_LOG_QUEUE) di_print("%s done\n", __func__); } static int list_count(int queue_idx) { return queue[queue_idx].num; } static bool queue_empty(int queue_idx) { bool ret = (queue[queue_idx].num == 0); return ret; } static bool is_in_queue(struct di_buf_s *di_buf, int queue_idx) { bool ret = 0; struct di_buf_s *p = NULL; int itmp; unsigned int overflow_cnt; overflow_cnt = 0; if ((di_buf == NULL) || (queue_idx < 0) || (queue_idx >= QUEUE_NUM)) { ret = 0; di_print("%s: not in queue:%d!!!\n", __func__, queue_idx); return ret; } queue_for_each_entry(p, ptmp, queue_idx, list) { if (p == di_buf) { ret = 1; break; } if (overflow_cnt++ > MAX_QUEUE_POOL_SIZE) { ret = 0; di_print("%s: overflow_cnt!!!\n", __func__); break; } } return ret; } //--------------------------- u8 *di_vmap(ulong addr, u32 size, bool *bflg) { u8 *vaddr = NULL; ulong phys = addr; u32 offset = phys & ~PAGE_MASK; u32 npages = PAGE_ALIGN(size) / PAGE_SIZE; struct page **pages = NULL; pgprot_t pgprot; int i; if (!PageHighMem(phys_to_page(phys))) return phys_to_virt(phys); if (offset) npages++; pages = vmalloc(sizeof(struct page *) * npages); if (!pages) return NULL; for (i = 0; i < npages; i++) { pages[i] = phys_to_page(phys); phys += PAGE_SIZE; } /*nocache*/ pgprot = pgprot_writecombine(PAGE_KERNEL); vaddr = vmap(pages, npages, VM_MAP, pgprot); if (!vaddr) { pr_err("the phy(%lx) vmaped fail, size: %d\n", addr - offset, npages << PAGE_SHIFT); vfree(pages); return NULL; } vfree(pages); // if (debug_mode & 0x20) { // di_print("[HIGH-MEM-MAP] %s, pa(%lx) to va(%p), size: %d\n", // __func__, addr, vaddr + offset, npages << PAGE_SHIFT); // } *bflg = true; return vaddr + offset; } void di_unmap_phyaddr(u8 *vaddr) { void *addr = (void *)(PAGE_MASK & (ulong)vaddr); vunmap(addr); } //------------------------- static ssize_t store_dump_mem(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { #ifdef CONFIG_AMLOGIC_ENABLE_MEDIA_FILE unsigned int n = 0, canvas_w = 0, canvas_h = 0; unsigned long nr_size = 0, dump_adr = 0; struct di_buf_s *di_buf = NULL; struct vframe_s *post_vf = NULL; char *buf_orig, *ps, *token; char *parm[3] = { NULL }; char delim1[3] = " "; char delim2[2] = "\n"; struct file *filp = NULL; loff_t pos = 0; void *buff = NULL; mm_segment_t old_fs; bool bflg_vmap = false; buf_orig = kstrdup(buf, GFP_KERNEL); ps = buf_orig; strcat(delim1, delim2); while (1) { token = strsep(&ps, delim1); if (token == NULL) break; if (*token == '\0') continue; parm[n++] = token; } if (strcmp(parm[0], "capture") == 0) di_buf = di_pre_stru.di_mem_buf_dup_p; else if (strcmp(parm[0], "capture_post") == 0) { if (di_vf_peek(NULL)) { post_vf = di_vf_get(NULL); if (!IS_ERR_OR_NULL(post_vf)) { di_buf = post_vf->private_data; di_vf_put(post_vf, NULL); } post_vf = NULL; } } else if (strcmp(parm[0], "capture_nrds") == 0) get_nr_ds_buf(&dump_adr, &nr_size); else { pr_err("wrong dump cmd\n"); return len; } if (nr_size == 0) { if (unlikely(di_buf == NULL)) return len; canvas_w = di_buf->canvas_width[NR_CANVAS]; canvas_h = di_buf->canvas_height; nr_size = canvas_w * canvas_h * 2; dump_adr = di_buf->nr_adr; } old_fs = get_fs(); set_fs(KERNEL_DS); /* pr_dbg("dump path =%s\n",dump_path); */ filp = filp_open(parm[1], O_RDWR | O_CREAT, 0666); if (IS_ERR(filp)) { pr_err("create %s error.\n", parm[1]); return len; } dump_state_flag = 1; if (de_devp->flags & DI_MAP_FLAG) { //buff = (void *)phys_to_virt(dump_adr); buff = di_vmap(dump_adr, nr_size, &bflg_vmap); if (buff == NULL) { pr_info("di_vap err\n"); filp_close(filp, NULL); return len; } } else { buff = ioremap(dump_adr, nr_size); } if (IS_ERR_OR_NULL(buff)) pr_err("%s: ioremap error.\n", __func__); vfs_write(filp, buff, nr_size, &pos); /* pr_dbg("di_chan2_buf_dup_p:\n nr:%u,mtn:%u,cnt:%u\n", * di_pre_stru.di_chan2_buf_dup_p->nr_adr, * di_pre_stru.di_chan2_buf_dup_p->mtn_adr, * di_pre_stru.di_chan2_buf_dup_p->cnt_adr); * pr_dbg("di_inp_buf:\n nr:%u,mtn:%u,cnt:%u\n", * di_pre_stru.di_inp_buf->nr_adr, * di_pre_stru.di_inp_buf->mtn_adr, * di_pre_stru.di_inp_buf->cnt_adr); * pr_dbg("di_wr_buf:\n nr:%u,mtn:%u,cnt:%u\n", * di_pre_stru.di_wr_buf->nr_adr, * di_pre_stru.di_wr_buf->mtn_adr, * di_pre_stru.di_wr_buf->cnt_adr); * pr_dbg("di_mem_buf_dup_p:\n nr:%u,mtn:%u,cnt:%u\n", * di_pre_stru.di_mem_buf_dup_p->nr_adr, * di_pre_stru.di_mem_buf_dup_p->mtn_adr, * di_pre_stru.di_mem_buf_dup_p->cnt_adr); * pr_dbg("di_mem_start=%u\n",di_mem_start); */ vfs_fsync(filp, 0); pr_info("write buffer 0x%lx to %s.\n", dump_adr, parm[1]); if (bflg_vmap) di_unmap_phyaddr(buff); if (!(de_devp->flags & DI_MAP_FLAG)) iounmap(buff); dump_state_flag = 0; filp_close(filp, NULL); set_fs(old_fs); #endif return len; } #define is_from_vdin(vframe) (vframe->type & VIDTYPE_VIU_422) static void recycle_vframe_type_pre(struct di_buf_s *di_buf); static void recycle_vframe_type_post(struct di_buf_s *di_buf); static void add_dummy_vframe_type_pre(struct di_buf_s *src_buf); #ifdef DI_BUFFER_DEBUG static void recycle_vframe_type_post_print(struct di_buf_s *di_buf, const char *func, const int line); #endif static void dis2_di(void) { ulong flags = 0, irq_flag2 = 0; init_flag = 0; di_lock_irqfiq_save(irq_flag2); /* vf_unreg_provider(&di_vf_prov); */ vf_light_unreg_provider(&di_vf_prov); di_unlock_irqfiq_restore(irq_flag2); reg_flag = 0; spin_lock_irqsave(&plist_lock, flags); di_lock_irqfiq_save(irq_flag2); if (di_pre_stru.di_inp_buf) { if (vframe_in[di_pre_stru.di_inp_buf->index]) { vf_put( vframe_in[di_pre_stru.di_inp_buf->index], VFM_NAME); vframe_in[di_pre_stru.di_inp_buf->index] = NULL; vf_notify_provider( VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); } di_pre_stru.di_inp_buf->invert_top_bot_flag = 0; queue_in(di_pre_stru.di_inp_buf, QUEUE_IN_FREE); di_pre_stru.di_inp_buf = NULL; } di_uninit_buf(0); if (get_blackout_policy()) { DI_Wr(DI_CLKG_CTRL, 0x2); #ifndef CONFIG_AMLOGIC_REMOVE_OLD if (is_meson_txlx_cpu() || is_meson_txhd_cpu()) { enable_di_post_mif(GATE_OFF); di_post_gate_control(false); di_top_gate_control(false, false); } #endif } if (post_wr_en && post_wr_support) diwr_set_power_control(0); di_unlock_irqfiq_restore(irq_flag2); spin_unlock_irqrestore(&plist_lock, flags); } static ssize_t store_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc = 0; char *parm[2] = { NULL }, *buf_orig; buf_orig = kstrdup(buf, GFP_KERNEL); parse_cmd_params(buf_orig, (char **)(&parm)); if (strncmp(buf, "disable", 7) == 0) { di_print("%s: disable\n", __func__); if (init_flag) { di_pre_stru.disable_req_flag = 1; trigger_pre_di_process(TRIGGER_PRE_BY_DEBUG_DISABLE); while (di_pre_stru.disable_req_flag) usleep_range(1000, 1001); } } else if (strncmp(buf, "dis2", 4) == 0) { dis2_di(); } else if (strcmp(parm[0], "hold_video") == 0) { pr_info("%s(%s %s)\n", __func__, parm[0], parm[1]); rc = kstrtouint(parm[1], 10, &hold_video); } kfree(buf_orig); return count; } static unsigned char is_progressive(vframe_t *vframe) { unsigned char ret = 0; ret = ((vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_PROGRESSIVE); return ret; } static unsigned char is_source_change(vframe_t *vframe) { #define VFRAME_FORMAT_MASK \ (VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_444 | \ VIDTYPE_MVC) if ((di_pre_stru.cur_width != vframe->width) || (di_pre_stru.cur_height != vframe->height) || (((di_pre_stru.cur_inp_type & VFRAME_FORMAT_MASK) != (vframe->type & VFRAME_FORMAT_MASK)) && (!is_handle_prog_frame_as_interlace(vframe))) || (di_pre_stru.cur_source_type != vframe->source_type)) { /* video format changed */ return 1; } else if ( ((di_pre_stru.cur_prog_flag != is_progressive(vframe)) && (!is_handle_prog_frame_as_interlace(vframe))) || ((di_pre_stru.cur_inp_type & VIDTYPE_VIU_FIELD) != (vframe->type & VIDTYPE_VIU_FIELD)) ) { /* just scan mode changed */ if (!di_pre_stru.force_interlace) pr_dbg("DI I<->P.\n"); return 2; } return 0; } /* * static unsigned char is_vframe_type_change(vframe_t* vframe) * { * if( * (di_pre_stru.cur_prog_flag!=is_progressive(vframe))|| * ((di_pre_stru.cur_inp_type&VFRAME_FORMAT_MASK)!= * (vframe->type&VFRAME_FORMAT_MASK)) * ) * return 1; * * return 0; * } */ static int trick_mode; unsigned char is_bypass(vframe_t *vf_in) { unsigned int vtype = 0; int ret = 0; static vframe_t vf_tmp; isbypass_flag = true; if (di_debug_flag & 0x10000) /* for debugging */ return (di_debug_flag >> 17) & 0x1; if (bypass_all) return 1; if (di_pre_stru.cur_prog_flag && ( (di_pre_stru.cur_width > 1920) || (di_pre_stru.cur_height > 1080) || (di_pre_stru.cur_inp_type & VIDTYPE_VIU_444)) ) return 1; if ((di_pre_stru.cur_width < 16) || (di_pre_stru.cur_height < 16)) return 1; if (di_pre_stru.cur_inp_type & VIDTYPE_MVC) return 1; if (di_pre_stru.cur_source_type == VFRAME_SOURCE_TYPE_PPMGR) return 1; if (bypass_trick_mode) { int trick_mode_fffb = 0; int trick_mode_i = 0; if (bypass_trick_mode&0x1) query_video_status(0, &trick_mode_fffb); if (bypass_trick_mode&0x2) query_video_status(1, &trick_mode_i); trick_mode = trick_mode_fffb | (trick_mode_i << 1); if (trick_mode) return 1; } if (bypass_3d && (di_pre_stru.source_trans_fmt != 0)) return 1; /*prot is conflict with di post*/ if (vf_in && vf_in->video_angle) return 1; if (vf_in && (vf_in->type & VIDTYPE_PIC)) return 1; #if 0 if (vf_in && (vf_in->type & VIDTYPE_COMPRESS)) return 1; #endif if ((di_vscale_skip_enable & 0x4) && vf_in && !post_wr_en) { //-------------------------------------- if (vf_in->type & VIDTYPE_COMPRESS) { vf_tmp.width = vf_in->compWidth; vf_tmp.height = vf_in->compHeight; if (vf_tmp.width > 1920 || vf_tmp.height > 1088) return 1; } //-------------------------------------- /*backup vtype,set type as progressive*/ vtype = vf_in->type; vf_in->type &= (~VIDTYPE_TYPEMASK); vf_in->type &= (~VIDTYPE_VIU_NV21); vf_in->type |= VIDTYPE_VIU_SINGLE_PLANE; vf_in->type |= VIDTYPE_VIU_FIELD; vf_in->type |= VIDTYPE_PRE_INTERLACE; vf_in->type |= VIDTYPE_VIU_422; ret = get_current_vscale_skip_count(vf_in); di_vscale_skip_count = (ret&0xff); vpp_3d_mode = ((ret>>8)&0xff); vf_in->type = vtype; if (di_vscale_skip_count > 0 || (vpp_3d_mode #ifdef DET3D && (!det3d_en) #endif ) ) return 1; } isbypass_flag = false; return 0; } static unsigned char is_bypass_post(void) { if (di_debug_flag & 0x40000) /* for debugging */ return (di_debug_flag >> 19) & 0x1; /*prot is conflict with di post*/ if (di_pre_stru.orientation) return 1; if (bypass_post) return 1; #ifdef DET3D if (di_pre_stru.vframe_interleave_flag != 0) return 1; #endif return 0; } #ifdef RUN_DI_PROCESS_IN_IRQ static unsigned char is_input2pre(void) { if (input2pre && di_pre_stru.cur_prog_flag && di_pre_stru.vdin_source && (bypass_state == 0)) return 1; return 0; } #endif #ifdef DI_USE_FIXED_CANVAS_IDX static int di_post_idx[2][6]; static int di_pre_idx[2][10]; #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC static unsigned int di_inp_idx[3]; #else static int di_wr_idx; #endif static int di_get_canvas(void) { unsigned int pre_num = 7, post_num = 6, i = 0; if (mcpre_en) { /* mem/chan2/nr/mtn/contrd/contrd2/ * contw/mcinfrd/mcinfow/mcvecw */ pre_num = 10; /* buf0/buf1/buf2/mtnp/mcvec */ post_num = 6; } if (canvas_pool_alloc_canvas_table("di_pre", &di_pre_idx[0][0], pre_num, CANVAS_MAP_TYPE_1)) { pr_err("%s allocate di pre canvas error.\n", __func__); return 1; } if (di_pre_rdma_enable) { if (canvas_pool_alloc_canvas_table("di_pre", &di_pre_idx[1][0], pre_num, CANVAS_MAP_TYPE_1)) { pr_err("%s allocate di pre canvas error.\n", __func__); return 1; } } else { for (i = 0; i < pre_num; i++) di_pre_idx[1][i] = di_pre_idx[0][i]; } if (canvas_pool_alloc_canvas_table("di_post", &di_post_idx[0][0], post_num, CANVAS_MAP_TYPE_1)) { pr_err("%s allocate di post canvas error.\n", __func__); return 1; } #ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA if (canvas_pool_alloc_canvas_table("di_post", &di_post_idx[1][0], post_num, CANVAS_MAP_TYPE_1)) { pr_err("%s allocate di post canvas error.\n", __func__); return 1; } #else for (i = 0; i < post_num; i++) di_post_idx[1][i] = di_post_idx[0][i]; #endif #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC if (canvas_pool_alloc_canvas_table("di_inp", &di_inp_idx[0], 3, CANVAS_MAP_TYPE_1)) { pr_err("%s allocat di inp canvas error.\n", __func__); return 1; } pr_info("DI: support multi decoding %u~%u~%u.\n", di_inp_idx[0], di_inp_idx[1], di_inp_idx[2]); #endif if (de_devp->post_wr_support == 0) return 0; #ifndef CONFIG_AMLOGIC_MEDIA_MULTI_DEC if (canvas_pool_alloc_canvas_table("di_wr", &di_wr_idx, 1, CANVAS_MAP_TYPE_1)) { pr_err("%s allocat di write back canvas error.\n", __func__); return 1; } pr_info("DI: support post write back %u.\n", di_wr_idx); #endif return 0; } static void config_canvas_idx(struct di_buf_s *di_buf, int nr_canvas_idx, int mtn_canvas_idx) { unsigned int height = 0; if (!di_buf) return; if (di_buf->canvas_config_flag == 1) { if (nr_canvas_idx >= 0) { /* linked two interlace buffer should double height*/ if (di_buf->di_wr_linked_buf) height = (di_buf->canvas_height << 1); else height = di_buf->canvas_height; di_buf->nr_canvas_idx = nr_canvas_idx; canvas_config(nr_canvas_idx, di_buf->nr_adr, di_buf->canvas_width[NR_CANVAS], height, 0, 0); } } else if (di_buf->canvas_config_flag == 2) { if (nr_canvas_idx >= 0) { di_buf->nr_canvas_idx = nr_canvas_idx; canvas_config(nr_canvas_idx, di_buf->nr_adr, di_buf->canvas_width[NR_CANVAS], di_buf->canvas_height, 0, 0); } if (mtn_canvas_idx >= 0) { di_buf->mtn_canvas_idx = mtn_canvas_idx; canvas_config(mtn_canvas_idx, di_buf->mtn_adr, di_buf->canvas_width[MTN_CANVAS], di_buf->canvas_height, 0, 0); } } if (nr_canvas_idx >= 0) { di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx; di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx; } } static void config_cnt_canvas_idx(struct di_buf_s *di_buf, unsigned int cnt_canvas_idx) { if (!di_buf) return; di_buf->cnt_canvas_idx = cnt_canvas_idx; canvas_config( cnt_canvas_idx, di_buf->cnt_adr, di_buf->canvas_width[MTN_CANVAS], di_buf->canvas_height, 0, 0); } static void config_mcinfo_canvas_idx(struct di_buf_s *di_buf, int mcinfo_canvas_idx) { if (!di_buf) return; di_buf->mcinfo_canvas_idx = mcinfo_canvas_idx; canvas_config( mcinfo_canvas_idx, di_buf->mcinfo_adr, di_buf->canvas_height, 2, 0, 0); } static void config_mcvec_canvas_idx(struct di_buf_s *di_buf, int mcvec_canvas_idx) { if (!di_buf) return; di_buf->mcvec_canvas_idx = mcvec_canvas_idx; canvas_config( mcvec_canvas_idx, di_buf->mcvec_adr, di_buf->canvas_width[MV_CANVAS], di_buf->canvas_height, 0, 0); } #else static void config_canvas(struct di_buf_s *di_buf) { unsigned int height = 0; if (!di_buf) return; if (di_buf->canvas_config_flag == 1) { /* linked two interlace buffer should double height*/ if (di_buf->di_wr_linked_buf) height = (di_buf->canvas_height << 1); else height = di_buf->canvas_height; canvas_config(di_buf->nr_canvas_idx, di_buf->nr_adr, di_buf->canvas_width[NR_CANVAS], height, 0, 0); di_buf->canvas_config_flag = 0; } else if (di_buf->canvas_config_flag == 2) { canvas_config(di_buf->nr_canvas_idx, di_buf->nr_adr, di_buf->canvas_width[MV_CANVAS], di_buf->canvas_height, 0, 0); canvas_config(di_buf->mtn_canvas_idx, di_buf->mtn_adr, di_buf->canvas_width[MTN_CANVAS], di_buf->canvas_height, 0, 0); di_buf->canvas_config_flag = 0; } } #endif #ifdef CONFIG_CMA /********************************************************** * ./include/linux/amlogic/media/codec_mm/codec_mm.h: * unsigned long codec_mm_alloc_for_dma(const char *owner, * int page_cnt, * int align2n, * int memflags); * int codec_mm_free_for_dma(const char *owner, * unsigned long phy_addr); * void *codec_mm_phys_to_virt(unsigned long phy_addr); ***********************************************************/ #define TVP_MEM_PAGES 0xffff /********************************************************** * alloc mm from codec mm * o: out: * return: * true: seccuss * false: failed ***********************************************************/ static bool mm_codec_alloc(const char *owner, size_t count, int cma_mode, struct di_mm_s *o) { int flags = 0; bool istvp = false; if (codec_mm_video_tvp_enabled()) { istvp = true; flags |= CODEC_MM_FLAGS_TVP; } else { flags |= CODEC_MM_FLAGS_RESERVED | CODEC_MM_FLAGS_CPU; } if (cma_mode == 4 && !istvp) flags = CODEC_MM_FLAGS_CMA_FIRST | CODEC_MM_FLAGS_CPU; o->addr = codec_mm_alloc_for_dma(owner, count, 0, flags); if (o->addr == 0) { /*failed*/ pr_err("di:%s: failed\n", __func__); return false; } if (istvp) o->ppage = (struct page *) TVP_MEM_PAGES; else o->ppage = codec_mm_phys_to_virt(o->addr); //pr_info("%s:page:0x%p,add:0x%lx\n", __func__, o->ppage, o->addr); return true; } /********************************************************** * ./include/linux/dma-contiguous.h: * struct page *dma_alloc_from_contiguous(struct device *dev, * size_t count, * unsigned int order); * bool dma_release_from_contiguous(struct device *dev, * struct page *pages, * int count); * ***********************************************************/ /********************************************************** * alloc mm by cma * o: out: * return: * true: seccuss * false: failed ***********************************************************/ static bool mm_cma_alloc(struct device *dev, size_t count, struct di_mm_s *o) { o->ppage = dma_alloc_from_contiguous(dev, count, 0, 0); if (o->ppage) { o->addr = page_to_phys(o->ppage); return true; } pr_err("di:%s: failed\n", __func__); return false; } bool di_mm_alloc(int cma_mode, size_t count, struct di_mm_s *o) { bool ret; if (cma_mode == 3 || cma_mode == 4) ret = mm_codec_alloc(DEVICE_NAME, count, cma_mode, o); else ret = mm_cma_alloc(&(de_devp->pdev->dev), count, o); return ret; } bool di_mm_release(int cma_mode, struct page *pages, int count, unsigned long addr) { bool ret = true; if (cma_mode == 3 || cma_mode == 4) codec_mm_free_for_dma(DEVICE_NAME, addr); else ret = dma_release_from_contiguous(&(de_devp->pdev->dev), pages, count); return ret; } /***********************************************************/ static unsigned int di_cma_alloc_total(struct di_dev_s *de_devp) { #if 0 de_devp->total_pages = dma_alloc_from_contiguous(&(de_devp->pdev->dev), de_devp->mem_size >> PAGE_SHIFT, 0); if (de_devp->total_pages) { de_devp->mem_start = page_to_phys(de_devp->total_pages); pr_dbg("%s:DI CMA allocate addr:0x%lx ok.\n", __func__, de_devp->mem_start); return 1; } pr_err("%s:xxxxxx DI CMA allocate fail.\n", __func__); if (de_devp->flag_cma != 0 && de_devp->nrds_enable) { nr_ds_buf_init(de_devp->flag_cma, 0, &(de_devp->pdev->dev)); } return 0; #else /*****************************************************/ struct di_mm_s omm; bool ret; ret = di_mm_alloc(de_devp->flag_cma, de_devp->mem_size >> PAGE_SHIFT, &omm); if (!ret) /*failed*/ return 0; de_devp->total_pages = omm.ppage; de_devp->mem_start = omm.addr; if (de_devp->flag_cma != 0 && de_devp->nrds_enable) { nr_ds_buf_init(de_devp->flag_cma, 0, &(de_devp->pdev->dev)); } return 1; #endif } static unsigned int di_cma_alloc(struct di_dev_s *devp) { unsigned int start_time, end_time, delta_time; struct di_buf_s *buf_p = NULL; int itmp, alloc_cnt = 0; u8 *tmp; bool aret; struct di_mm_s omm; start_time = jiffies_to_msecs(jiffies); queue_for_each_entry(buf_p, ptmp, QUEUE_LOCAL_FREE, list) { #if 0 if (buf_p->pages == NULL) { buf_p->pages = dma_alloc_from_contiguous(&(devp->pdev->dev), devp->buffer_size >> PAGE_SHIFT, 0); if (IS_ERR_OR_NULL(buf_p->pages)) { buf_p->pages = NULL; pr_err("xxxxxxxxx DI CMA allocate %d fail.\n", buf_p->index); return 0; } else { alloc_cnt++; if (cma_print) pr_info("DI CMA allocate buf[%d]page:0x%p\n", buf_p->index, buf_p->pages); } } else { if (cma_print) { pr_err("DI buf[%d] page:0x%p cma alloced skip\n", buf_p->index, buf_p->pages); } } buf_p->nr_adr = page_to_phys(buf_p->pages); #else if (buf_p->pages != NULL) { pr_err("di:err1:%s:buf[%d] page:0x%p alloced skip\n", __func__, buf_p->index, buf_p->pages); continue; } aret = di_mm_alloc(devp->flag_cma, devp->buffer_size >> PAGE_SHIFT, &omm); if (!aret) { buf_p->pages = NULL; pr_err("di:err2:%s: alloc failed %d fail.\n", __func__, buf_p->index); return 0; } buf_p->pages = omm.ppage; buf_p->nr_adr = omm.addr; alloc_cnt++; if (cma_print) pr_info("DI CMA allocate buf[%d]page:0x%p\n", buf_p->index, buf_p->pages); #endif if (cma_print) pr_info(" addr 0x%lx ok.\n", buf_p->nr_adr); if (di_pre_stru.buf_alloc_mode == 0) { buf_p->mtn_adr = buf_p->nr_adr + di_pre_stru.nr_size; buf_p->cnt_adr = buf_p->nr_adr + di_pre_stru.nr_size + di_pre_stru.mtn_size; if (mc_mem_alloc) { buf_p->mcvec_adr = buf_p->nr_adr + di_pre_stru.nr_size + di_pre_stru.mtn_size + di_pre_stru.count_size; buf_p->mcinfo_adr = buf_p->nr_adr + di_pre_stru.nr_size + di_pre_stru.mtn_size + di_pre_stru.count_size + di_pre_stru.mv_size; tmp = di_vmap(buf_p->mcinfo_adr, di_pre_stru.mcinfo_size, &buf_p->bflg_vmap); if (buf_p->bflg_vmap == true) buf_p->mcinfo_vaddr = (unsigned short *)tmp; else { buf_p->mcinfo_vaddr = NULL; pr_err("DI: %s vmap fail\n", __func__); } } } } if (post_wr_en && post_wr_support) { queue_for_each_entry(buf_p, ptmp, QUEUE_POST_FREE, list) { #if 0 if (buf_p->pages == NULL) { buf_p->pages = dma_alloc_from_contiguous( &(devp->pdev->dev), devp->post_buffer_size>>PAGE_SHIFT, 0); if (IS_ERR_OR_NULL(buf_p->pages)) { buf_p->pages = NULL; pr_err("xxxxxxxxx DI CMA allocate %d fail.\n", buf_p->index); return 0; } alloc_cnt++; if (cma_print) pr_info("DI CMA allocate buf[%d]page:0x%p\n", buf_p->index, buf_p->pages); } else { pr_err("DI buf[%d] page:0x%p cma alloced skip\n", buf_p->index, buf_p->pages); } buf_p->nr_adr = page_to_phys(buf_p->pages); #else if (buf_p->pages != NULL) { pr_err("di:err3:%s:buf[%d] page:0x%p skip\n", __func__, buf_p->index, buf_p->pages); continue; } aret = di_mm_alloc(devp->flag_cma, devp->post_buffer_size>>PAGE_SHIFT, &omm); if (!aret) { buf_p->pages = NULL; pr_err("di:err4:%s: alloc failed %d fail.\n", __func__, buf_p->index); return 0; } buf_p->pages = omm.ppage; buf_p->nr_adr = omm.addr; alloc_cnt++; if (cma_print) pr_info("di:%s:pbuf[%d]page:0x%p\n", __func__, buf_p->index, buf_p->pages); #endif if (cma_print) pr_info(" addr 0x%lx ok.\n", buf_p->nr_adr); } } if (de_devp->flag_cma != 0 && de_devp->nrds_enable) { nr_ds_buf_init(de_devp->flag_cma, 0, &(de_devp->pdev->dev)); } end_time = jiffies_to_msecs(jiffies); delta_time = end_time - start_time; pr_info("%s:alloc %u buffer use %u ms(%u~%u)\n", __func__, alloc_cnt, delta_time, start_time, end_time); return 1; } static void di_cma_release(struct di_dev_s *devp) { unsigned int i, ii, rels_cnt = 0, start_time, end_time, delta_time; struct di_buf_s *buf_p, *keep_buf; bool ret; keep_buf = di_post_stru.keep_buf; start_time = jiffies_to_msecs(jiffies); for (i = 0; (i < devp->buf_num_avail); i++) { buf_p = &(di_buf_local[i]); ii = USED_LOCAL_BUF_MAX; if (!IS_ERR_OR_NULL(keep_buf)) { for (ii = 0; ii < USED_LOCAL_BUF_MAX; ii++) { if (buf_p == keep_buf->di_buf_dup_p[ii]) { pr_dbg("%s skip buf[%d].\n\n", __func__, i); break; } } } if ((ii >= USED_LOCAL_BUF_MAX) && (buf_p->pages != NULL)) { if (buf_p->bflg_vmap == true) { di_unmap_phyaddr((u8 *)buf_p->mcinfo_vaddr); buf_p->bflg_vmap = false; } #if 0 if (dma_release_from_contiguous(&(devp->pdev->dev), buf_p->pages, devp->buffer_size >> PAGE_SHIFT)) { buf_p->pages = NULL; rels_cnt++; if (cma_print) pr_info( "DI CMA release buf[%d] ok.\n", i); } else { pr_err("DI CMA release buf[%d] fail.\n", i); } #else ret = di_mm_release(devp->flag_cma, buf_p->pages, devp->buffer_size >> PAGE_SHIFT, buf_p->nr_adr); if (ret) { buf_p->pages = NULL; rels_cnt++; if (cma_print) pr_info( "DI release buf[%d] ok.\n", i); } else { pr_err("di:err:%s:release buf[%d] fail.\n", __func__, i); } #endif } else { if (!IS_ERR_OR_NULL(buf_p->pages) && cma_print) { pr_info("DI buf[%d] page:0x%p no release.\n", buf_p->index, buf_p->pages); } } } if (post_wr_en && post_wr_support) { for (i = 0; i < di_post_stru.di_post_num; i++) { buf_p = &(di_buf_post[i]); #if 0 if (buf_p->pages != NULL) { if (dma_release_from_contiguous( &(devp->pdev->dev), buf_p->pages, devp->post_buffer_size>> PAGE_SHIFT)) { buf_p->pages = NULL; rels_cnt++; if (cma_print) pr_info( "DI CMA release post buf[%d] ok.\n", i); } else { pr_err("DI CMA release post buf[%d] fail.\n", i); } } #else if (buf_p->pages == NULL) { pr_err("di:err2:%s:post buf[%d] is null\n", __func__, i); continue; } ret = di_mm_release(devp->flag_cma, buf_p->pages, devp->post_buffer_size >> PAGE_SHIFT, buf_p->nr_adr); if (ret) { buf_p->pages = NULL; rels_cnt++; if (cma_print) pr_info( "DI release post buf[%d] ok.\n", i); } else { pr_err("di:err:%s:release post buf[%d] fail\n", __func__, i); } #endif } } if (de_devp->nrds_enable) { nr_ds_buf_uninit(de_devp->flag_cma, &(de_devp->pdev->dev)); } end_time = jiffies_to_msecs(jiffies); delta_time = end_time - start_time; pr_info("%s:release %u buffer use %u ms(%u~%u)\n", __func__, rels_cnt, delta_time, start_time, end_time); } #endif static void di_patch_mov_ini(void) { struct di_patch_mov_s *pmov; if (!de_devp) return; pmov = &de_devp->mov; if (!cpu_after_eq(MESON_CPU_MAJOR_ID_TL1)) { pmov->en_support = false; return; } pmov->en_support = true; pmov->mode = -1; pmov->nub = 3; pmov->reg_addr[0] = DI_NR_CTRL1; pmov->reg_addr[1] = DI_NR_CTRL2; pmov->reg_addr[2] = NR4_TOP_CTRL; } bool di_patch_mov_db(unsigned int addr, unsigned int val) { bool ret = false; int i; struct di_patch_mov_s *pmov; struct di_patch_mov_d_s *pmv; if (!de_devp) return ret; pmov = &de_devp->mov; if (!pmov->en_support || !pmov->nub) return ret; for (i = 0; i < pmov->nub; i++) { if (addr == pmov->reg_addr[i]) { pmv = &pmov->val_db[i]; pmv->val = val; pmv->en = true; pmv->mask = 0xffffffff; ret = true; } } return ret; } void di_patch_mov_setreg(unsigned int nub, unsigned int *preg) { struct di_patch_mov_s *pmov; unsigned int i; if (!de_devp) return; pmov = &de_devp->mov; if (!pmov->en_support) return; pmov->nub = nub; if (nub > DI_PATCH_MOV_MAX_NUB) { pr_error("err: %s:nub is overflow %d\n", __func__, nub); pmov->nub = DI_PATCH_MOV_MAX_NUB; } pmov->mode = -1; for (i = 0; i < pmov->nub; i++) { pmov->reg_addr[i] = preg[i]; di_pr_info("reg:0x%x\n", preg[i]); } } EXPORT_SYMBOL(di_patch_mov_setreg); /************************************************** * pdate: * value / mask * value / mask * need keep same order with di_patch_mov_setreg **************************************************/ bool di_api_mov_sel(unsigned int mode, unsigned int *pdate) { struct di_patch_mov_s *pmov; int i; struct di_patch_mov_d_s *pmv; bool ret = true; if (!de_devp) return false; pmov = &de_devp->mov; if (!pmov || !pmov->en_support || !init_flag) return false; switch (mode) { case 0:/*setting from db*/ pmov->mode = 0; pmov->update = 1; break; case 1:/*setting from pq*/ pmov->update = 0; for (i = 0; i < pmov->nub; i++) { pmv = &pmov->val_pq[i]; pmv->val = pdate[i * 2]; pmv->mask = pdate[i * 2 + 1]; pmv->en = true; } pmov->mode = 1; pmov->update = true; break; default: ret = false; break; } return ret; } EXPORT_SYMBOL(di_api_mov_sel); static void di_patch_mov_setting(void) { struct di_patch_mov_s *pmov; int i; struct di_patch_mov_d_s *pmv; unsigned int val; if (!de_devp) return; pmov = &de_devp->mov; if (!pmov || !pmov->en_support || pmov->mode < 0 || pmov->mode > 1 || !pmov->update) return; if (pmov->mode == 0) pmv = &pmov->val_db[0]; else pmv = &pmov->val_pq[0]; for (i = 0; i < pmov->nub; i++) { if (pmv->en) { if (pmv->mask != 0xffffffff) { val = ((RDMA_RD(pmov->reg_addr[i]) & (~(pmv->mask))) | (pmv->val & pmv->mask)); } else { val = pmv->val; } DI_Wr(pmov->reg_addr[i], val); } pmv++; } pmov->update = 0; } void di_set_comb_mode(unsigned int mode) { di_pre_stru.comb_mode = mode; } EXPORT_SYMBOL(di_set_comb_mode); static int di_init_buf(int width, int height, unsigned char prog_flag) { int i; int canvas_height = height + 8; struct page *tmp_page = NULL; unsigned int di_buf_size = 0, di_post_buf_size = 0, mtn_size = 0; unsigned int nr_size = 0, count_size = 0, mv_size = 0, mc_size = 0; unsigned int nr_width = width, mtn_width = width, mv_width = width; unsigned int nr_canvas_width = width, mtn_canvas_width = width; unsigned int mv_canvas_width = width, canvas_align_width = 32; unsigned long di_post_mem = 0, nrds_mem = 0; struct di_buf_s *keep_buf = di_post_stru.keep_buf; u8 *tmp; if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) canvas_align_width = 64; pr_info("di: %s -S\n", __func__); frame_count = 0; disp_frame_count = 0; cur_post_ready_di_buf = NULL; /* decoder'buffer had been release no need put */ for (i = 0; i < MAX_IN_BUF_NUM; i++) vframe_in[i] = NULL; memset(&di_pre_stru, 0, sizeof(di_pre_stru)); if (nr10bit_support) { if (full_422_pack) nr_width = (width * 5) / 4; else nr_width = (width * 3) / 2; } else { nr_width = width; } /* make sure canvas width must be divided by 256bit|32byte align */ nr_canvas_width = nr_width << 1; mtn_canvas_width = mtn_width >> 1; mv_canvas_width = (mv_width << 1) / 5; nr_canvas_width = roundup(nr_canvas_width, canvas_align_width); mtn_canvas_width = roundup(mtn_canvas_width, canvas_align_width); mv_canvas_width = roundup(mv_canvas_width, canvas_align_width); nr_width = nr_canvas_width >> 1; mtn_width = mtn_canvas_width << 1; mv_width = (mv_canvas_width * 5) >> 1; if (prog_flag) { di_pre_stru.prog_proc_type = 1; di_pre_stru.buf_alloc_mode = 1; di_buf_size = nr_width * canvas_height * 2; di_buf_size = roundup(di_buf_size, PAGE_SIZE); de_devp->buf_num_avail = de_devp->mem_size / di_buf_size; if (de_devp->buf_num_avail > (2 * MAX_LOCAL_BUF_NUM)) de_devp->buf_num_avail = 2 * MAX_LOCAL_BUF_NUM; } else { di_pre_stru.prog_proc_type = 0; di_pre_stru.buf_alloc_mode = 0; /*nr_size(bits)=w*active_h*8*2(yuv422) * mtn(bits)=w*active_h*4 * cont(bits)=w*active_h*4 mv(bits)=w*active_h/5*16 * mcinfo(bits)=active_h*16 */ nr_size = (nr_width * canvas_height)*8*2/16; mtn_size = (mtn_width * canvas_height)*4/16; count_size = (mtn_width * canvas_height)*4/16; mv_size = (mv_width * canvas_height)/5; mc_size = roundup(canvas_height >> 1, canvas_align_width) << 1; if (mc_mem_alloc) { di_buf_size = nr_size + mtn_size + count_size + mv_size + mc_size; } else { di_buf_size = nr_size + mtn_size + count_size; } di_buf_size = roundup(di_buf_size, PAGE_SIZE); de_devp->buf_num_avail = de_devp->mem_size / di_buf_size; if (post_wr_en && post_wr_support) { de_devp->buf_num_avail = (de_devp->mem_size + (nr_width*canvas_height<<2)) / (di_buf_size + (nr_width*canvas_height<<1)); } if (de_devp->buf_num_avail > MAX_LOCAL_BUF_NUM) de_devp->buf_num_avail = MAX_LOCAL_BUF_NUM; } de_devp->buffer_size = di_buf_size; di_pre_stru.nr_size = nr_size; di_pre_stru.count_size = count_size; di_pre_stru.mtn_size = mtn_size; di_pre_stru.mv_size = mv_size; di_pre_stru.mcinfo_size = mc_size; same_field_top_count = 0; same_field_bot_count = 0; queue_init(de_devp->buf_num_avail); for (i = 0; i < de_devp->buf_num_avail; i++) { struct di_buf_s *di_buf = &(di_buf_local[i]); int ii = USED_LOCAL_BUF_MAX; if (!IS_ERR_OR_NULL(keep_buf)) { for (ii = 0; ii < USED_LOCAL_BUF_MAX; ii++) { if (di_buf == keep_buf->di_buf_dup_p[ii]) { di_print("%s skip %d\n", __func__, i); break; } } } if (ii >= USED_LOCAL_BUF_MAX) { /* backup cma pages */ tmp_page = di_buf->pages; memset(di_buf, 0, sizeof(struct di_buf_s)); di_buf->pages = tmp_page; di_buf->type = VFRAME_TYPE_LOCAL; di_buf->pre_ref_count = 0; di_buf->post_ref_count = 0; di_buf->canvas_width[NR_CANVAS] = nr_canvas_width; di_buf->canvas_width[MTN_CANVAS] = mtn_canvas_width; di_buf->canvas_width[MV_CANVAS] = mv_canvas_width; if (prog_flag) { di_buf->canvas_height = canvas_height; //use reserved memory if (de_devp->flag_cma == 0) di_buf->nr_adr = de_devp->mem_start + di_buf_size * i; di_buf->canvas_config_flag = 1; } else { di_buf->canvas_height = (canvas_height>>1); di_buf->canvas_height = roundup(di_buf->canvas_height, canvas_align_width); //use reserved memory if (de_devp->flag_cma == 0) { di_buf->nr_adr = de_devp->mem_start + di_buf_size * i; di_buf->mtn_adr = de_devp->mem_start + di_buf_size * i + nr_size; di_buf->cnt_adr = de_devp->mem_start + di_buf_size * i + nr_size + mtn_size; if (mc_mem_alloc) { di_buf->mcvec_adr = de_devp->mem_start + di_buf_size * i + nr_size + mtn_size + count_size; di_buf->mcinfo_adr = de_devp->mem_start + di_buf_size * i + nr_size + mtn_size + count_size + mv_size; tmp = di_vmap( di_buf->mcinfo_adr, di_pre_stru. mcinfo_size, &di_buf->bflg_vmap); if (di_buf->bflg_vmap == true) di_buf->mcinfo_vaddr = (unsigned short *)tmp; else { di_buf->mcinfo_vaddr = NULL; pr_err("DI: %s vmap fail\n", __func__); } } } di_buf->canvas_config_flag = 2; } di_buf->index = i; di_buf->vframe = &(vframe_local[i]); di_buf->vframe->private_data = di_buf; di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx; di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx; di_buf->queue_index = -1; di_buf->invert_top_bot_flag = 0; queue_in(di_buf, QUEUE_LOCAL_FREE); } } #ifdef CONFIG_CMA if (de_devp->flag_cma == 1 || (de_devp->flag_cma == 4) || (de_devp->flag_cma == 3)) { pr_dbg("%s:cma alloc req time: %u ms\n", __func__, jiffies_to_msecs(jiffies)); atomic_set(&de_devp->mem_flag, 0); di_pre_stru.cma_alloc_req = 1; up(&di_sema); } #endif //use reserved memory if (de_devp->flag_cma == 0) di_post_mem = de_devp->mem_start + di_buf_size*de_devp->buf_num_avail; if (post_wr_en && post_wr_support) { di_post_buf_size = nr_width * canvas_height * 2; /* pre buffer must 2 more than post buffer */ if ((de_devp->buf_num_avail - 2) > MAX_POST_BUF_NUM) di_post_stru.di_post_num = MAX_POST_BUF_NUM; else di_post_stru.di_post_num = (de_devp->buf_num_avail - 2); pr_info("DI: di post buffer size %u byte.\n", di_post_buf_size); } else { di_post_stru.di_post_num = MAX_POST_BUF_NUM; di_post_buf_size = 0; } de_devp->post_buffer_size = di_post_buf_size; for (i = 0; i < MAX_IN_BUF_NUM; i++) { struct di_buf_s *di_buf = &(di_buf_in[i]); if (di_buf) { memset(di_buf, 0, sizeof(struct di_buf_s)); di_buf->type = VFRAME_TYPE_IN; di_buf->pre_ref_count = 0; di_buf->post_ref_count = 0; di_buf->vframe = &(vframe_in_dup[i]); di_buf->vframe->private_data = di_buf; di_buf->index = i; di_buf->queue_index = -1; di_buf->invert_top_bot_flag = 0; queue_in(di_buf, QUEUE_IN_FREE); } } for (i = 0; i < di_post_stru.di_post_num; i++) { struct di_buf_s *di_buf = &(di_buf_post[i]); if (di_buf) { if (di_buf != keep_buf) { memset(di_buf, 0, sizeof(struct di_buf_s)); di_buf->type = VFRAME_TYPE_POST; di_buf->index = i; di_buf->vframe = &(vframe_post[i]); di_buf->vframe->private_data = di_buf; di_buf->queue_index = -1; di_buf->invert_top_bot_flag = 0; if (post_wr_en && post_wr_support) { di_buf->canvas_width[NR_CANVAS] = (nr_width << 1); di_buf->canvas_height = canvas_height; di_buf->canvas_config_flag = 1; //use reserved memory if (de_devp->flag_cma == 0) di_buf->nr_adr = di_post_mem + di_post_buf_size*i; } queue_in(di_buf, QUEUE_POST_FREE); } } } if (de_devp->flag_cma == 0 && de_devp->nrds_enable) { nrds_mem = di_post_mem + di_post_stru.di_post_num * di_post_buf_size; nr_ds_buf_init(de_devp->flag_cma, nrds_mem, &(de_devp->pdev->dev)); } pr_info("di: %s -E\n", __func__); return 0; } static void keep_mirror_buffer(void) { struct di_buf_s *p = NULL; int i = 0, ii = 0, itmp; queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) { if (p->di_buf[0]->type != VFRAME_TYPE_IN && (p->process_fun_index != PROCESS_FUN_NULL) && (ii < USED_LOCAL_BUF_MAX) && (p->index == di_post_stru.cur_disp_index)) { di_post_stru.keep_buf = p; for (i = 0; i < USED_LOCAL_BUF_MAX; i++) { if (IS_ERR_OR_NULL(p->di_buf_dup_p[i])) continue; /* prepare for recycle * the keep buffer */ p->di_buf_dup_p[i]->pre_ref_count = 0; p->di_buf_dup_p[i]->post_ref_count = 0; if ((p->di_buf_dup_p[i]->queue_index >= 0) && (p->di_buf_dup_p[i]->queue_index < QUEUE_NUM)) { if (is_in_queue(p->di_buf_dup_p[i], p->di_buf_dup_p[i]->queue_index)) queue_out(p->di_buf_dup_p[i]); } ii++; if (p->di_buf_dup_p[i]->di_wr_linked_buf) p->di_buf_dup_p[i+1] = p->di_buf_dup_p[i]->di_wr_linked_buf; } queue_out(p); break; } } } static void di_uninit_buf(unsigned int disable_mirror) { struct di_buf_s *keep_buf = NULL; int i = 0; if (!queue_empty(QUEUE_DISPLAY) || disable_mirror) di_post_stru.keep_buf = NULL; if (disable_mirror != 1) keep_mirror_buffer(); if (!IS_ERR_OR_NULL(di_post_stru.keep_buf)) { keep_buf = di_post_stru.keep_buf; pr_dbg("%s keep cur di_buf %d (", __func__, keep_buf->index); for (i = 0; i < USED_LOCAL_BUF_MAX; i++) { /*coverity[var_deref_op] null pointer has been judged*/ if (!IS_ERR_OR_NULL(keep_buf->di_buf_dup_p[i])) pr_dbg("%d\t", keep_buf->di_buf_dup_p[i]->index); } pr_dbg(")\n"); } queue_init(0); /* decoder'buffer had been release no need put */ for (i = 0; i < MAX_IN_BUF_NUM; i++) vframe_in[i] = NULL; di_pre_stru.pre_de_process_done = 0; di_pre_stru.pre_de_process_flag = 0; if (post_wr_en && post_wr_support) { di_post_stru.cur_post_buf = NULL; di_post_stru.post_de_busy = 0; di_post_stru.de_post_process_done = 0; di_post_stru.post_wr_cnt = 0; } if (de_devp->flag_cma == 0 && de_devp->nrds_enable) { nr_ds_buf_uninit(de_devp->flag_cma, &(de_devp->pdev->dev)); } } static void log_buffer_state(unsigned char *tag) { if (di_log_flag & DI_LOG_BUFFER_STATE) { struct di_buf_s *p = NULL;/* , *ptmp; */ int itmp; int in_free = 0; int local_free = 0; int pre_ready = 0; int post_free = 0; int post_ready = 0; int post_ready_ext = 0; int display = 0; int display_ext = 0; int recycle = 0; int di_inp = 0; int di_wr = 0; ulong irq_flag2 = 0; di_lock_irqfiq_save(irq_flag2); in_free = list_count(QUEUE_IN_FREE); local_free = list_count(QUEUE_LOCAL_FREE); pre_ready = list_count(QUEUE_PRE_READY); post_free = list_count(QUEUE_POST_FREE); post_ready = list_count(QUEUE_POST_READY); queue_for_each_entry(p, ptmp, QUEUE_POST_READY, list) { if (p->di_buf[0]) post_ready_ext++; if (p->di_buf[1]) post_ready_ext++; } queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) { display++; if (p->di_buf[0]) display_ext++; if (p->di_buf[1]) display_ext++; } recycle = list_count(QUEUE_RECYCLE); if (di_pre_stru.di_inp_buf) di_inp++; if (di_pre_stru.di_wr_buf) di_wr++; if (buf_state_log_threshold == 0) buf_state_log_start = 0; else if (post_ready < buf_state_log_threshold) buf_state_log_start = 1; if (buf_state_log_start) { di_print( "[%s]i i_f %d/%d, l_f %d/%d, pre_r %d, post_f %d/%d,", tag, in_free, MAX_IN_BUF_NUM, local_free, de_devp->buf_num_avail, pre_ready, post_free, MAX_POST_BUF_NUM); di_print( "post_r (%d:%d), disp (%d:%d),rec %d, di_i %d, di_w %d\n", post_ready, post_ready_ext, display, display_ext, recycle, di_inp, di_wr ); } di_unlock_irqfiq_restore(irq_flag2); } } static void dump_state(void) { struct di_buf_s *p = NULL, *keep_buf; int itmp, i; dump_state_flag = 1; pr_info("version %s, init_flag %d, is_bypass %d\n", version_s, init_flag, is_bypass(NULL)); pr_info("isbypass_flag %d, needbypass_flag %d\n", isbypass_flag, needbypass_flag); pr_info("di_pre_stru.bypass_flag=%d\n", di_pre_stru.bypass_flag); pr_info("afbcd support %d\n", afbc_is_supported()); pr_info("recovery_flag = %d, recovery_log_reason=%d, di_blocking=%d", recovery_flag, recovery_log_reason, di_blocking); pr_info("recovery_log_queue_idx=%d, recovery_log_di_buf=0x%p\n", recovery_log_queue_idx, recovery_log_di_buf); pr_info("buffer_size=%d, mem_flag=%d, cma_flag=%d\n", de_devp->buffer_size, atomic_read(&de_devp->mem_flag), de_devp->flag_cma); keep_buf = di_post_stru.keep_buf; pr_info("used_post_buf_index %d(0x%p),", IS_ERR_OR_NULL(keep_buf) ? -1 : keep_buf->index, keep_buf); if (!IS_ERR_OR_NULL(keep_buf)) { pr_info("used_local_buf_index:\n"); for (i = 0; i < USED_LOCAL_BUF_MAX; i++) { p = keep_buf->di_buf_dup_p[i]; pr_info("%d(0x%p) ", IS_ERR_OR_NULL(p) ? -1 : p->index, p); } } pr_info("\nin_free_list (max %d):\n", MAX_IN_BUF_NUM); queue_for_each_entry(p, ptmp, QUEUE_IN_FREE, list) { pr_info("index %2d, 0x%p, type %d\n", p->index, p, p->type); } pr_info("local_free_list (max %d):\n", de_devp->buf_num_avail); queue_for_each_entry(p, ptmp, QUEUE_LOCAL_FREE, list) { pr_info("index %2d, 0x%p, type %d\n", p->index, p, p->type); } pr_info("post_doing_list:\n"); queue_for_each_entry(p, ptmp, QUEUE_POST_DOING, list) { print_di_buf(p, 2); } pr_info("pre_ready_list:\n"); queue_for_each_entry(p, ptmp, QUEUE_PRE_READY, list) { print_di_buf(p, 2); } pr_info("post_free_list (max %d):\n", di_post_stru.di_post_num); queue_for_each_entry(p, ptmp, QUEUE_POST_FREE, list) { pr_info("index %2d, 0x%p, type %d, vframetype 0x%x\n", p->index, p, p->type, p->vframe->type); } pr_info("post_ready_list:\n"); queue_for_each_entry(p, ptmp, QUEUE_POST_READY, list) { print_di_buf(p, 2); print_di_buf(p->di_buf[0], 1); print_di_buf(p->di_buf[1], 1); } pr_info("display_list:\n"); queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) { print_di_buf(p, 2); print_di_buf(p->di_buf[0], 1); print_di_buf(p->di_buf[1], 1); } pr_info("recycle_list:\n"); queue_for_each_entry(p, ptmp, QUEUE_RECYCLE, list) { pr_info( "index %d, 0x%p, type %d, vframetype 0x%x pre_ref_count %d post_ref_count %d\n", p->index, p, p->type, p->vframe->type, p->pre_ref_count, p->post_ref_count); if (p->di_wr_linked_buf) { pr_info( "linked index %2d, 0x%p, type %d pre_ref_count %d post_ref_count %d\n", p->di_wr_linked_buf->index, p->di_wr_linked_buf, p->di_wr_linked_buf->type, p->di_wr_linked_buf->pre_ref_count, p->di_wr_linked_buf->post_ref_count); } } if (di_pre_stru.di_inp_buf) { pr_info("di_inp_buf:index %d, 0x%p, type %d\n", di_pre_stru.di_inp_buf->index, di_pre_stru.di_inp_buf, di_pre_stru.di_inp_buf->type); } else { pr_info("di_inp_buf: NULL\n"); } if (di_pre_stru.di_wr_buf) { pr_info("di_wr_buf:index %d, 0x%p, type %d\n", di_pre_stru.di_wr_buf->index, di_pre_stru.di_wr_buf, di_pre_stru.di_wr_buf->type); } else { pr_info("di_wr_buf: NULL\n"); } dump_di_pre_stru(&di_pre_stru); dump_di_post_stru(&di_post_stru); pr_info("vframe_in[]:"); for (i = 0; i < MAX_IN_BUF_NUM; i++) pr_info("0x%p ", vframe_in[i]); pr_info("\n"); pr_info("vf_peek()=>0x%p, video_peek_cnt = %d\n", vf_peek(VFM_NAME), video_peek_cnt); pr_info("reg_unreg_timerout = %lu\n", reg_unreg_timeout_cnt); dump_state_flag = 0; } static unsigned char check_di_buf(struct di_buf_s *di_buf, int reason) { int error = 0; if (di_buf == NULL) { pr_dbg("%s: Error %d, di_buf is NULL\n", __func__, reason); return 1; } if (di_buf->type == VFRAME_TYPE_IN) { if (di_buf->vframe != &vframe_in_dup[di_buf->index]) error = 1; } else if (di_buf->type == VFRAME_TYPE_LOCAL) { if (di_buf->vframe != &vframe_local[di_buf->index]) error = 1; } else if (di_buf->type == VFRAME_TYPE_POST) { if (di_buf->vframe != &vframe_post[di_buf->index]) error = 1; } else { error = 1; } if (error) { pr_dbg( "%s: Error %d, di_buf wrong\n", __func__, reason); if (recovery_flag == 0) recovery_log_reason = reason; recovery_flag++; dump_di_buf(di_buf); return 1; } return 0; } /* * di pre process */ static void config_di_mcinford_mif(struct DI_MC_MIF_s *di_mcinford_mif, struct di_buf_s *di_buf) { if (di_buf) { di_mcinford_mif->size_x = (di_buf->vframe->height + 2) / 4 - 1; di_mcinford_mif->size_y = 1; di_mcinford_mif->canvas_num = di_buf->mcinfo_canvas_idx; } } static void config_di_pre_mc_mif(struct DI_MC_MIF_s *di_mcinfo_mif, struct DI_MC_MIF_s *di_mcvec_mif, struct di_buf_s *di_buf) { unsigned int pre_size_w = 0, pre_size_h = 0; if (di_buf) { pre_size_w = di_buf->vframe->width; pre_size_h = (di_buf->vframe->height + 1) / 2; di_mcinfo_mif->size_x = (pre_size_h + 1) / 2 - 1; di_mcinfo_mif->size_y = 1; di_mcinfo_mif->canvas_num = di_buf->mcinfo_canvas_idx; di_mcvec_mif->size_x = (pre_size_w + 4) / 5 - 1; di_mcvec_mif->size_y = pre_size_h - 1; di_mcvec_mif->canvas_num = di_buf->mcvec_canvas_idx; } } static void config_di_cnt_mif(struct DI_SIM_MIF_s *di_cnt_mif, struct di_buf_s *di_buf) { if (di_buf) { di_cnt_mif->start_x = 0; di_cnt_mif->end_x = di_buf->vframe->width - 1; di_cnt_mif->start_y = 0; di_cnt_mif->end_y = di_buf->vframe->height / 2 - 1; di_cnt_mif->canvas_num = di_buf->cnt_canvas_idx; } } static void config_di_wr_mif(struct DI_SIM_MIF_s *di_nrwr_mif, struct DI_SIM_MIF_s *di_mtnwr_mif, struct di_buf_s *di_buf) { vframe_t *vf = di_buf->vframe; di_nrwr_mif->canvas_num = di_buf->nr_canvas_idx; di_nrwr_mif->start_x = 0; di_nrwr_mif->end_x = vf->width - 1; di_nrwr_mif->start_y = 0; if (di_buf->vframe->bitdepth & BITDEPTH_Y10) di_nrwr_mif->bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; else di_nrwr_mif->bit_mode = 0; if (di_pre_stru.prog_proc_type == 0) di_nrwr_mif->end_y = vf->height / 2 - 1; else di_nrwr_mif->end_y = vf->height - 1; if (di_pre_stru.prog_proc_type == 0) { di_mtnwr_mif->start_x = 0; di_mtnwr_mif->end_x = vf->width - 1; di_mtnwr_mif->start_y = 0; di_mtnwr_mif->end_y = vf->height / 2 - 1; di_mtnwr_mif->canvas_num = di_buf->mtn_canvas_idx; } } static void config_di_mif(struct DI_MIF_s *di_mif, struct di_buf_s *di_buf) { if (di_buf == NULL) return; di_mif->canvas0_addr0 = di_buf->vframe->canvas0Addr & 0xff; di_mif->canvas0_addr1 = (di_buf->vframe->canvas0Addr >> 8) & 0xff; di_mif->canvas0_addr2 = (di_buf->vframe->canvas0Addr >> 16) & 0xff; di_mif->nocompress = (di_buf->vframe->type & VIDTYPE_COMPRESS)?0:1; if (di_buf->vframe->bitdepth & BITDEPTH_Y10) { if (di_buf->vframe->type & VIDTYPE_VIU_444) di_mif->bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2; else if (di_buf->vframe->type & VIDTYPE_VIU_422) di_mif->bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; } else { di_mif->bit_mode = 0; } if (di_buf->vframe->type & VIDTYPE_VIU_422) { /* from vdin or local vframe */ if ((!is_progressive(di_buf->vframe)) || (di_pre_stru.prog_proc_type) ) { di_mif->video_mode = 0; di_mif->set_separate_en = 0; di_mif->src_field_mode = 0; di_mif->output_field_num = 0; di_mif->luma_x_start0 = 0; di_mif->luma_x_end0 = di_buf->vframe->width - 1; di_mif->luma_y_start0 = 0; if (di_pre_stru.prog_proc_type) di_mif->luma_y_end0 = di_buf->vframe->height - 1; else di_mif->luma_y_end0 = di_buf->vframe->height / 2 - 1; di_mif->chroma_x_start0 = 0; di_mif->chroma_x_end0 = 0; di_mif->chroma_y_start0 = 0; di_mif->chroma_y_end0 = 0; di_mif->canvas0_addr0 = di_buf->vframe->canvas0Addr & 0xff; di_mif->canvas0_addr1 = (di_buf->vframe->canvas0Addr >> 8) & 0xff; di_mif->canvas0_addr2 = (di_buf->vframe->canvas0Addr >> 16) & 0xff; } } else { if (di_buf->vframe->type & VIDTYPE_VIU_444) di_mif->video_mode = 1; else di_mif->video_mode = 0; if (di_buf->vframe->type & VIDTYPE_VIU_NV21) di_mif->set_separate_en = 2; else di_mif->set_separate_en = 1; if (is_progressive(di_buf->vframe) && (di_pre_stru.prog_proc_type)) { di_mif->src_field_mode = 0; di_mif->output_field_num = 0; /* top */ di_mif->luma_x_start0 = 0; di_mif->luma_x_end0 = di_buf->vframe->width - 1; di_mif->luma_y_start0 = 0; di_mif->luma_y_end0 = di_buf->vframe->height - 1; di_mif->chroma_x_start0 = 0; di_mif->chroma_x_end0 = di_buf->vframe->width / 2 - 1; di_mif->chroma_y_start0 = 0; di_mif->chroma_y_end0 = di_buf->vframe->height / 2 - 1; } else { if (di_pre_stru.cur_inp_type & VIDTYPE_INTERLACE) di_mif->src_prog = 0; else di_mif->src_prog = force_prog?1:0; di_mif->src_field_mode = 1; if ( (di_buf->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP) { di_mif->output_field_num = 0; /* top */ di_mif->luma_x_start0 = 0; di_mif->luma_x_end0 = di_buf->vframe->width - 1; di_mif->luma_y_start0 = 0; di_mif->luma_y_end0 = di_buf->vframe->height - 2; di_mif->chroma_x_start0 = 0; di_mif->chroma_x_end0 = di_buf->vframe->width / 2 - 1; di_mif->chroma_y_start0 = 0; di_mif->chroma_y_end0 = di_buf->vframe->height / 2 - (di_mif->src_prog?1:2); } else { di_mif->output_field_num = 1; /* bottom */ di_mif->luma_x_start0 = 0; di_mif->luma_x_end0 = di_buf->vframe->width - 1; di_mif->luma_y_start0 = 1; di_mif->luma_y_end0 = di_buf->vframe->height - 1; di_mif->chroma_x_start0 = 0; di_mif->chroma_x_end0 = di_buf->vframe->width / 2 - 1; di_mif->chroma_y_start0 = (di_mif->src_prog?0:1); di_mif->chroma_y_end0 = di_buf->vframe->height / 2 - 1; } } } } static void di_pre_size_change(unsigned short width, unsigned short height, unsigned short vf_type); #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC static void pre_inp_canvas_config(struct vframe_s *vf); #endif static void pre_de_process(void) { ulong irq_flag2 = 0; unsigned short pre_width = 0, pre_height = 0; unsigned char chan2_field_num = 1; int canvases_idex = di_pre_stru.field_count_for_cont % 2; unsigned short cur_inp_field_type = VIDTYPE_TYPEMASK; unsigned short int_mask = 0x7f; di_pre_stru.pre_de_process_flag = 1; di_pre_stru.pre_de_busy_timer_count = 0; ddbg_mod_save(eDI_DBG_MOD_PRE_SETB, 0, di_pre_stru.in_seq);/*dbg*/ #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC pre_inp_canvas_config(di_pre_stru.di_inp_buf->vframe); #endif config_di_mif(&di_pre_stru.di_inp_mif, di_pre_stru.di_inp_buf); /* pr_dbg("set_separate_en=%d vframe->type %d\n", * di_pre_stru.di_inp_mif.set_separate_en, * di_pre_stru.di_inp_buf->vframe->type); */ #ifdef DI_USE_FIXED_CANVAS_IDX if (di_pre_stru.di_mem_buf_dup_p != NULL && di_pre_stru.di_mem_buf_dup_p != di_pre_stru.di_inp_buf) { config_canvas_idx(di_pre_stru.di_mem_buf_dup_p, di_pre_idx[canvases_idex][0], -1); config_cnt_canvas_idx(di_pre_stru.di_mem_buf_dup_p, di_pre_idx[canvases_idex][1]); } else { config_cnt_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][1]); config_di_cnt_mif(&di_pre_stru.di_contp2rd_mif, di_pre_stru.di_wr_buf); } if (di_pre_stru.di_chan2_buf_dup_p != NULL) { config_canvas_idx(di_pre_stru.di_chan2_buf_dup_p, di_pre_idx[canvases_idex][2], -1); config_cnt_canvas_idx(di_pre_stru.di_chan2_buf_dup_p, di_pre_idx[canvases_idex][3]); } else { config_cnt_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][3]); } config_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][4], di_pre_idx[canvases_idex][5]); config_cnt_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][6]); if (mcpre_en) { if (di_pre_stru.di_chan2_buf_dup_p != NULL) config_mcinfo_canvas_idx(di_pre_stru.di_chan2_buf_dup_p, di_pre_idx[canvases_idex][7]); else config_mcinfo_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][7]); config_mcinfo_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][8]); config_mcvec_canvas_idx(di_pre_stru.di_wr_buf, di_pre_idx[canvases_idex][9]); } #endif config_di_mif(&di_pre_stru.di_mem_mif, di_pre_stru.di_mem_buf_dup_p); if (di_pre_stru.di_chan2_buf_dup_p == NULL) { config_di_mif(&di_pre_stru.di_chan2_mif, di_pre_stru.di_inp_buf); } else config_di_mif(&di_pre_stru.di_chan2_mif, di_pre_stru.di_chan2_buf_dup_p); config_di_wr_mif(&di_pre_stru.di_nrwr_mif, &di_pre_stru.di_mtnwr_mif, di_pre_stru.di_wr_buf); if (di_pre_stru.di_chan2_buf_dup_p) config_di_cnt_mif(&di_pre_stru.di_contprd_mif, di_pre_stru.di_chan2_buf_dup_p); else config_di_cnt_mif(&di_pre_stru.di_contprd_mif, di_pre_stru.di_wr_buf); config_di_cnt_mif(&di_pre_stru.di_contwr_mif, di_pre_stru.di_wr_buf); if (mcpre_en) { if (di_pre_stru.di_chan2_buf_dup_p) config_di_mcinford_mif(&di_pre_stru.di_mcinford_mif, di_pre_stru.di_chan2_buf_dup_p); else config_di_mcinford_mif(&di_pre_stru.di_mcinford_mif, di_pre_stru.di_wr_buf); config_di_pre_mc_mif(&di_pre_stru.di_mcinfowr_mif, &di_pre_stru.di_mcvecwr_mif, di_pre_stru.di_wr_buf); } if ((di_pre_stru.di_chan2_buf_dup_p) && ((di_pre_stru.di_chan2_buf_dup_p->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP)) chan2_field_num = 0; pre_width = di_pre_stru.di_nrwr_mif.end_x + 1; pre_height = di_pre_stru.di_nrwr_mif.end_y + 1; if (di_pre_stru.input_size_change_flag) { cur_inp_field_type = (di_pre_stru.di_inp_buf->vframe->type & VIDTYPE_TYPEMASK); cur_inp_field_type = di_pre_stru.cur_prog_flag ? VIDTYPE_PROGRESSIVE : cur_inp_field_type; di_pre_size_change(pre_width, pre_height, cur_inp_field_type); di_pre_stru.input_size_change_flag = false; } di_patch_mov_setting(); if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) { if (de_devp->nrds_enable) { nr_ds_mif_config(); nr_ds_hw_ctrl(true); int_mask = 0x3f; } else { nr_ds_hw_ctrl(false); } } #ifdef CONFIG_AMLOGIC_MEDIA_TVIN /*patch for SECAM signal format from vlsi-feijun for all IC*/ di_nr_opl()->secam_cfr_adjust(di_pre_stru.di_inp_buf->vframe->sig_fmt, di_pre_stru.di_inp_buf->vframe->type); #endif /* set interrupt mask for pre module. * we need to only leave one mask open * to prevent multiple entry for de_irq */ enable_di_pre_aml(&di_pre_stru.di_inp_mif, &di_pre_stru.di_mem_mif, &di_pre_stru.di_chan2_mif, &di_pre_stru.di_nrwr_mif, &di_pre_stru.di_mtnwr_mif, &di_pre_stru.di_contp2rd_mif, &di_pre_stru.di_contprd_mif, &di_pre_stru.di_contwr_mif, di_pre_stru.madi_enable, chan2_field_num, di_pre_stru.vdin2nr); enable_afbc_input(di_pre_stru.di_inp_buf->vframe); if (mcpre_en) { if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) enable_mc_di_pre_g12( &di_pre_stru.di_mcinford_mif, &di_pre_stru.di_mcinfowr_mif, &di_pre_stru.di_mcvecwr_mif, di_pre_stru.mcdi_enable); else enable_mc_di_pre( &di_pre_stru.di_mcinford_mif, &di_pre_stru.di_mcinfowr_mif, &di_pre_stru.di_mcvecwr_mif, di_pre_stru.mcdi_enable); } di_pre_stru.field_count_for_cont++; if (di_pre_stru.field_count_for_cont >= 5) DI_Wr_reg_bits(DI_MTN_CTRL, 0, 30, 1); di_txl_patch_prog(di_pre_stru.cur_prog_flag, di_pre_stru.field_count_for_cont, mcpre_en); #ifdef SUPPORT_MPEG_TO_VDIN if (mpeg2vdin_flag) { struct vdin_arg_s vdin_arg; struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops(); vdin_arg.cmd = VDIN_CMD_FORCE_GO_FIELD; if (vdin_ops->tvin_vdin_func) vdin_ops->tvin_vdin_func(0, &vdin_arg); } #endif /* must make sure follow part issue without interrupts, * otherwise may cause watch dog reboot */ di_lock_irqfiq_save(irq_flag2); if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) pre_frame_reset_g12(di_pre_stru.madi_enable, di_pre_stru.mcdi_enable); else pre_frame_reset(); /* enable mc pre mif*/ enable_di_pre_mif(true, mcpre_en); di_unlock_irqfiq_restore(irq_flag2); /*reinit pre busy flag*/ di_pre_stru.pre_de_busy_timer_count = 0; di_pre_stru.pre_de_busy = 1; #ifdef SUPPORT_MPEG_TO_VDIN if (mpeg2vdin_flag) RDMA_WR_BITS(DI_PRE_CTRL, 1, 13, 1); #endif di_pre_stru.irq_time[0] = cur_to_msecs(); di_pre_stru.irq_time[1] = cur_to_msecs(); ddbg_mod_save(eDI_DBG_MOD_PRE_SETE, 0, di_pre_stru.in_seq);/*dbg*/ #ifdef CONFIG_AMLOGIC_MEDIA_RDMA if (di_pre_rdma_enable & 0x2) rdma_config(de_devp->rdma_handle, RDMA_TRIGGER_MANUAL); else if (di_pre_rdma_enable & 1) rdma_config(de_devp->rdma_handle, RDMA_DEINT_IRQ); #endif di_pre_stru.pre_de_process_flag = 0; } static void pre_de_done_buf_clear(void) { struct di_buf_s *wr_buf = NULL; if (di_pre_stru.di_wr_buf) { wr_buf = di_pre_stru.di_wr_buf; if ((di_pre_stru.prog_proc_type == 2) && wr_buf->di_wr_linked_buf) { wr_buf->di_wr_linked_buf->pre_ref_count = 0; wr_buf->di_wr_linked_buf->post_ref_count = 0; queue_in(wr_buf->di_wr_linked_buf, QUEUE_RECYCLE); wr_buf->di_wr_linked_buf = NULL; } wr_buf->pre_ref_count = 0; wr_buf->post_ref_count = 0; queue_in(wr_buf, QUEUE_RECYCLE); di_pre_stru.di_wr_buf = NULL; } if (di_pre_stru.di_inp_buf) { if (di_pre_stru.di_mem_buf_dup_p == di_pre_stru.di_inp_buf) di_pre_stru.di_mem_buf_dup_p = NULL; queue_in(di_pre_stru.di_inp_buf, QUEUE_RECYCLE); di_pre_stru.di_inp_buf = NULL; } } static void top_bot_config(struct di_buf_s *di_buf) { vframe_t *vframe = di_buf->vframe; if (((invert_top_bot & 0x1) != 0) && (!is_progressive(vframe))) { if (di_buf->invert_top_bot_flag == 0) { if ( (vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP) { vframe->type &= (~VIDTYPE_TYPEMASK); vframe->type |= VIDTYPE_INTERLACE_BOTTOM; } else { vframe->type &= (~VIDTYPE_TYPEMASK); vframe->type |= VIDTYPE_INTERLACE_TOP; } di_buf->invert_top_bot_flag = 1; } } } static void pre_de_done_buf_config(void) { ulong irq_flag2 = 0; struct di_buf_s *post_wr_buf = NULL; unsigned int glb_frame_mot_num = 0; unsigned int glb_field_mot_num = 0; unsigned int pull_down_info = 0; ddbg_mod_save(eDI_DBG_MOD_PRE_DONEB, 0, di_pre_stru.in_seq);/*dbg*/ if (di_pre_stru.di_wr_buf) { if (di_pre_stru.pre_throw_flag > 0) { di_pre_stru.di_wr_buf->throw_flag = 1; di_pre_stru.pre_throw_flag--; } else { di_pre_stru.di_wr_buf->throw_flag = 0; } #ifdef DET3D if (di_pre_stru.di_wr_buf->vframe->trans_fmt == 0 && di_pre_stru.det3d_trans_fmt != 0 && det3d_en) { di_pre_stru.di_wr_buf->vframe->trans_fmt = di_pre_stru.det3d_trans_fmt; set3d_view(di_pre_stru.det3d_trans_fmt, di_pre_stru.di_wr_buf->vframe); } #endif if (!di_pre_rdma_enable) di_pre_stru.di_post_wr_buf = di_pre_stru.di_wr_buf; post_wr_buf = di_pre_stru.di_post_wr_buf; if (post_wr_buf) { post_wr_buf->vframe->di_pulldown = 0; post_wr_buf->vframe->di_gmv = 0; post_wr_buf->vframe->di_cm_cnt = 0; } if (IS_ERR_OR_NULL(post_wr_buf)) return; if (post_wr_buf && !di_pre_stru.cur_prog_flag) { read_pulldown_info(&glb_frame_mot_num, &glb_field_mot_num); if (pulldown_enable) { pull_down_info = pulldown_detection( &post_wr_buf->pd_config, di_pre_stru.mtn_status, overturn, di_pre_stru.di_inp_buf->vframe); post_wr_buf->vframe->di_pulldown = pull_down_info; } post_wr_buf->vframe->di_pulldown |= 0x08; post_wr_buf->vframe->di_gmv = glb_frame_mot_num; post_wr_buf->vframe->di_cm_cnt = di_rd_mcdi_fldcnt(); if (di_pre_stru.combing_fix_en) cur_lev = adaptive_combing_fixing( di_pre_stru.mtn_status, glb_field_mot_num, glb_frame_mot_num, di_force_bit_mode); if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX)) di_nr_opl()->adaptive_cue_adjust(glb_frame_mot_num, glb_field_mot_num); pulldown_info_clear_g12a(); } if (di_pre_stru.cur_prog_flag) { if (di_pre_stru.prog_proc_type == 0) { /* di_mem_buf_dup->vfrme * is either local vframe, * or bot field of vframe from in_list */ di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0; di_pre_stru.di_mem_buf_dup_p = di_pre_stru.di_chan2_buf_dup_p; di_pre_stru.di_chan2_buf_dup_p = di_pre_stru.di_wr_buf; #ifdef DI_BUFFER_DEBUG di_print("%s:set di_mem to di_chan2,", __func__); di_print("%s:set di_chan2 to di_wr_buf\n", __func__); #endif } else { di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0; /*recycle the progress throw buffer*/ if (di_pre_stru.di_wr_buf->throw_flag) { di_pre_stru.di_wr_buf-> pre_ref_count = 0; di_pre_stru.di_mem_buf_dup_p = NULL; #ifdef DI_BUFFER_DEBUG di_print( "%s set throw %s[%d] pre_ref_count to 0.\n", __func__, vframe_type_name[di_pre_stru.di_wr_buf->type], di_pre_stru.di_wr_buf->index); #endif } else { di_pre_stru.di_mem_buf_dup_p = di_pre_stru.di_wr_buf; } #ifdef DI_BUFFER_DEBUG di_print( "%s: set di_mem_buf_dup_p to di_wr_buf\n", __func__); #endif } di_pre_stru.di_wr_buf->seq = di_pre_stru.pre_ready_seq++; di_pre_stru.di_wr_buf->post_ref_count = 0; di_pre_stru.di_wr_buf->left_right = di_pre_stru.left_right; if (di_pre_stru.source_change_flag) { di_pre_stru.di_wr_buf->new_format_flag = 1; di_pre_stru.source_change_flag = 0; } else { di_pre_stru.di_wr_buf->new_format_flag = 0; } if (bypass_state == 1) { di_pre_stru.di_wr_buf->new_format_flag = 1; bypass_state = 0; #ifdef DI_BUFFER_DEBUG di_print( "%s:bypass_state->0, is_bypass() %d\n", __func__, is_bypass(NULL)); di_print( "trick_mode %d bypass_all %d\n", trick_mode, bypass_all); #endif } if (di_pre_stru.di_post_wr_buf) queue_in(di_pre_stru.di_post_wr_buf, QUEUE_PRE_READY); #ifdef DI_BUFFER_DEBUG di_print( "%s: %s[%d] => pre_ready_list\n", __func__, vframe_type_name[di_pre_stru.di_wr_buf->type], di_pre_stru.di_wr_buf->index); #endif if (di_pre_stru.di_wr_buf) { if (di_pre_rdma_enable) di_pre_stru.di_post_wr_buf = di_pre_stru.di_wr_buf; else di_pre_stru.di_post_wr_buf = NULL; di_pre_stru.di_wr_buf = NULL; } } else { di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0; di_pre_stru.di_mem_buf_dup_p = NULL; if (di_pre_stru.di_chan2_buf_dup_p) { di_pre_stru.di_mem_buf_dup_p = di_pre_stru.di_chan2_buf_dup_p; #ifdef DI_BUFFER_DEBUG di_print( "%s: di_mem_buf_dup_p = di_chan2_buf_dup_p\n", __func__); #endif } di_pre_stru.di_chan2_buf_dup_p = di_pre_stru.di_wr_buf; if (di_pre_stru.source_change_flag) { /* add dummy buf, will not be displayed */ /*coverity[var_deref_model] post_wr_buf has been judged*/ add_dummy_vframe_type_pre(post_wr_buf); } di_pre_stru.di_wr_buf->seq = di_pre_stru.pre_ready_seq++; di_pre_stru.di_wr_buf->left_right = di_pre_stru.left_right; di_pre_stru.di_wr_buf->post_ref_count = 0; if (di_pre_stru.source_change_flag) { di_pre_stru.di_wr_buf->new_format_flag = 1; di_pre_stru.source_change_flag = 0; } else { di_pre_stru.di_wr_buf->new_format_flag = 0; } if (bypass_state == 1) { di_pre_stru.di_wr_buf->new_format_flag = 1; bypass_state = 0; #ifdef DI_BUFFER_DEBUG di_print( "%s:bypass_state->0, is_bypass() %d\n", __func__, is_bypass(NULL)); di_print( "trick_mode %d bypass_all %d\n", trick_mode, bypass_all); #endif } if (di_pre_stru.di_post_wr_buf) queue_in(di_pre_stru.di_post_wr_buf, QUEUE_PRE_READY); di_print("%s: %s[%d] => pre_ready_list\n", __func__, vframe_type_name[di_pre_stru.di_wr_buf->type], di_pre_stru.di_wr_buf->index); if (di_pre_stru.di_wr_buf) { if (di_pre_rdma_enable) di_pre_stru.di_post_wr_buf = di_pre_stru.di_wr_buf; else di_pre_stru.di_post_wr_buf = NULL; di_pre_stru.di_wr_buf = NULL; } } } if (di_pre_stru.di_post_inp_buf && di_pre_rdma_enable) { #ifdef DI_BUFFER_DEBUG di_print("%s: %s[%d] => recycle_list\n", __func__, vframe_type_name[di_pre_stru.di_post_inp_buf->type], di_pre_stru.di_post_inp_buf->index); #endif di_lock_irqfiq_save(irq_flag2); queue_in(di_pre_stru.di_post_inp_buf, QUEUE_RECYCLE); di_pre_stru.di_post_inp_buf = NULL; di_unlock_irqfiq_restore(irq_flag2); } if (di_pre_stru.di_inp_buf) { if (!di_pre_rdma_enable) { #ifdef DI_BUFFER_DEBUG di_print("%s: %s[%d] => recycle_list\n", __func__, vframe_type_name[di_pre_stru.di_inp_buf->type], di_pre_stru.di_inp_buf->index); #endif di_lock_irqfiq_save(irq_flag2); queue_in(di_pre_stru.di_inp_buf, QUEUE_RECYCLE); di_pre_stru.di_inp_buf = NULL; di_unlock_irqfiq_restore(irq_flag2); } else { di_pre_stru.di_post_inp_buf = di_pre_stru.di_inp_buf; di_pre_stru.di_inp_buf = NULL; } } ddbg_mod_save(eDI_DBG_MOD_PRE_DONEE, 0, di_pre_stru.in_seq);/*dbg*/ } static void recycle_vframe_type_pre(struct di_buf_s *di_buf) { ulong irq_flag2 = 0; di_lock_irqfiq_save(irq_flag2); queue_in(di_buf, QUEUE_RECYCLE); di_unlock_irqfiq_restore(irq_flag2); } /* * add dummy buffer to pre ready queue */ static void add_dummy_vframe_type_pre(struct di_buf_s *src_buf) { struct di_buf_s *di_buf_tmp = NULL; if (!queue_empty(QUEUE_LOCAL_FREE)) { di_buf_tmp = get_di_buf_head(QUEUE_LOCAL_FREE); if (di_buf_tmp) { queue_out(di_buf_tmp); di_buf_tmp->pre_ref_count = 0; di_buf_tmp->post_ref_count = 0; di_buf_tmp->post_proc_flag = 3; di_buf_tmp->new_format_flag = 0; if (!IS_ERR_OR_NULL(src_buf)) memcpy(di_buf_tmp->vframe, src_buf->vframe, sizeof(vframe_t)); queue_in(di_buf_tmp, QUEUE_PRE_READY); #ifdef DI_BUFFER_DEBUG di_print("%s: dummy %s[%d] => pre_ready_list\n", __func__, vframe_type_name[di_buf_tmp->type], di_buf_tmp->index); #endif } } } /* * it depend on local buffer queue type is 2 */ static int peek_free_linked_buf(void) { struct di_buf_s *p = NULL; int itmp, p_index = -2; if (list_count(QUEUE_LOCAL_FREE) < 2) return -1; queue_for_each_entry(p, ptmp, QUEUE_LOCAL_FREE, list) { if (abs(p->index - p_index) == 1) return min(p->index, p_index); p_index = p->index; } return -1; } /* * it depend on local buffer queue type is 2 */ static struct di_buf_s *get_free_linked_buf(int idx) { struct di_buf_s *di_buf = NULL, *di_buf_linked = NULL; int pool_idx = 0, di_buf_idx = 0; queue_t *q = &(queue[QUEUE_LOCAL_FREE]); if (list_count(QUEUE_LOCAL_FREE) < 2) return NULL; if (q->pool[idx] != 0 && q->pool[idx + 1] != 0) { pool_idx = ((q->pool[idx] >> 8) & 0xff) - 1; di_buf_idx = q->pool[idx] & 0xff; if (pool_idx < VFRAME_TYPE_NUM) { if (di_buf_idx < di_buf_pool[pool_idx].size) { di_buf = &(di_buf_pool[pool_idx]. di_buf_ptr[di_buf_idx]); queue_out(di_buf); } } pool_idx = ((q->pool[idx + 1] >> 8) & 0xff) - 1; di_buf_idx = q->pool[idx + 1] & 0xff; if (pool_idx < VFRAME_TYPE_NUM) { if (di_buf_idx < di_buf_pool[pool_idx].size) { di_buf_linked = &(di_buf_pool[pool_idx]. di_buf_ptr[di_buf_idx]); queue_out(di_buf_linked); } } /*coverity[FORWARD_NULL] di_buf has been judged empty*/ if (IS_ERR_OR_NULL(di_buf)) return NULL; di_buf->di_wr_linked_buf = di_buf_linked; } return di_buf; } #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC static void pre_inp_canvas_config(struct vframe_s *vf) { if (vf->canvas0Addr == (u32)-1) { canvas_config_config(di_inp_idx[0], &vf->canvas0_config[0]); canvas_config_config(di_inp_idx[1], &vf->canvas0_config[1]); vf->canvas0Addr = (di_inp_idx[1]<<8)|(di_inp_idx[0]); if (vf->plane_num == 2) { vf->canvas0Addr |= (di_inp_idx[1]<<16); } else if (vf->plane_num == 3) { canvas_config_config(di_inp_idx[2], &vf->canvas0_config[2]); vf->canvas0Addr |= (di_inp_idx[2]<<16); } vf->canvas1Addr = vf->canvas0Addr; } } #endif u32 di_request_afbc(u32 onoff) { u32 afbc_busy; if (onoff) afbc_busy = di_request_afbc_hw(afbc_get_decnub(), true); else afbc_busy = di_request_afbc_hw(afbc_get_decnub(), false); return afbc_busy; } static unsigned char pre_de_buf_config(void) { struct di_buf_s *di_buf = NULL; vframe_t *vframe; int i, di_linked_buf_idx = -1; unsigned char change_type = 0; bool bit10_pack_patch = false; unsigned int width_roundup = 2; u32 rls_timeout; u32 afbc_busy; u32 is_afbc_mode = 0; bool flg_1080i = false; bool flg_480i = false; if (di_blocking || !atomic_read(&de_devp->mem_flag)) return 0; if ((list_count(QUEUE_IN_FREE) < 2 && (!di_pre_stru.di_inp_buf_next)) || (queue_empty(QUEUE_LOCAL_FREE))) return 0; if (is_bypass(NULL)) { /* some provider has problem if receiver * get all buffers of provider */ int in_buf_num = 0; cur_lev = 0; for (i = 0; i < MAX_IN_BUF_NUM; i++) if (vframe_in[i] != NULL) in_buf_num++; if (in_buf_num > BYPASS_GET_MAX_BUF_NUM #ifdef DET3D && (di_pre_stru.vframe_interleave_flag == 0) #endif ) return 0; di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false); } else if (di_pre_stru.prog_proc_type == 2) { di_linked_buf_idx = peek_free_linked_buf(); if (di_linked_buf_idx == -1 && !IS_ERR_OR_NULL(di_post_stru.keep_buf)) { recycle_keep_buffer(); pr_info("%s: recycle keep buffer for peek null linked buf\n", __func__); return 0; } } if (di_pre_stru.di_inp_buf_next) { di_pre_stru.di_inp_buf = di_pre_stru.di_inp_buf_next; di_pre_stru.di_inp_buf_next = NULL; #ifdef DI_BUFFER_DEBUG di_print("%s: di_inp_buf_next %s[%d] => di_inp_buf\n", __func__, vframe_type_name[di_pre_stru.di_inp_buf->type], di_pre_stru.di_inp_buf->index); #endif if (di_pre_stru.di_mem_buf_dup_p == NULL) {/* use n */ di_pre_stru.di_mem_buf_dup_p = di_pre_stru.di_inp_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: set di_mem_buf_dup_p to be di_inp_buf\n", __func__); #endif } } else { /* check if source change */ vframe = vf_peek(VFM_NAME); if (vframe && is_from_vdin(vframe)) { #ifdef RUN_DI_PROCESS_IN_IRQ di_pre_stru.vdin2nr = is_input2pre(); #endif } vframe = vf_get(VFM_NAME); if (vframe == NULL) return 0; /*for support compress from dec*/ if (IS_COMP_MODE(vframe->type) && !is_bypass(vframe)) { is_afbc_mode = true; if (IS_VDIN_SRC(vframe->source_type) && IS_I_SRC(vframe->type)) { vframe->width = vframe->compWidth; vframe->height = vframe->compHeight*2; } else { vframe->width = vframe->compWidth; vframe->height = vframe->compHeight; } } di_print("DI: get %dth vf[0x%p] from frontend %u ms.\n", di_pre_stru.in_seq, vframe, jiffies_to_msecs(jiffies_64 - vframe->ready_jiffies64)); vframe->prog_proc_config = (prog_proc_config&0x20) >> 5; if (vframe->width > 10000 || vframe->height > 10000 || hold_video || di_pre_stru.bad_frame_throw_count > 0) { if (vframe->width > 10000 || vframe->height > 10000) di_pre_stru.bad_frame_throw_count = 10; di_pre_stru.bad_frame_throw_count--; vf_put(vframe, VFM_NAME); vf_notify_provider( VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); return 0; } /* * for afbc used by vpp and di, when di use it, * vpp need release afbc, waitting vpp release */ if (di_pre_stru.wait_afbc) { /*check time out and afbc release status*/ rls_timeout = jiffies_to_msecs(jiffies_64 - di_pre_stru.afbc_rls_time); afbc_busy = di_request_afbc(true); if (afbc_busy && (rls_timeout < 80)) { vf_put(vframe, VFM_NAME); vf_notify_provider( VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); pr_info("di: drop vframe (%d) t:%d\n", afbc_busy, rls_timeout); return 0; } else if (!afbc_busy) { /*afbc_busy = di_request_afbc(false);*/ di_pre_stru.wait_afbc = false; pr_info("di: afbc hw free\n"); } else { di_pre_stru.wait_afbc = false; pr_info("di: afbc wait timeout\n"); } } bit10_pack_patch = (is_meson_gxtvbb_cpu() || is_meson_gxl_cpu() || is_meson_gxm_cpu()); width_roundup = bit10_pack_patch ? 16 : width_roundup; if (di_force_bit_mode == 10) force_width = roundup(vframe->width, width_roundup); else force_width = 0; di_pre_stru.source_trans_fmt = vframe->trans_fmt; di_pre_stru.left_right = di_pre_stru.left_right ? 0 : 1; di_pre_stru.invert_flag = (vframe->type & TB_DETECT_MASK) ? true : false; vframe->type &= ~TB_DETECT_MASK; if ((((invert_top_bot & 0x2) != 0) || di_pre_stru.invert_flag) && (!is_progressive(vframe))) { if ( (vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP) { vframe->type &= (~VIDTYPE_TYPEMASK); vframe->type |= VIDTYPE_INTERLACE_BOTTOM; } else { vframe->type &= (~VIDTYPE_TYPEMASK); vframe->type |= VIDTYPE_INTERLACE_TOP; } } /*di_pre_stru.width_bk = vframe->width;*/ if (force_width) vframe->width = force_width; if (force_height) vframe->height = force_height; /* backup frame motion info */ vframe->combing_cur_lev = cur_lev; di_print("%s: vf_get => 0x%p\n", __func__, vframe); di_buf = get_di_buf_head(QUEUE_IN_FREE); if (check_di_buf(di_buf, 10)) return 0; if (di_log_flag & DI_LOG_VFRAME) dump_vframe(vframe); #ifdef SUPPORT_MPEG_TO_VDIN if ( (!is_from_vdin(vframe)) && (vframe->sig_fmt == TVIN_SIG_FMT_NULL) && mpeg2vdin_flag) { struct vdin_arg_s vdin_arg; struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops(); vdin_arg.cmd = VDIN_CMD_GET_HISTGRAM; vdin_arg.private = (unsigned int)vframe; if (vdin_ops->tvin_vdin_func) vdin_ops->tvin_vdin_func(0, &vdin_arg); } #endif memcpy(di_buf->vframe, vframe, sizeof(vframe_t)); di_buf->width_bk = vframe->width; di_buf->vframe->private_data = di_buf; vframe_in[di_buf->index] = vframe; di_buf->seq = di_pre_stru.in_seq; di_pre_stru.in_seq++; queue_out(di_buf); change_type = is_source_change(vframe); /* source change, when i mix p,force p as i*/ if (change_type == 1 || (change_type == 2 && di_pre_stru.cur_prog_flag == 1)) { di_pre_stru.field_count_for_cont = 0; if (di_pre_stru.di_mem_buf_dup_p) { /*avoid only 2 i field then p field*/ if ( (di_pre_stru.cur_prog_flag == 0) && use_2_interlace_buff) di_pre_stru.di_mem_buf_dup_p-> post_proc_flag = -1; di_pre_stru.di_mem_buf_dup_p->pre_ref_count = 0; di_pre_stru.di_mem_buf_dup_p = NULL; } if (di_pre_stru.di_chan2_buf_dup_p) { /*avoid only 1 i field then p field*/ if ( (di_pre_stru.cur_prog_flag == 0) && use_2_interlace_buff) di_pre_stru.di_chan2_buf_dup_p-> post_proc_flag = -1; di_pre_stru.di_chan2_buf_dup_p->pre_ref_count = 0; di_pre_stru.di_chan2_buf_dup_p = NULL; } #if 0 /* channel change will occur between atv and dtv, * that need mirror */ if (!IS_ERR_OR_NULL(di_post_stru.keep_buf)) { if (di_post_stru.keep_buf->vframe ->source_type != di_buf->vframe->source_type) { recycle_keep_buffer(); pr_info("%s: source type changed recycle buffer!!!\n", __func__); } } #endif pr_info( "%s:%ums %dth source change: 0x%x/%d/%d/%d=>0x%x/%d/%d/%d\n", __func__, jiffies_to_msecs(jiffies_64), di_pre_stru.in_seq, di_pre_stru.cur_inp_type, di_pre_stru.cur_width, di_pre_stru.cur_height, di_pre_stru.cur_source_type, di_buf->vframe->type, di_buf->vframe->width, di_buf->vframe->height, di_buf->vframe->source_type); if (IS_COMP_MODE(di_buf->vframe->type) && !is_bypass(vframe)) { if (IS_VDIN_SRC(di_buf->vframe->source_type) && IS_I_SRC(di_buf->vframe->type)) { di_pre_stru.cur_width = di_buf->vframe->compWidth; di_pre_stru.cur_height = di_buf->vframe->compHeight*2; } else { di_pre_stru.cur_width = di_buf->vframe->compWidth; di_pre_stru.cur_height = di_buf->vframe->compHeight; } } else { di_pre_stru.cur_width = di_buf->vframe->width; di_pre_stru.cur_height = di_buf->vframe->height; } di_pre_stru.cur_prog_flag = is_progressive(di_buf->vframe); if (di_pre_stru.cur_prog_flag) { if ((use_2_interlace_buff) && !(prog_proc_config & 0x10)) di_pre_stru.prog_proc_type = 2; else di_pre_stru.prog_proc_type = prog_proc_config & 0x10; } else di_pre_stru.prog_proc_type = 0; di_pre_stru.cur_inp_type = di_buf->vframe->type; di_pre_stru.cur_source_type = di_buf->vframe->source_type; #ifdef CONFIG_AMLOGIC_MEDIA_TVIN di_pre_stru.cur_sig_fmt = di_buf->vframe->sig_fmt; #endif di_pre_stru.orientation = di_buf->vframe->video_angle; di_pre_stru.source_change_flag = 1; di_pre_stru.input_size_change_flag = true; #ifdef SUPPORT_MPEG_TO_VDIN if ((!is_from_vdin(vframe)) && (vframe->sig_fmt == TVIN_SIG_FMT_NULL) && (mpeg2vdin_en)) { struct vdin_arg_s vdin_arg; struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops(); vdin_arg.cmd = VDIN_CMD_MPEGIN_START; vdin_arg.h_active = di_pre_stru.cur_width; vdin_arg.v_active = di_pre_stru.cur_height; if (vdin_ops->tvin_vdin_func) vdin_ops->tvin_vdin_func(0, &vdin_arg); mpeg2vdin_flag = 1; } #endif /* * for afbc used by vpp and di, when di use it, * vpp need release afbc, waitting vpp release */ if (is_meson_tl1_cpu() || is_meson_sm1_cpu()) { /*compress mode and format changed*/ if (!is_bypass(di_buf->vframe) && di_pre_stru.source_change_flag && IS_COMP_MODE(di_pre_stru.cur_inp_type) && !afbc_is_free() && !di_pre_stru.wait_afbc) { afbc_busy = di_request_afbc(true); vf_put(vframe, VFM_NAME); vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); recycle_vframe_type_pre(di_buf); di_pre_stru.afbc_rls_time = jiffies_64; di_pre_stru.wait_afbc = true; pr_info("di req afbc:%d\n", afbc_busy); return 0; } } /*di_pre_stru.field_count_for_cont = 0;*/ } else if (di_pre_stru.cur_prog_flag == 0) { /* check if top/bot interleaved */ if (change_type == 2) /* source is i interleaves p fields */ di_pre_stru.force_interlace = true; if ((di_pre_stru.cur_inp_type & VIDTYPE_TYPEMASK) == (di_buf->vframe->type & VIDTYPE_TYPEMASK)) { if ((di_buf->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP) same_field_top_count++; else same_field_bot_count++; } di_pre_stru.cur_inp_type = di_buf->vframe->type; } else { di_pre_stru.cur_inp_type = di_buf->vframe->type; } if (is_bypass(di_buf->vframe)) { /* bypass progressive */ di_buf->seq = di_pre_stru.pre_ready_seq++; di_buf->post_ref_count = 0; cur_lev = 0; if (di_pre_stru.source_change_flag) { di_buf->new_format_flag = 1; di_pre_stru.source_change_flag = 0; } else { di_buf->new_format_flag = 0; } if (bypass_state == 0) { if (di_pre_stru.di_mem_buf_dup_p) { di_pre_stru.di_mem_buf_dup_p-> pre_ref_count = 0; di_pre_stru.di_mem_buf_dup_p = NULL; } if (di_pre_stru.di_chan2_buf_dup_p) { di_pre_stru.di_chan2_buf_dup_p-> pre_ref_count = 0; di_pre_stru.di_chan2_buf_dup_p = NULL; } if (di_pre_stru.di_wr_buf) { di_pre_stru.di_wr_buf->pre_ref_count = 0; di_pre_stru.di_wr_buf->post_ref_count = 0; recycle_vframe_type_pre( di_pre_stru.di_wr_buf); #ifdef DI_BUFFER_DEBUG di_print( "%s: %s[%d] => recycle_list\n", __func__, vframe_type_name[di_pre_stru. di_wr_buf->type], di_pre_stru.di_wr_buf->index); #endif di_pre_stru.di_wr_buf = NULL; } di_buf->new_format_flag = 1; bypass_state = 1; #ifdef DI_BUFFER_DEBUG di_print( "%s:bypass_state = 1, is_bypass() %d\n", __func__, is_bypass(NULL)); di_print( "trick_mode %d bypass_all %d\n", trick_mode, bypass_all); #endif } top_bot_config(di_buf); queue_in(di_buf, QUEUE_PRE_READY); /*if previous isn't bypass post_wr_buf not recycled */ if (di_pre_stru.di_post_wr_buf && di_pre_rdma_enable) { queue_in( di_pre_stru.di_post_inp_buf, QUEUE_RECYCLE); di_pre_stru.di_post_inp_buf = NULL; } if ( (by_pass_pre & 0x2) && !di_pre_stru.cur_prog_flag) di_buf->post_proc_flag = -2; else di_buf->post_proc_flag = 0; #ifdef DI_BUFFER_DEBUG di_print( "%s: %s[%d] => pre_ready_list\n", __func__, vframe_type_name[di_buf->type], di_buf->index); #endif return 0; } else if (is_progressive(di_buf->vframe)) { if ( is_handle_prog_frame_as_interlace(vframe) && (is_progressive(vframe))) { struct di_buf_s *di_buf_tmp = NULL; vframe_in[di_buf->index] = NULL; di_buf->vframe->type &= (~VIDTYPE_TYPEMASK); di_buf->vframe->type |= VIDTYPE_INTERLACE_TOP; di_buf->post_proc_flag = 0; di_buf_tmp = get_di_buf_head(QUEUE_IN_FREE); if (check_di_buf(di_buf_tmp, 10)) { recycle_vframe_type_pre(di_buf); pr_err("DI:no free in_buffer for progressive skip.\n"); return 0; } queue_out(di_buf_tmp); di_buf_tmp->vframe->private_data = di_buf_tmp; di_buf_tmp->seq = di_pre_stru.in_seq; di_pre_stru.in_seq++; vframe_in[di_buf_tmp->index] = vframe; memcpy( di_buf_tmp->vframe, vframe, sizeof(vframe_t)); di_pre_stru.di_inp_buf_next = di_buf_tmp; di_buf_tmp->vframe->type &= (~VIDTYPE_TYPEMASK); di_buf_tmp->vframe->type |= VIDTYPE_INTERLACE_BOTTOM; di_buf_tmp->post_proc_flag = 0; di_pre_stru.di_inp_buf = di_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: %s[%d] => di_inp_buf; %s[%d] => di_inp_buf_next\n", __func__, vframe_type_name[di_buf->type], di_buf->index, vframe_type_name[di_buf_tmp->type], di_buf_tmp->index); #endif if (di_pre_stru.di_mem_buf_dup_p == NULL) { di_pre_stru.di_mem_buf_dup_p = di_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: set di_mem_buf_dup_p to be di_inp_buf\n", __func__); #endif } } else { di_buf->post_proc_flag = 0; if ((prog_proc_config & 0x40) || di_pre_stru.force_interlace) di_buf->post_proc_flag = 1; di_pre_stru.di_inp_buf = di_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: %s[%d] => di_inp_buf\n", __func__, vframe_type_name[di_buf->type], di_buf->index); #endif if ( di_pre_stru.di_mem_buf_dup_p == NULL) { /* use n */ di_pre_stru.di_mem_buf_dup_p = di_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: set di_mem_buf_dup_p to be di_inp_buf\n", __func__); #endif } } } else { /*********************************/ if ((di_buf->vframe->width >= 1920) && (di_buf->vframe->height >= 1080)) flg_1080i = true; else if ((di_buf->vframe->width == 720) && (di_buf->vframe->height == 480)) flg_480i = true; /*********************************/ if (di_pre_stru.di_chan2_buf_dup_p == NULL) { di_pre_stru.field_count_for_cont = 0; di_pre_stru.source_change_flag = 1; di_pre_stru.input_size_change_flag = true; //di_buf->new_format_flag = 1; di_pr_info("set cnt 0;"); /* ignore contp2rd and contprd */ } di_buf->post_proc_flag = 1; di_pre_stru.di_inp_buf = di_buf; di_print("%s: %s[%d] => di_inp_buf\n", __func__, vframe_type_name[di_buf->type], di_buf->index); if (di_pre_stru.di_mem_buf_dup_p == NULL) {/* use n */ di_pre_stru.di_mem_buf_dup_p = di_buf; #ifdef DI_BUFFER_DEBUG di_print( "%s: set di_mem_buf_dup_p to be di_inp_buf\n", __func__); #endif } } } /* di_wr_buf */ if (di_pre_stru.prog_proc_type == 2) { di_linked_buf_idx = peek_free_linked_buf(); if (di_linked_buf_idx != -1) di_buf = get_free_linked_buf(di_linked_buf_idx); else di_buf = NULL; if (di_buf == NULL) { /* recycle_vframe_type_pre(di_pre_stru.di_inp_buf); *save for next process */ recycle_keep_buffer(); di_pre_stru.di_inp_buf_next = di_pre_stru.di_inp_buf; return 0; } di_buf->post_proc_flag = 0; di_buf->di_wr_linked_buf->pre_ref_count = 0; di_buf->di_wr_linked_buf->post_ref_count = 0; di_buf->canvas_config_flag = 1; } else { di_buf = get_di_buf_head(QUEUE_LOCAL_FREE); if (check_di_buf(di_buf, 11)) { /* recycle_keep_buffer(); * pr_dbg("%s:recycle keep buffer\n", __func__); */ recycle_vframe_type_pre(di_pre_stru.di_inp_buf); return 0; } queue_out(di_buf); if (di_pre_stru.prog_proc_type & 0x10) di_buf->canvas_config_flag = 1; else di_buf->canvas_config_flag = 2; di_buf->di_wr_linked_buf = NULL; } di_pre_stru.di_wr_buf = di_buf; di_pre_stru.di_wr_buf->pre_ref_count = 1; #ifdef DI_BUFFER_DEBUG di_print("%s: %s[%d] => di_wr_buf\n", __func__, vframe_type_name[di_buf->type], di_buf->index); if (di_buf->di_wr_linked_buf) di_print("%s: linked %s[%d] => di_wr_buf\n", __func__, vframe_type_name[di_buf->di_wr_linked_buf->type], di_buf->di_wr_linked_buf->index); #endif /*for support compress from dec, not from vdin*/ if (IS_COMP_MODE(di_pre_stru.cur_inp_type) && (!(di_pre_stru.cur_inp_type & VIDTYPE_VIU_422))) { /*compress type and not from vdin*/ di_pre_stru.di_inp_buf->vframe->width = di_pre_stru.di_inp_buf->vframe->compWidth; di_pre_stru.di_inp_buf->vframe->height = di_pre_stru.di_inp_buf->vframe->compHeight; } memcpy(di_buf->vframe, di_pre_stru.di_inp_buf->vframe, sizeof(vframe_t)); di_buf->vframe->private_data = di_buf; di_buf->vframe->canvas0Addr = di_buf->nr_canvas_idx; di_buf->vframe->canvas1Addr = di_buf->nr_canvas_idx; /* set vframe bit info */ di_buf->vframe->bitdepth &= ~(BITDEPTH_YMASK); di_buf->vframe->bitdepth &= ~(FULL_PACK_422_MODE); di_buf->width_bk = di_buf->vframe->width; if (de_devp->pps_enable && pps_position) { if (pps_dstw != di_buf->vframe->width) { di_buf->vframe->width = pps_dstw; /*di_pre_stru.width_bk = pps_dstw;*/ di_buf->width_bk = pps_dstw; } if (pps_dsth != di_buf->vframe->height) di_buf->vframe->height = pps_dsth; } else if (de_devp->h_sc_down_en) { if (pre_hsc_down_width != di_buf->vframe->width) { pr_info("di: hscd %d to %d\n", di_buf->vframe->width, pre_hsc_down_width); di_buf->vframe->width = pre_hsc_down_width; /*di_pre_stru.width_bk = pre_hsc_down_width;*/ di_buf->width_bk = pre_hsc_down_width; } } if (di_force_bit_mode == 10) { di_buf->vframe->bitdepth |= (BITDEPTH_Y10); if (full_422_pack) di_buf->vframe->bitdepth |= (FULL_PACK_422_MODE); } else di_buf->vframe->bitdepth |= (BITDEPTH_Y8); if (di_pre_stru.prog_proc_type) { di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD; if (di_pre_stru.cur_inp_type & VIDTYPE_PRE_INTERLACE) di_buf->vframe->type |= VIDTYPE_PRE_INTERLACE; } else { if ( ((di_pre_stru.di_inp_buf->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP)) di_buf->vframe->type = VIDTYPE_INTERLACE_TOP | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD; else di_buf->vframe->type = VIDTYPE_INTERLACE_BOTTOM | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD; /*add for vpp skip line ref*/ if (bypass_state == 0) di_buf->vframe->type |= VIDTYPE_PRE_INTERLACE; } if (is_afbc_mode) { di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; /*pr_info("vf type:0x%x\n", di_buf->vframe->type);*/ } if (is_bypass_post()) { if (bypass_post_state == 0) di_pre_stru.source_change_flag = 1; bypass_post_state = 1; } else { if (bypass_post_state) di_pre_stru.source_change_flag = 1; bypass_post_state = 0; } if (di_pre_stru.di_inp_buf->post_proc_flag == 0) { di_pre_stru.madi_enable = 0; di_pre_stru.mcdi_enable = 0; di_buf->post_proc_flag = 0; di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false); } else if (bypass_post_state) { di_pre_stru.madi_enable = 0; di_pre_stru.mcdi_enable = 0; di_buf->post_proc_flag = 0; di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false); } else { di_pre_stru.madi_enable = (pre_enable_mask&1); di_pre_stru.mcdi_enable = ((pre_enable_mask>>1)&1); di_buf->post_proc_flag = 1; di_patch_post_update_mc_sw(DI_MC_SW_OTHER, mcpre_en);//en } if ((di_pre_stru.di_mem_buf_dup_p == di_pre_stru.di_wr_buf) || (di_pre_stru.di_chan2_buf_dup_p == di_pre_stru.di_wr_buf)) { pr_dbg("+++++++++++++++++++++++\n"); if (recovery_flag == 0) recovery_log_reason = 12; recovery_flag++; return 0; } if (is_meson_tl1_cpu() && di_pre_stru.comb_mode && flg_1080i) { di_pre_stru.combing_fix_en = false; fix_tl1_1080i_patch_sel(di_pre_stru.comb_mode); } else { di_pre_stru.combing_fix_en = combing_fix_en; } if (di_pre_stru.combing_fix_en) { if (flg_1080i) com_patch_pre_sw_set(1); else if (flg_480i) com_patch_pre_sw_set(2); else com_patch_pre_sw_set(0); } return 1; } static int check_recycle_buf(void) { struct di_buf_s *di_buf = NULL;/* , *ptmp; */ int itmp; int ret = 0; bool blk_flg = 0; #ifdef DI_KEEP_HIS if (di_blocking) return ret; #endif queue_for_each_entry(di_buf, ptmp, QUEUE_RECYCLE, list) { if (di_blocking) blk_flg = 1; if ((di_buf->pre_ref_count == 0) && (di_buf->post_ref_count == 0)) { if (di_buf->type == VFRAME_TYPE_IN) { queue_out(di_buf); if (vframe_in[di_buf->index]) { vf_put( vframe_in[di_buf->index], VFM_NAME); vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); di_print( "%s: vf_put(%d) 0x%p, %u ms\n", __func__, di_pre_stru.recycle_seq, vframe_in[di_buf->index], jiffies_to_msecs(jiffies_64 - vframe_in[di_buf->index]->ready_jiffies64)); vframe_in[di_buf->index] = NULL; } di_buf->invert_top_bot_flag = 0; queue_in(di_buf, QUEUE_IN_FREE); di_pre_stru.recycle_seq++; ret |= 1; } else { queue_out(di_buf); di_buf->invert_top_bot_flag = 0; queue_in(di_buf, QUEUE_LOCAL_FREE); if (di_buf->di_wr_linked_buf) { queue_in( di_buf->di_wr_linked_buf, QUEUE_LOCAL_FREE); #ifdef DI_BUFFER_DEBUG di_print( "%s: linked %s[%d]=>recycle_list\n", __func__, vframe_type_name[ di_buf->di_wr_linked_buf->type], di_buf->di_wr_linked_buf->index ); #endif di_buf->di_wr_linked_buf = NULL; } ret |= 2; } #ifdef DI_BUFFER_DEBUG di_print("%s: recycle %s[%d]\n", __func__, vframe_type_name[di_buf->type], di_buf->index); #endif } } if (blk_flg) pr_info("di:blk:recycle\n"); return ret; } #ifdef DET3D static void set3d_view(enum tvin_trans_fmt trans_fmt, struct vframe_s *vf) { struct vframe_view_s *left_eye, *right_eye; left_eye = &vf->left_eye; right_eye = &vf->right_eye; switch (trans_fmt) { case TVIN_TFMT_3D_DET_LR: case TVIN_TFMT_3D_LRH_OLOR: left_eye->start_x = 0; left_eye->start_y = 0; left_eye->width = vf->width >> 1; left_eye->height = vf->height; right_eye->start_x = vf->width >> 1; right_eye->start_y = 0; right_eye->width = vf->width >> 1; right_eye->height = vf->height; break; case TVIN_TFMT_3D_DET_TB: case TVIN_TFMT_3D_TB: left_eye->start_x = 0; left_eye->start_y = 0; left_eye->width = vf->width; left_eye->height = vf->height >> 1; right_eye->start_x = 0; right_eye->start_y = vf->height >> 1; right_eye->width = vf->width; right_eye->height = vf->height >> 1; break; case TVIN_TFMT_3D_DET_INTERLACE: left_eye->start_x = 0; left_eye->start_y = 0; left_eye->width = vf->width; left_eye->height = vf->height >> 1; right_eye->start_x = 0; right_eye->start_y = 0; right_eye->width = vf->width; right_eye->height = vf->height >> 1; break; case TVIN_TFMT_3D_DET_CHESSBOARD: /*** * LRLRLR LRLRLR * LRLRLR or RLRLRL * LRLRLR LRLRLR * LRLRLR RLRLRL */ break; default: /* 2D */ left_eye->start_x = 0; left_eye->start_y = 0; left_eye->width = 0; left_eye->height = 0; right_eye->start_x = 0; right_eye->start_y = 0; right_eye->width = 0; right_eye->height = 0; break; } } /* * static int get_3d_info(struct vframe_s *vf) * { * int ret = 0; * * vf->trans_fmt = det3d_fmt_detect(); * pr_dbg("[det3d..]new 3d fmt: %d\n", vf->trans_fmt); * * vdin_set_view(vf->trans_fmt, vf); * * return ret; * } */ static void det3d_irq(void) { unsigned int data32 = 0, likely_val = 0; unsigned long frame_sum = 0; if (!det3d_en) return; data32 = det3d_fmt_detect(); switch (data32) { case TVIN_TFMT_3D_DET_LR: case TVIN_TFMT_3D_LRH_OLOR: di_pre_stru.det_lr++; break; case TVIN_TFMT_3D_DET_TB: case TVIN_TFMT_3D_TB: di_pre_stru.det_tp++; break; case TVIN_TFMT_3D_DET_INTERLACE: di_pre_stru.det_la++; break; default: di_pre_stru.det_null++; break; } if (det3d_mode != data32) { det3d_mode = data32; di_print("[det3d..]new 3d fmt: %d\n", det3d_mode); } if (frame_count > 20) { frame_sum = di_pre_stru.det_lr + di_pre_stru.det_tp + di_pre_stru.det_la + di_pre_stru.det_null; if ((frame_count%det3d_frame_cnt) || (frame_sum > UINT_MAX)) return; likely_val = max3(di_pre_stru.det_lr, di_pre_stru.det_tp, di_pre_stru.det_la); if (di_pre_stru.det_null >= likely_val) det3d_mode = 0; else if (likely_val == di_pre_stru.det_lr) det3d_mode = TVIN_TFMT_3D_LRH_OLOR; else if (likely_val == di_pre_stru.det_tp) det3d_mode = TVIN_TFMT_3D_TB; else det3d_mode = TVIN_TFMT_3D_DET_INTERLACE; di_pre_stru.det3d_trans_fmt = det3d_mode; } else { di_pre_stru.det3d_trans_fmt = 0; } } #endif unsigned int ro_mcdi_col_cfd[26]; static void get_mcinfo_from_reg_in_irq(void) { unsigned int i = 0, ncolcrefsum = 0, blkcount = 0, *reg = NULL; /*get info for current field process by post*/ di_pre_stru.di_wr_buf->curr_field_mcinfo.highvertfrqflg = (Rd(MCDI_RO_HIGH_VERT_FRQ_FLG) & 0x1); /* post:MCDI_MC_REL_GAIN_OFFST_0 */ di_pre_stru.di_wr_buf->curr_field_mcinfo.motionparadoxflg = (Rd(MCDI_RO_MOTION_PARADOX_FLG) & 0x1); /* post:MCDI_MC_REL_GAIN_OFFST_0 */ reg = di_pre_stru.di_wr_buf->curr_field_mcinfo.regs; for (i = 0; i < 26; i++) { ro_mcdi_col_cfd[i] = Rd(0x2fb0 + i); di_pre_stru.di_wr_buf->curr_field_mcinfo.regs[i] = 0; if (!calc_mcinfo_en) *(reg + i) = ro_mcdi_col_cfd[i]; } if (calc_mcinfo_en) { blkcount = (di_pre_stru.cur_width + 4) / 5; for (i = 0; i < blkcount; i++) { ncolcrefsum += ((ro_mcdi_col_cfd[i / 32] >> (i % 32)) & 0x1); if ( ((ncolcrefsum + (blkcount >> 1)) << 8) / blkcount > colcfd_thr) for (i = 0; i < blkcount; i++) *(reg + i / 32) += (1 << (i % 32)); } } } static unsigned int bit_reverse(unsigned int val) { unsigned int i = 0, res = 0; for (i = 0; i < 16; i++) { res |= (((val&(1<>i)<<(31-i)); res |= (((val&(1<<(31-i)))<>(31-i)); } return res; } static void set_post_mcinfo(struct mcinfo_pre_s *curr_field_mcinfo) { unsigned int i = 0, value = 0; DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_REL_GAIN_OFFST_0, curr_field_mcinfo->highvertfrqflg, 24, 1); DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_REL_GAIN_OFFST_0, curr_field_mcinfo->motionparadoxflg, 25, 1); for (i = 0; i < 26; i++) { if (overturn) value = bit_reverse(curr_field_mcinfo->regs[i]); else value = curr_field_mcinfo->regs[i]; DI_VSYNC_WR_MPEG_REG(0x2f78 + i, value); } } static unsigned char intr_mode; static irqreturn_t de_irq(int irq, void *dev_instance) { #ifndef CHECK_DI_DONE unsigned int data32 = Rd(DI_INTR_CTRL); unsigned int mask32 = (data32 >> 16) & 0x3ff; unsigned int flag = 0; data32 &= 0x3fffffff; if ((data32 & 1) == 0 && di_dbg_mask&8) pr_info("irq[%d]pre|post=0 write done.\n", irq); if (di_pre_stru.pre_de_busy) { /* only one interrupt mask should be enable */ if ((data32 & 2) && !(mask32 & 2)) { di_print("== MTNWR ==\n"); flag = 1; } else if ((data32 & 1) && !(mask32 & 1)) { di_print("== NRWR ==\n"); flag = 1; } else { di_print("== DI IRQ 0x%x ==\n", data32); flag = 0; } } #endif ddbg_mod_save(eDI_DBG_MOD_PRE_IRQB, 0, di_pre_stru.in_seq); #ifdef DET3D if (det3d_en) { if ((data32 & 0x100) && !(mask32 & 0x100) && flag) { DI_Wr(DI_INTR_CTRL, data32); det3d_irq(); } else { goto end; } } else { DI_Wr(DI_INTR_CTRL, data32); } #else if (flag) DI_Wr(DI_INTR_CTRL, (data32&0xfffffffb)|(intr_mode<<30)); #endif if (di_pre_stru.pre_de_busy == 0) { pr_err("di:err:%s: enter %x\n", __func__, Rd(DI_INTR_CTRL)); return IRQ_HANDLED; } if (flag) { di_pre_stru.irq_time[0] = (cur_to_msecs() - di_pre_stru.irq_time[0]); trace_di_pre("PRE-IRQ-0", di_pre_stru.field_count_for_cont, di_pre_stru.irq_time[0]); /*add from valsi wang.feng*/ di_arb_sw(false); di_arb_sw(true); if (mcpre_en) { get_mcinfo_from_reg_in_irq(); #ifndef CONFIG_AMLOGIC_REMOVE_OLD if ((is_meson_gxlx_cpu() && di_pre_stru.field_count_for_cont >= 4) || is_meson_txhd_cpu()) mc_pre_mv_irq(); #endif calc_lmv_base_mcinfo((di_pre_stru.cur_height>>1), di_pre_stru.di_wr_buf->mcinfo_vaddr); } di_nr_opl()->nr_process_in_irq(); if ((data32&0x200) && de_devp->nrds_enable) nr_ds_irq(); /* disable mif */ enable_di_pre_mif(false, mcpre_en); di_pre_stru.pre_de_process_done = 1; di_pre_stru.pre_de_busy = 0; if (init_flag) /* pr_dbg("%s:up di sema\n", __func__); */ trigger_pre_di_process(TRIGGER_PRE_BY_DE_IRQ); } ddbg_mod_save(eDI_DBG_MOD_PRE_IRQE, 0, di_pre_stru.in_seq); return IRQ_HANDLED; } static irqreturn_t post_irq(int irq, void *dev_instance) { unsigned int data32 = Rd(DI_INTR_CTRL); data32 &= 0x3fffffff; if ((data32 & 4) == 0) { if (di_dbg_mask&8) pr_info("irq[%d]post write undone.\n", irq); return IRQ_HANDLED; } ddbg_mod_save(eDI_DBG_MOD_POST_IRQB, 0, 0); if ((post_wr_en && post_wr_support) && (data32&0x4)) { di_post_stru.de_post_process_done = 1; di_post_stru.post_de_busy = 0; di_post_stru.irq_time = (cur_to_msecs() - di_post_stru.irq_time); trace_di_post("POST-IRQ-1", di_post_stru.post_wr_cnt, di_post_stru.irq_time); DI_Wr(DI_INTR_CTRL, (data32&0xffff0004)|(intr_mode<<30)); /* disable wr back avoid pps read in g12a */ DI_Wr_reg_bits(DI_POST_CTRL, 0, 7, 1); } ddbg_mod_save(eDI_DBG_MOD_POST_IRQE, 0, 0); if (init_flag) trigger_pre_di_process(TRIGGER_PRE_BY_DE_IRQ); return IRQ_HANDLED; } /* * di post process */ static void inc_post_ref_count(struct di_buf_s *di_buf) { /* int post_blend_mode; */ if (IS_ERR_OR_NULL(di_buf)) { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) recovery_log_reason = 13; recovery_flag++; return; } if (di_buf->di_buf_dup_p[1]) di_buf->di_buf_dup_p[1]->post_ref_count++; if (di_buf->di_buf_dup_p[0]) di_buf->di_buf_dup_p[0]->post_ref_count++; if (di_buf->di_buf_dup_p[2]) di_buf->di_buf_dup_p[2]->post_ref_count++; } static void dec_post_ref_count(struct di_buf_s *di_buf) { if (IS_ERR_OR_NULL(di_buf)) { pr_dbg("%s: Error\n", __func__); if (recovery_flag == 0) recovery_log_reason = 14; recovery_flag++; return; } if (di_buf->pd_config.global_mode == PULL_DOWN_BUF1) return; if (di_buf->di_buf_dup_p[1]) di_buf->di_buf_dup_p[1]->post_ref_count--; if (di_buf->di_buf_dup_p[0] && di_buf->di_buf_dup_p[0]->post_proc_flag != -2) di_buf->di_buf_dup_p[0]->post_ref_count--; if (di_buf->di_buf_dup_p[2]) di_buf->di_buf_dup_p[2]->post_ref_count--; } static void vscale_skip_disable_post(struct di_buf_s *di_buf, vframe_t *disp_vf) { struct di_buf_s *di_buf_i = NULL; int canvas_height = di_buf->di_buf[0]->canvas_height; if (di_vscale_skip_enable & 0x2) {/* drop the bottom field */ if ((di_buf->di_buf_dup_p[0]) && (di_buf->di_buf_dup_p[1])) di_buf_i = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? di_buf->di_buf_dup_p[1] : di_buf->di_buf_dup_p[0]; else di_buf_i = di_buf->di_buf[0]; } else { if ((di_buf->di_buf[0]->post_proc_flag > 0) && (di_buf->di_buf_dup_p[1])) di_buf_i = di_buf->di_buf_dup_p[1]; else di_buf_i = di_buf->di_buf[0]; } disp_vf->type = di_buf_i->vframe->type; /* pr_dbg("%s (%x %x) (%x %x)\n", __func__, * disp_vf, disp_vf->type, di_buf_i->vframe, * di_buf_i->vframe->type); */ disp_vf->width = di_buf_i->vframe->width; disp_vf->height = di_buf_i->vframe->height; disp_vf->duration = di_buf_i->vframe->duration; disp_vf->pts = di_buf_i->vframe->pts; disp_vf->flag = di_buf_i->vframe->flag; disp_vf->canvas0Addr = di_post_idx[di_post_stru.canvas_id][0]; disp_vf->canvas1Addr = di_post_idx[di_post_stru.canvas_id][0]; canvas_config( di_post_idx[di_post_stru.canvas_id][0], di_buf_i->nr_adr, di_buf_i->canvas_width[NR_CANVAS], canvas_height, 0, 0); disable_post_deinterlace_2(); di_post_stru.vscale_skip_flag = true; } static void process_vscale_skip(struct di_buf_s *di_buf, vframe_t *disp_vf) { int ret = 0, vpp_3d_mode = 0; if ((di_buf->di_buf[0] != NULL) && (di_vscale_skip_enable & 0x5) && (di_buf->process_fun_index != PROCESS_FUN_NULL)) { ret = get_current_vscale_skip_count(disp_vf); di_vscale_skip_count = (ret & 0xff); vpp_3d_mode = ((ret >> 8) & 0xff); if (((di_vscale_skip_count > 0) && (di_vscale_skip_enable & 0x5)) || (di_vscale_skip_enable >> 16) ) { if ((di_vscale_skip_enable & 0x8) || vpp_3d_mode) { vscale_skip_disable_post(di_buf, disp_vf); } else { if (di_buf->di_buf_dup_p[1] && di_buf->pd_config.global_mode != PULL_DOWN_BUF1) di_buf->pd_config.global_mode = PULL_DOWN_EI; } } } } static int do_post_wr_fun(void *arg, vframe_t *disp_vf) { di_post_stru.toggle_flag = true; return 1; } static int de_post_disable_fun(void *arg, vframe_t *disp_vf) { struct di_buf_s *di_buf = (struct di_buf_s *)arg; di_post_stru.vscale_skip_flag = false; di_post_stru.toggle_flag = true; process_vscale_skip(di_buf, disp_vf); /* for atv static image flickering */ if (di_buf->process_fun_index == PROCESS_FUN_NULL) disable_post_deinterlace_2(); return 1; /* called for new_format_flag, make * video set video_property_changed */ } void di_api_post_disable(void) { if (dil_get_diff_ver_flag()) return; di_print("%s:\n", __func__); disable_post_deinterlace_2(); } EXPORT_SYMBOL(di_api_post_disable); static int do_nothing_fun(void *arg, vframe_t *disp_vf) { struct di_buf_s *di_buf = (struct di_buf_s *)arg; di_post_stru.vscale_skip_flag = false; di_post_stru.toggle_flag = true; process_vscale_skip(di_buf, disp_vf); if (di_buf->process_fun_index == PROCESS_FUN_NULL) { if (Rd(DI_IF1_GEN_REG) & 0x1 || Rd(DI_POST_CTRL) & 0xf) disable_post_deinterlace_2(); /*if(di_buf->pulldown_mode == PULL_DOWN_EI && Rd(DI_IF1_GEN_REG)&0x1) * DI_VSYNC_WR_MPEG_REG(DI_IF1_GEN_REG, 0x3 << 30); */ } return 0; } static int do_pre_only_fun(void *arg, vframe_t *disp_vf) { di_post_stru.vscale_skip_flag = false; di_post_stru.toggle_flag = true; #ifdef DI_USE_FIXED_CANVAS_IDX if (arg) { struct di_buf_s *di_buf = (struct di_buf_s *)arg; vframe_t *vf = di_buf->vframe; int width, canvas_height; if ((vf == NULL) || (di_buf->di_buf[0] == NULL)) { di_print("error:%s,NULL point!!\n", __func__); return 0; } width = di_buf->di_buf[0]->canvas_width[NR_CANVAS]; /* linked two interlace buffer should double height*/ if (di_buf->di_buf[0]->di_wr_linked_buf) canvas_height = (di_buf->di_buf[0]->canvas_height << 1); else canvas_height = di_buf->di_buf[0]->canvas_height; #ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA if (is_vsync_rdma_enable()) { di_post_stru.canvas_id = di_post_stru.next_canvas_id; } else { di_post_stru.canvas_id = 0; di_post_stru.next_canvas_id = 1; if (post_wr_en && post_wr_support) di_post_stru.canvas_id = di_post_stru.next_canvas_id; } #endif canvas_config( di_post_idx[di_post_stru.canvas_id][0], di_buf->di_buf[0]->nr_adr, di_buf->di_buf[0]->canvas_width[NR_CANVAS], canvas_height, 0, 0); vf->canvas0Addr = di_post_idx[di_post_stru.canvas_id][0]; vf->canvas1Addr = di_post_idx[di_post_stru.canvas_id][0]; #ifdef DET3D if (di_pre_stru.vframe_interleave_flag && di_buf->di_buf[1]) { canvas_config( di_post_idx[di_post_stru.canvas_id][1], di_buf->di_buf[1]->nr_adr, di_buf->di_buf[1]->canvas_width[NR_CANVAS], canvas_height, 0, 0); vf->canvas1Addr = di_post_idx[di_post_stru.canvas_id][1]; vf->duration <<= 1; } #endif di_post_stru.next_canvas_id = di_post_stru.canvas_id ? 0 : 1; if (di_buf->process_fun_index == PROCESS_FUN_NULL) { if (Rd(DI_IF1_GEN_REG) & 0x1 || Rd(DI_POST_CTRL) & 0x10f) disable_post_deinterlace_2(); } } #endif return 0; } static void get_vscale_skip_count(unsigned int par) { di_vscale_skip_count_real = (par >> 24) & 0xff; } #define get_vpp_reg_update_flag(par) ((par >> 16) & 0x1) unsigned int di_last_display; static int de_post_process(void *arg, unsigned int zoom_start_x_lines, unsigned int zoom_end_x_lines, unsigned int zoom_start_y_lines, unsigned int zoom_end_y_lines, vframe_t *disp_vf) { struct di_buf_s *di_buf = (struct di_buf_s *)arg; struct di_buf_s *di_pldn_buf = NULL; unsigned int di_width, di_height, di_start_x, di_end_x, mv_offset; unsigned int di_start_y, di_end_y, hold_line = post_hold_line; unsigned int post_blend_en = 0, post_blend_mode = 0, blend_mtn_en = 0, ei_en = 0, post_field_num = 0; int di_vpp_en, di_ddr_en; unsigned char mc_pre_flag = 0; bool invert_mv = false; static int post_index = -1; #ifndef CONFIG_AMLOGIC_REMOVE_OLD unsigned char tmp_idx = 0; #endif post_cnt++; if (di_post_stru.vscale_skip_flag) return 0; get_vscale_skip_count(zoom_start_x_lines); if (IS_ERR_OR_NULL(di_buf)) return 0; else if (IS_ERR_OR_NULL(di_buf->di_buf_dup_p[0])) return 0; di_pldn_buf = di_buf->di_buf_dup_p[pldn_dly]; if (is_in_queue(di_buf, QUEUE_POST_FREE) && post_index != di_buf->index) { post_index = di_buf->index; pr_info("%s post_buf[%d] is in post free list.\n", __func__, di_buf->index); return 0; } if (di_post_stru.toggle_flag && di_buf->di_buf_dup_p[1]) top_bot_config(di_buf->di_buf_dup_p[1]); di_post_stru.toggle_flag = false; di_post_stru.cur_disp_index = di_buf->index; di_last_display = di_buf->index;/*tmp for keep buf*/ if (get_vpp_reg_update_flag(zoom_start_x_lines) || post_refresh) di_post_stru.update_post_reg_flag = 1; zoom_start_x_lines = zoom_start_x_lines & 0xffff; zoom_end_x_lines = zoom_end_x_lines & 0xffff; zoom_start_y_lines = zoom_start_y_lines & 0xffff; zoom_end_y_lines = zoom_end_y_lines & 0xffff; if (init_flag == 0 && IS_ERR_OR_NULL(di_post_stru.keep_buf)) return 0; ddbg_mod_save(eDI_DBG_MOD_POST_SETB, 0, frame_count);/*dbg*/ di_start_x = zoom_start_x_lines; di_end_x = zoom_end_x_lines; di_width = di_end_x - di_start_x + 1; di_start_y = zoom_start_y_lines; di_end_y = zoom_end_y_lines; di_height = di_end_y - di_start_y + 1; di_height = di_height / (di_vscale_skip_count_real + 1); /* make sure the height is even number */ if (di_height%2) { /*for skip mode,post only half line-1*/ if (((di_height > 150) && (di_height < 1080)) && di_vscale_skip_count_real) di_height = di_height - 3; else di_height++; } if (Rd(DI_POST_SIZE) != ((di_width - 1) | ((di_height - 1) << 16)) || di_post_stru.buf_type != di_buf->di_buf_dup_p[0]->type || (di_post_stru.di_buf0_mif.luma_x_start0 != di_start_x) || (di_post_stru.di_buf0_mif.luma_y_start0 != di_start_y / 2)) { ddbg_mod_save(eDI_DBG_MOD_POST_RESIZE, 0, frame_count);/*dbg*/ di_post_stru.buf_type = di_buf->di_buf_dup_p[0]->type; initial_di_post_2(di_width, di_height, hold_line, (post_wr_en && post_wr_support)); if ((di_buf->di_buf_dup_p[0]->vframe == NULL) || (di_buf->vframe == NULL)) return 0; /* bit mode config */ if (di_buf->vframe->bitdepth & BITDEPTH_Y10) { if (di_buf->vframe->type & VIDTYPE_VIU_444) { di_post_stru.di_buf0_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2; di_post_stru.di_buf1_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2; di_post_stru.di_buf2_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2; di_post_stru.di_diwr_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:2; } else { di_post_stru.di_buf0_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; di_post_stru.di_buf1_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; di_post_stru.di_buf2_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; di_post_stru.di_diwr_mif.bit_mode = (di_buf->vframe->bitdepth & FULL_PACK_422_MODE)?3:1; } } else { di_post_stru.di_buf0_mif.bit_mode = 0; di_post_stru.di_buf1_mif.bit_mode = 0; di_post_stru.di_buf2_mif.bit_mode = 0; di_post_stru.di_diwr_mif.bit_mode = 0; } if (di_buf->vframe->type & VIDTYPE_VIU_444) { di_post_stru.di_buf0_mif.video_mode = 1; di_post_stru.di_buf1_mif.video_mode = 1; di_post_stru.di_buf2_mif.video_mode = 1; } else { di_post_stru.di_buf0_mif.video_mode = 0; di_post_stru.di_buf1_mif.video_mode = 0; di_post_stru.di_buf2_mif.video_mode = 0; } if (di_post_stru.buf_type == VFRAME_TYPE_IN && !(di_buf->di_buf_dup_p[0]->vframe->type & VIDTYPE_VIU_FIELD)) { if (di_buf->vframe->type & VIDTYPE_VIU_NV21) { di_post_stru.di_buf0_mif.set_separate_en = 1; di_post_stru.di_buf1_mif.set_separate_en = 1; di_post_stru.di_buf2_mif.set_separate_en = 1; } else { di_post_stru.di_buf0_mif.set_separate_en = 0; di_post_stru.di_buf1_mif.set_separate_en = 0; di_post_stru.di_buf2_mif.set_separate_en = 0; } di_post_stru.di_buf0_mif.luma_y_start0 = di_start_y; di_post_stru.di_buf0_mif.luma_y_end0 = di_end_y; } else { /* from vdin or local vframe process by di pre */ di_post_stru.di_buf0_mif.set_separate_en = 0; di_post_stru.di_buf0_mif.luma_y_start0 = di_start_y >> 1; di_post_stru.di_buf0_mif.luma_y_end0 = di_end_y >> 1; di_post_stru.di_buf1_mif.set_separate_en = 0; di_post_stru.di_buf1_mif.luma_y_start0 = di_start_y >> 1; di_post_stru.di_buf1_mif.luma_y_end0 = di_end_y >> 1; di_post_stru.di_buf2_mif.set_separate_en = 0; di_post_stru.di_buf2_mif.luma_y_end0 = di_end_y >> 1; di_post_stru.di_buf2_mif.luma_y_start0 = di_start_y >> 1; } di_post_stru.di_buf0_mif.luma_x_start0 = di_start_x; di_post_stru.di_buf0_mif.luma_x_end0 = di_end_x; di_post_stru.di_buf1_mif.luma_x_start0 = di_start_x; di_post_stru.di_buf1_mif.luma_x_end0 = di_end_x; di_post_stru.di_buf2_mif.luma_x_start0 = di_start_x; di_post_stru.di_buf2_mif.luma_x_end0 = di_end_x; if (post_wr_en && post_wr_support) { if (de_devp->pps_enable && pps_position == 0) { di_pps_config(0, di_width, di_height, pps_dstw, pps_dsth); di_post_stru.di_diwr_mif.start_x = 0; di_post_stru.di_diwr_mif.end_x = pps_dstw - 1; di_post_stru.di_diwr_mif.start_y = 0; di_post_stru.di_diwr_mif.end_y = pps_dsth - 1; } else { di_post_stru.di_diwr_mif.start_x = di_start_x; di_post_stru.di_diwr_mif.end_x = di_end_x; di_post_stru.di_diwr_mif.start_y = di_start_y; di_post_stru.di_diwr_mif.end_y = di_end_y; } } di_post_stru.di_mtnprd_mif.start_x = di_start_x; di_post_stru.di_mtnprd_mif.end_x = di_end_x; di_post_stru.di_mtnprd_mif.start_y = di_start_y >> 1; di_post_stru.di_mtnprd_mif.end_y = di_end_y >> 1; if (mcpre_en) { di_post_stru.di_mcvecrd_mif.start_x = di_start_x / 5; mv_offset = (di_start_x % 5) ? (5 - di_start_x % 5) : 0; di_post_stru.di_mcvecrd_mif.vecrd_offset = overturn ? (di_end_x + 1) % 5 : mv_offset; di_post_stru.di_mcvecrd_mif.start_y = (di_start_y >> 1); di_post_stru.di_mcvecrd_mif.size_x = (di_end_x + 1 + 4) / 5 - 1 - di_start_x / 5; di_post_stru.di_mcvecrd_mif.end_y = (di_end_y >> 1); } di_post_stru.update_post_reg_flag = 1; /* if height decrease, mtn will not enough */ if ((di_buf->pd_config.global_mode != PULL_DOWN_BUF1) && !di_buf->di_buf_dup_p[2] && !post_wr_en) di_buf->pd_config.global_mode = PULL_DOWN_EI; } #ifdef DI_USE_FIXED_CANVAS_IDX #ifdef CONFIG_AMLOGIC_MEDIA_VSYNC_RDMA if (is_vsync_rdma_enable()) { di_post_stru.canvas_id = di_post_stru.next_canvas_id; } else { di_post_stru.canvas_id = 0; di_post_stru.next_canvas_id = 1; if (post_wr_en && post_wr_support) di_post_stru.canvas_id = di_post_stru.next_canvas_id; } #endif post_blend = di_buf->pd_config.global_mode; switch (post_blend) { case PULL_DOWN_BLEND_0: case PULL_DOWN_NORMAL: config_canvas_idx( di_buf->di_buf_dup_p[1], di_post_idx[di_post_stru.canvas_id][0], -1); config_canvas_idx( di_buf->di_buf_dup_p[2], -1, di_post_idx[di_post_stru.canvas_id][2]); config_canvas_idx( di_buf->di_buf_dup_p[0], di_post_idx[di_post_stru.canvas_id][1], -1); config_canvas_idx( di_buf->di_buf_dup_p[2], di_post_idx[di_post_stru.canvas_id][3], -1); if (mcpre_en) config_mcvec_canvas_idx( di_buf->di_buf_dup_p[2], di_post_idx[di_post_stru.canvas_id][4]); break; case PULL_DOWN_BLEND_2: case PULL_DOWN_NORMAL_2: config_canvas_idx( di_buf->di_buf_dup_p[0], di_post_idx[di_post_stru.canvas_id][3], -1); config_canvas_idx( di_buf->di_buf_dup_p[1], di_post_idx[di_post_stru.canvas_id][0], -1); config_canvas_idx( di_buf->di_buf_dup_p[2], -1, di_post_idx[di_post_stru.canvas_id][2]); config_canvas_idx( di_buf->di_buf_dup_p[2], di_post_idx[di_post_stru.canvas_id][1], -1); if (mcpre_en) config_mcvec_canvas_idx( di_buf->di_buf_dup_p[2], di_post_idx[di_post_stru.canvas_id][4]); break; case PULL_DOWN_MTN: config_canvas_idx( di_buf->di_buf_dup_p[1], di_post_idx[di_post_stru.canvas_id][0], -1); config_canvas_idx( di_buf->di_buf_dup_p[2], -1, di_post_idx[di_post_stru.canvas_id][2]); config_canvas_idx( di_buf->di_buf_dup_p[0], di_post_idx[di_post_stru.canvas_id][1], -1); break; case PULL_DOWN_BUF1:/* wave with buf1 */ config_canvas_idx( di_buf->di_buf_dup_p[1], di_post_idx[di_post_stru.canvas_id][0], -1); config_canvas_idx( di_buf->di_buf_dup_p[1], -1, di_post_idx[di_post_stru.canvas_id][2]); config_canvas_idx( di_buf->di_buf_dup_p[0], di_post_idx[di_post_stru.canvas_id][1], -1); break; case PULL_DOWN_EI: if (di_buf->di_buf_dup_p[1]) config_canvas_idx( di_buf->di_buf_dup_p[1], di_post_idx[di_post_stru.canvas_id][0], -1); break; default: break; } di_post_stru.next_canvas_id = di_post_stru.canvas_id ? 0 : 1; #endif if (di_buf->di_buf_dup_p[1] == NULL) return 0; if ((di_buf->di_buf_dup_p[1]->vframe == NULL) || di_buf->di_buf_dup_p[0]->vframe == NULL) return 0; #ifndef CONFIG_AMLOGIC_REMOVE_OLD if (is_meson_txl_cpu() && overturn && di_buf->di_buf_dup_p[2]) { /*sync from kernel 3.14 txl*/ if (post_blend == PULL_DOWN_BLEND_2) post_blend = PULL_DOWN_BLEND_0; else if (post_blend == PULL_DOWN_BLEND_0) post_blend = PULL_DOWN_BLEND_2; } #endif switch (post_blend) { case PULL_DOWN_BLEND_0: case PULL_DOWN_NORMAL: post_field_num = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; di_post_stru.di_buf0_mif.canvas0_addr0 = di_buf->di_buf_dup_p[1]->nr_canvas_idx; di_post_stru.di_buf1_mif.canvas0_addr0 = di_buf->di_buf_dup_p[0]->nr_canvas_idx; di_post_stru.di_buf2_mif.canvas0_addr0 = di_buf->di_buf_dup_p[2]->nr_canvas_idx; di_post_stru.di_mtnprd_mif.canvas_num = di_buf->di_buf_dup_p[2]->mtn_canvas_idx; //mc_pre_flag = is_meson_txl_cpu()?2:(overturn?0:1); #ifndef CONFIG_AMLOGIC_REMOVE_OLD if (is_meson_txl_cpu() && overturn) { /* swap if1&if2 mean negation of mv for normal di*/ tmp_idx = di_post_stru.di_buf1_mif.canvas0_addr0; di_post_stru.di_buf1_mif.canvas0_addr0 = di_post_stru.di_buf2_mif.canvas0_addr0; di_post_stru.di_buf2_mif.canvas0_addr0 = tmp_idx; } #endif mc_pre_flag = overturn?0:1; if (di_buf->pd_config.global_mode == PULL_DOWN_NORMAL) { post_blend_mode = 3; /*if pulldown, mcdi_mcpreflag is 1,*/ /*it means use previous field for MC*/ /*else not pulldown,mcdi_mcpreflag is 2*/ /*it means use forward & previous field for MC*/ if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD)) mc_pre_flag = 2; } else { if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD)) mc_pre_flag = 1; post_blend_mode = 1; } #ifndef CONFIG_AMLOGIC_REMOVE_OLD if (is_meson_txl_cpu() && overturn) mc_pre_flag = 1; #endif if (mcpre_en) { di_post_stru.di_mcvecrd_mif.canvas_num = di_buf->di_buf_dup_p[2]->mcvec_canvas_idx; } blend_mtn_en = 1; post_ei = ei_en = 1; post_blend_en = 1; break; case PULL_DOWN_BLEND_2: case PULL_DOWN_NORMAL_2: post_field_num = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; di_post_stru.di_buf0_mif.canvas0_addr0 = di_buf->di_buf_dup_p[1]->nr_canvas_idx; di_post_stru.di_buf1_mif.canvas0_addr0 = di_buf->di_buf_dup_p[2]->nr_canvas_idx; di_post_stru.di_buf2_mif.canvas0_addr0 = di_buf->di_buf_dup_p[0]->nr_canvas_idx; di_post_stru.di_mtnprd_mif.canvas_num = di_buf->di_buf_dup_p[2]->mtn_canvas_idx; #ifndef CONFIG_AMLOGIC_REMOVE_OLD if (is_meson_txl_cpu() && overturn) { di_post_stru.di_buf1_mif.canvas0_addr0 = di_post_stru.di_buf2_mif.canvas0_addr0; } #endif if (mcpre_en) { di_post_stru.di_mcvecrd_mif.canvas_num = di_buf->di_buf_dup_p[2]->mcvec_canvas_idx; mc_pre_flag = is_meson_txl_cpu()?0:(overturn?1:0); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) invert_mv = true; else if (!overturn) di_post_stru.di_buf2_mif.canvas0_addr0 = di_buf->di_buf_dup_p[2]->nr_canvas_idx; } if (di_buf->pd_config.global_mode == PULL_DOWN_NORMAL_2) { post_blend_mode = 3; /*if pulldown, mcdi_mcpreflag is 1,*/ /*it means use previous field for MC*/ /*else not pulldown,mcdi_mcpreflag is 2*/ /*it means use forward & previous field for MC*/ if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD)) mc_pre_flag = 2; } else { if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD)) mc_pre_flag = 1; post_blend_mode = 1; } blend_mtn_en = 1; post_ei = ei_en = 1; post_blend_en = 1; break; case PULL_DOWN_MTN: post_field_num = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; di_post_stru.di_buf0_mif.canvas0_addr0 = di_buf->di_buf_dup_p[1]->nr_canvas_idx; di_post_stru.di_buf1_mif.canvas0_addr0 = di_buf->di_buf_dup_p[0]->nr_canvas_idx; di_post_stru.di_mtnprd_mif.canvas_num = di_buf->di_buf_dup_p[2]->mtn_canvas_idx; post_blend_mode = 0; blend_mtn_en = 1; post_ei = ei_en = 1; post_blend_en = 1; break; case PULL_DOWN_BUF1: post_field_num = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; di_post_stru.di_buf0_mif.canvas0_addr0 = di_buf->di_buf_dup_p[1]->nr_canvas_idx; di_post_stru.di_mtnprd_mif.canvas_num = di_buf->di_buf_dup_p[1]->mtn_canvas_idx; di_post_stru.di_buf1_mif.canvas0_addr0 = di_buf->di_buf_dup_p[0]->nr_canvas_idx; post_blend_mode = 1; blend_mtn_en = 0; post_ei = ei_en = 0; post_blend_en = 0; break; case PULL_DOWN_EI: if (di_buf->di_buf_dup_p[1]) { di_post_stru.di_buf0_mif.canvas0_addr0 = di_buf->di_buf_dup_p[1]->nr_canvas_idx; post_field_num = (di_buf->di_buf_dup_p[1]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; } else { post_field_num = (di_buf->di_buf_dup_p[0]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_TOP ? 0 : 1; di_post_stru.di_buf0_mif.src_field_mode = post_field_num; } post_blend_mode = 2; blend_mtn_en = 0; post_ei = ei_en = 1; post_blend_en = 0; break; default: break; } if (post_wr_en && post_wr_support) { config_canvas_idx(di_buf, di_post_idx[di_post_stru.canvas_id][5], -1); di_post_stru.di_diwr_mif.canvas_num = di_buf->nr_canvas_idx; di_vpp_en = 0; di_ddr_en = 1; } else { di_vpp_en = 1; di_ddr_en = 0; } /* if post size < MIN_POST_WIDTH, force ei */ if ((di_width < MIN_BLEND_WIDTH) && (di_buf->pd_config.global_mode == PULL_DOWN_BLEND_0 || di_buf->pd_config.global_mode == PULL_DOWN_BLEND_2 || di_buf->pd_config.global_mode == PULL_DOWN_NORMAL )) { post_blend_mode = 1; blend_mtn_en = 0; post_ei = ei_en = 0; post_blend_en = 0; } if (mcpre_en) di_post_stru.di_mcvecrd_mif.blend_en = post_blend_en; invert_mv = overturn ? (!invert_mv) : invert_mv; if (di_post_stru.update_post_reg_flag) { enable_di_post_2( &di_post_stru.di_buf0_mif, &di_post_stru.di_buf1_mif, &di_post_stru.di_buf2_mif, &di_post_stru.di_diwr_mif, &di_post_stru.di_mtnprd_mif, ei_en, /* ei enable */ post_blend_en, /* blend enable */ blend_mtn_en, /* blend mtn enable */ post_blend_mode, /* blend mode. */ di_vpp_en, /* di_vpp_en. */ di_ddr_en, /* di_ddr_en. */ post_field_num, /* 1 bottom generate top */ hold_line, post_urgent, (invert_mv?1:0), di_vscale_skip_count_real ); if (mcpre_en) { if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) enable_mc_di_post_g12( &di_post_stru.di_mcvecrd_mif, post_urgent, overturn, (invert_mv?1:0)); else enable_mc_di_post( &di_post_stru.di_mcvecrd_mif, post_urgent, overturn, (invert_mv?1:0)); } else if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX)) DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2); } else { di_post_switch_buffer( &di_post_stru.di_buf0_mif, &di_post_stru.di_buf1_mif, &di_post_stru.di_buf2_mif, &di_post_stru.di_diwr_mif, &di_post_stru.di_mtnprd_mif, &di_post_stru.di_mcvecrd_mif, ei_en, /* ei enable */ post_blend_en, /* blend enable */ blend_mtn_en, /* blend mtn enable */ post_blend_mode, /* blend mode. */ di_vpp_en, /* di_vpp_en. */ di_ddr_en, /* di_ddr_en. */ post_field_num, /* 1 bottom generate top */ hold_line, post_urgent, (invert_mv?1:0), pulldown_enable, mcpre_en, di_vscale_skip_count_real ); } if (is_meson_gxtvbb_cpu() || is_meson_txl_cpu() || is_meson_txlx_cpu() || is_meson_gxlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu()) { di_post_read_reverse_irq(overturn, mc_pre_flag, post_blend_en ? mcpre_en : false); /* disable mc for first 2 fieldes mv unreliable */ if (di_buf->seq < 2) DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2); } if (mcpre_en) { if (di_buf->di_buf_dup_p[2]) set_post_mcinfo(&di_buf->di_buf_dup_p[2] ->curr_field_mcinfo); } else if (is_meson_gxlx_cpu() || is_meson_txl_cpu() || is_meson_txlx_cpu()) DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 2); /* set pull down region (f(t-1) */ if (di_pldn_buf && pulldown_enable && !di_pre_stru.cur_prog_flag) { unsigned short offset = (di_start_y>>1); if (overturn) offset = ((di_buf->vframe->height - di_end_y)>>1); else offset = 0; pulldown_vof_win_vshift(&di_pldn_buf->pd_config, offset); pulldown_vof_win_config(&di_pldn_buf->pd_config); } if (di_post_stru.update_post_reg_flag > 0) di_post_stru.update_post_reg_flag--; ddbg_mod_save(eDI_DBG_MOD_POST_SETE, 0, frame_count);/*dbg*/ return 0; } static void post_de_done_buf_config(void) { ulong irq_flag2 = 0; struct di_buf_s *di_buf = NULL; if (di_post_stru.cur_post_buf == NULL) return; ddbg_mod_save(eDI_DBG_MOD_POST_DB, 0, frame_count);/*dbg*/ di_lock_irqfiq_save(irq_flag2); queue_out(di_post_stru.cur_post_buf); di_buf = di_post_stru.cur_post_buf; if (de_devp->pps_enable && pps_position == 0) { di_buf->vframe->width = pps_dstw; di_buf->vframe->height = pps_dsth; } queue_in(di_post_stru.cur_post_buf, QUEUE_POST_READY); di_unlock_irqfiq_restore(irq_flag2); vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); di_post_stru.cur_post_buf = NULL; ddbg_mod_save(eDI_DBG_MOD_POST_DE, 0, frame_count);/*dbg*/ } static void di_post_process(void) { struct di_buf_s *di_buf = NULL; vframe_t *vf_p = NULL; if (di_post_stru.post_de_busy) return; if (queue_empty(QUEUE_POST_DOING)) { di_post_stru.post_peek_underflow++; return; } di_buf = get_di_buf_head(QUEUE_POST_DOING); if (check_di_buf(di_buf, 20)) return; vf_p = di_buf->vframe; if (di_post_stru.run_early_proc_fun_flag) { if (vf_p->early_process_fun) vf_p->early_process_fun = do_post_wr_fun; } if (di_buf->process_fun_index) { di_post_stru.post_wr_cnt++; de_post_process( di_buf, 0, vf_p->width-1, 0, vf_p->height-1, vf_p); di_post_stru.post_de_busy = 1; di_post_stru.irq_time = cur_to_msecs(); } else { di_post_stru.de_post_process_done = 1; } di_post_stru.cur_post_buf = di_buf; } static void recycle_vframe_type_post(struct di_buf_s *di_buf) { int i; if (di_buf == NULL) { pr_dbg("%s:Error\n", __func__); if (recovery_flag == 0) recovery_log_reason = 15; recovery_flag++; return; } if (di_buf->process_fun_index == PROCESS_FUN_DI) dec_post_ref_count(di_buf); for (i = 0; i < 2; i++) { if (di_buf->di_buf[i]) queue_in(di_buf->di_buf[i], QUEUE_RECYCLE); } queue_out(di_buf); /* remove it from display_list_head */ di_buf->invert_top_bot_flag = 0; queue_in(di_buf, QUEUE_POST_FREE); } #ifdef DI_BUFFER_DEBUG static void recycle_vframe_type_post_print(struct di_buf_s *di_buf, const char *func, const int line) { int i; di_print("%s:%d ", func, line); for (i = 0; i < 2; i++) { if (di_buf->di_buf[i]) di_print("%s[%d]<%d>=>recycle_list; ", vframe_type_name[di_buf->di_buf[i]->type], di_buf->di_buf[i]->index, i); } di_print("%s[%d] =>post_free_list\n", vframe_type_name[di_buf->type], di_buf->index); } #endif static void set_pulldown_mode(struct di_buf_s *di_buf) { struct di_buf_s *pre_buf_p = di_buf->di_buf_dup_p[pldn_dly1]; if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXBB)) { if (pulldown_enable && !di_pre_stru.cur_prog_flag) { if (pre_buf_p) { di_buf->pd_config.global_mode = pre_buf_p->pd_config.global_mode; } else { pr_err("DI[%s]: index out of range.\n", __func__); } } else { di_buf->pd_config.global_mode = PULL_DOWN_NORMAL; } } } void drop_frame(int check_drop, int throw_flag, struct di_buf_s *di_buf) { ulong irq_flag2 = 0; int i = 0, drop_flag = 0; di_lock_irqfiq_save(irq_flag2); if ((frame_count == 0) && check_drop) di_post_stru.start_pts = di_buf->vframe->pts; if ((check_drop && (frame_count < start_frame_drop_count)) || throw_flag) { drop_flag = 1; } else { if (check_drop && (frame_count == start_frame_drop_count)) { if ((di_post_stru.start_pts) && (di_buf->vframe->pts == 0)) di_buf->vframe->pts = di_post_stru.start_pts; di_post_stru.start_pts = 0; } for (i = 0; i < 3; i++) { if (di_buf->di_buf_dup_p[i]) { if (di_buf->di_buf_dup_p[i]->vframe->bitdepth != di_buf->vframe->bitdepth) { pr_info("%s buf[%d] not match bit mode\n", __func__, i); drop_flag = 1; break; } } } } if (drop_flag) { queue_in(di_buf, QUEUE_TMP); recycle_vframe_type_post(di_buf); #ifdef DI_BUFFER_DEBUG recycle_vframe_type_post_print( di_buf, __func__, __LINE__); #endif } else { if (post_wr_en && post_wr_support) queue_in(di_buf, QUEUE_POST_DOING); else queue_in(di_buf, QUEUE_POST_READY); di_print("DI:%dth %s[%d] => post ready %u ms.\n", frame_count, vframe_type_name[di_buf->type], di_buf->index, jiffies_to_msecs(jiffies_64 - di_buf->vframe->ready_jiffies64)); } di_unlock_irqfiq_restore(irq_flag2); } static int process_post_vframe(void) { /* * 1) get buf from post_free_list, config it according to buf * in pre_ready_list, send it to post_ready_list * (it will be send to post_free_list in di_vf_put()) * 2) get buf from pre_ready_list, attach it to buf from post_free_list * (it will be send to recycle_list in di_vf_put() ) */ ulong irq_flag2 = 0; int i = 0; int ret = 0; int buffer_keep_count = 3; struct di_buf_s *di_buf = NULL; struct di_buf_s *ready_di_buf; struct di_buf_s *p = NULL;/* , *ptmp; */ int itmp; int ready_count = list_count(QUEUE_PRE_READY); bool check_drop = false; bool di_afbc = false; if (queue_empty(QUEUE_POST_FREE)) return 0; if (ready_count == 0) return 0; ready_di_buf = get_di_buf_head(QUEUE_PRE_READY); if ((ready_di_buf == NULL) || (ready_di_buf->vframe == NULL)) { pr_dbg("%s:Error1\n", __func__); if (recovery_flag == 0) recovery_log_reason = 16; recovery_flag++; return 0; } if (ready_di_buf->vframe->type & VIDTYPE_PRE_DI_AFBC) { di_afbc = true; /*pr_info("di afbc mode 0x%x\n", ready_di_buf->vframe->type);*/ } if ((ready_di_buf->post_proc_flag) && (ready_count >= buffer_keep_count)) { i = 0; queue_for_each_entry(p, ptmp, QUEUE_PRE_READY, list) { /* if(p->post_proc_flag == 0){ */ if (p->type == VFRAME_TYPE_IN) { ready_di_buf->post_proc_flag = -1; ready_di_buf->new_format_flag = 1; } i++; if (i > 2) break; } } if (ready_di_buf->post_proc_flag > 0) { if (ready_count >= buffer_keep_count) { di_lock_irqfiq_save(irq_flag2); di_buf = get_di_buf_head(QUEUE_POST_FREE); if (check_di_buf(di_buf, 17)) { di_unlock_irqfiq_restore(irq_flag2); return 0; } queue_out(di_buf); di_unlock_irqfiq_restore(irq_flag2); i = 0; queue_for_each_entry( p, ptmp, QUEUE_PRE_READY, list) { di_buf->di_buf_dup_p[i++] = p; if (i >= buffer_keep_count) break; } if (i < buffer_keep_count) { pr_dbg("%s:Error3\n", __func__); if (recovery_flag == 0) recovery_log_reason = 18; recovery_flag++; return 0; } memcpy(di_buf->vframe, di_buf->di_buf_dup_p[1]->vframe, sizeof(vframe_t)); di_buf->vframe->private_data = di_buf; if (di_buf->di_buf_dup_p[1]->post_proc_flag == 3) { /* dummy, not for display */ inc_post_ref_count(di_buf); di_buf->di_buf[0] = di_buf->di_buf_dup_p[0]; di_buf->di_buf[1] = NULL; queue_out(di_buf->di_buf[0]); di_lock_irqfiq_save(irq_flag2); queue_in(di_buf, QUEUE_TMP); recycle_vframe_type_post(di_buf); di_unlock_irqfiq_restore(irq_flag2); #ifdef DI_BUFFER_DEBUG di_print("%s : ", __func__); #endif } else { if (di_buf->di_buf_dup_p[1]-> post_proc_flag == 2){ di_buf->pd_config.global_mode = PULL_DOWN_BLEND_2; /* blend with di_buf->di_buf_dup_p[2] */ } else { set_pulldown_mode(di_buf); } di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD | VIDTYPE_PRE_INTERLACE; if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; di_buf->vframe->width = di_buf->di_buf_dup_p[1]->width_bk; if ( di_buf->di_buf_dup_p[1]-> new_format_flag) { /* if (di_buf->di_buf_dup_p[1] * ->post_proc_flag == 2) { */ di_buf->vframe-> early_process_fun = de_post_disable_fun; } else { di_buf->vframe-> early_process_fun = do_nothing_fun; } if (di_buf->di_buf_dup_p[1]->type == VFRAME_TYPE_IN) { /* next will be bypass */ di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD | VIDTYPE_PRE_INTERLACE; if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; di_buf->vframe->height >>= 1; di_buf->vframe->canvas0Addr = di_buf->di_buf_dup_p[0] ->nr_canvas_idx; /* top */ di_buf->vframe->canvas1Addr = di_buf->di_buf_dup_p[0] ->nr_canvas_idx; di_buf->vframe->process_fun = NULL; di_buf->process_fun_index = PROCESS_FUN_NULL; } else { /*for debug*/ if (debug_blend_mode != -1) di_buf->pd_config.global_mode = debug_blend_mode; di_buf->vframe->process_fun = ((post_wr_en && post_wr_support) ? NULL : de_post_process); di_buf->process_fun_index = PROCESS_FUN_DI; inc_post_ref_count(di_buf); } di_buf->di_buf[0] = di_buf->di_buf_dup_p[0]; di_buf->di_buf[1] = NULL; queue_out(di_buf->di_buf[0]); drop_frame(true, (di_buf->di_buf_dup_p[0]->throw_flag) || (di_buf->di_buf_dup_p[1]->throw_flag) || (di_buf->di_buf_dup_p[2]->throw_flag), di_buf); frame_count++; #ifdef DI_BUFFER_DEBUG di_print("%s : ", __func__); #endif if (!(post_wr_en && post_wr_support)) vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); } ret = 1; } } else { if (is_progressive(ready_di_buf->vframe) || ready_di_buf->type == VFRAME_TYPE_IN || ready_di_buf->post_proc_flag < 0 || bypass_post_state ){ int vframe_process_count = 1; #ifdef DET3D int dual_vframe_flag = 0; if ((di_pre_stru.vframe_interleave_flag && ready_di_buf->left_right) || (bypass_post & 0x100)) { dual_vframe_flag = 1; vframe_process_count = 2; } #endif if (skip_top_bot && (!is_progressive(ready_di_buf->vframe))) vframe_process_count = 2; if (ready_count >= vframe_process_count) { struct di_buf_s *di_buf_i; di_lock_irqfiq_save(irq_flag2); di_buf = get_di_buf_head(QUEUE_POST_FREE); if (check_di_buf(di_buf, 19)) { di_unlock_irqfiq_restore(irq_flag2); return 0; } queue_out(di_buf); di_unlock_irqfiq_restore(irq_flag2); i = 0; queue_for_each_entry( p, ptmp, QUEUE_PRE_READY, list){ di_buf->di_buf_dup_p[i++] = p; if (i >= vframe_process_count) { di_buf->di_buf_dup_p[i] = NULL; di_buf->di_buf_dup_p[i+1] = NULL; break; } } if (i < vframe_process_count) { pr_dbg("%s:Error6\n", __func__); if (recovery_flag == 0) recovery_log_reason = 22; recovery_flag++; return 0; } di_buf_i = di_buf->di_buf_dup_p[0]; if (!is_progressive(ready_di_buf->vframe) && ((skip_top_bot == 1) || (skip_top_bot == 2))) { unsigned int frame_type = di_buf->di_buf_dup_p[1]-> vframe->type & VIDTYPE_TYPEMASK; if (skip_top_bot == 1) { di_buf_i = (frame_type == VIDTYPE_INTERLACE_TOP) ? di_buf->di_buf_dup_p[1] : di_buf->di_buf_dup_p[0]; } else if (skip_top_bot == 2) { di_buf_i = (frame_type == VIDTYPE_INTERLACE_BOTTOM) ? di_buf->di_buf_dup_p[1] : di_buf->di_buf_dup_p[0]; } } memcpy(di_buf->vframe, di_buf_i->vframe, sizeof(vframe_t)); di_buf->vframe->width = di_buf_i->width_bk; di_buf->vframe->private_data = di_buf; if (ready_di_buf->new_format_flag && (ready_di_buf->type == VFRAME_TYPE_IN)) { pr_info("DI:%d disable post.\n", __LINE__); di_buf->vframe->early_process_fun = de_post_disable_fun; } else { if (ready_di_buf->type == VFRAME_TYPE_IN) di_buf->vframe-> early_process_fun = do_nothing_fun; else di_buf->vframe-> early_process_fun = do_pre_only_fun; } if (ready_di_buf->post_proc_flag == -2) { di_buf->vframe->type |= VIDTYPE_VIU_FIELD; di_buf->vframe->type &= ~(VIDTYPE_TYPEMASK); if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; di_buf->vframe->process_fun = (post_wr_en && post_wr_support)?NULL:de_post_process; di_buf->process_fun_index = PROCESS_FUN_DI; di_buf->pd_config.global_mode = PULL_DOWN_EI; } else { di_buf->vframe->process_fun = NULL; di_buf->process_fun_index = PROCESS_FUN_NULL; di_buf->pd_config.global_mode = PULL_DOWN_NORMAL; } di_buf->di_buf[0] = ready_di_buf; di_buf->di_buf[1] = NULL; queue_out(ready_di_buf); #ifdef DET3D if (dual_vframe_flag) { di_buf->di_buf[1] = di_buf->di_buf_dup_p[1]; queue_out(di_buf->di_buf[1]); } #endif drop_frame(check_drop, di_buf->di_buf[0]->throw_flag, di_buf); frame_count++; #ifdef DI_BUFFER_DEBUG di_print( "%s : ", __func__); #endif ret = 1; vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); } } else if (ready_count >= 2) { /*for progressive input,type * 1:separate tow fields,type * 2:bypass post as frame */ unsigned char prog_tb_field_proc_type = (prog_proc_config >> 1) & 0x3; di_lock_irqfiq_save(irq_flag2); di_buf = get_di_buf_head(QUEUE_POST_FREE); if (check_di_buf(di_buf, 20)) { di_unlock_irqfiq_restore(irq_flag2); return 0; } queue_out(di_buf); di_unlock_irqfiq_restore(irq_flag2); i = 0; queue_for_each_entry( p, ptmp, QUEUE_PRE_READY, list) { di_buf->di_buf_dup_p[i++] = p; if (i >= 2) { di_buf->di_buf_dup_p[i] = NULL; break; } } if (i < 2) { pr_dbg("%s:Error6\n", __func__); if (recovery_flag == 0) recovery_log_reason = 21; recovery_flag++; return 0; } memcpy(di_buf->vframe, di_buf->di_buf_dup_p[0]->vframe, sizeof(vframe_t)); di_buf->vframe->private_data = di_buf; /*separate one progressive frame * as two interlace fields */ if (prog_tb_field_proc_type == 1) { /* do weave by di post */ di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD | VIDTYPE_PRE_INTERLACE; if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; if ( di_buf->di_buf_dup_p[0]-> new_format_flag) di_buf->vframe-> early_process_fun = de_post_disable_fun; else di_buf->vframe-> early_process_fun = do_nothing_fun; di_buf->pd_config.global_mode = PULL_DOWN_BUF1; di_buf->vframe->process_fun = (post_wr_en && post_wr_support)?NULL:de_post_process; di_buf->process_fun_index = PROCESS_FUN_DI; } else if (prog_tb_field_proc_type == 0) { /* to do: need change for * DI_USE_FIXED_CANVAS_IDX */ /* do weave by vpp */ di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE; if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; if ( (di_buf->di_buf_dup_p[0]-> new_format_flag) || (Rd(DI_IF1_GEN_REG) & 1)) di_buf->vframe-> early_process_fun = de_post_disable_fun; else di_buf->vframe-> early_process_fun = do_nothing_fun; di_buf->vframe->process_fun = NULL; di_buf->process_fun_index = PROCESS_FUN_NULL; di_buf->vframe->canvas0Addr = di_buf->di_buf_dup_p[0]-> nr_canvas_idx; di_buf->vframe->canvas1Addr = di_buf->di_buf_dup_p[1]-> nr_canvas_idx; } else { /* to do: need change for * DI_USE_FIXED_CANVAS_IDX */ di_buf->vframe->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_422 | VIDTYPE_VIU_SINGLE_PLANE | VIDTYPE_VIU_FIELD | VIDTYPE_PRE_INTERLACE; if (di_afbc) di_buf->vframe->type |= VIDTYPE_PRE_DI_AFBC; di_buf->vframe->height >>= 1; di_buf->vframe->width = di_buf->di_buf_dup_p[0]->width_bk; if ( (di_buf->di_buf_dup_p[0]-> new_format_flag) || (Rd(DI_IF1_GEN_REG) & 1)) di_buf->vframe-> early_process_fun = de_post_disable_fun; else di_buf->vframe-> early_process_fun = do_nothing_fun; if (prog_tb_field_proc_type == 2) { di_buf->vframe->canvas0Addr = di_buf->di_buf_dup_p[0] ->nr_canvas_idx; /* top */ di_buf->vframe->canvas1Addr = di_buf->di_buf_dup_p[0] ->nr_canvas_idx; } else { di_buf->vframe->canvas0Addr = di_buf->di_buf_dup_p[1] ->nr_canvas_idx; /* top */ di_buf->vframe->canvas1Addr = di_buf->di_buf_dup_p[1] ->nr_canvas_idx; } } di_buf->di_buf[0] = di_buf->di_buf_dup_p[0]; queue_out(di_buf->di_buf[0]); /*check if the field is error,then drop*/ if ( (di_buf->di_buf_dup_p[0]->vframe->type & VIDTYPE_TYPEMASK) == VIDTYPE_INTERLACE_BOTTOM) { di_buf->di_buf[1] = di_buf->di_buf_dup_p[1] = NULL; queue_in(di_buf, QUEUE_TMP); recycle_vframe_type_post(di_buf); pr_dbg("%s drop field %d.\n", __func__, di_buf->di_buf_dup_p[0]->seq); } else { di_buf->di_buf[1] = di_buf->di_buf_dup_p[1]; queue_out(di_buf->di_buf[1]); drop_frame(check_start_drop_prog, (di_buf->di_buf_dup_p[0]->throw_flag) || (di_buf->di_buf_dup_p[1]->throw_flag), di_buf); } frame_count++; #ifdef DI_BUFFER_DEBUG di_print("%s : ", __func__); #endif ret = 1; vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); } } #ifdef DI_BUFFER_DEBUG if (di_buf) { di_print("%s[%d](", vframe_type_name[di_buf->type], di_buf->index); for (i = 0; i < 2; i++) { if (di_buf->di_buf[i]) di_print("%s[%d],", vframe_type_name[di_buf->di_buf[i]->type], di_buf->di_buf[i]->index); } di_print(")(vframe type %x dur %d)", di_buf->vframe->type, di_buf->vframe->duration); if (di_buf->di_buf_dup_p[1] && (di_buf->di_buf_dup_p[1]->post_proc_flag == 3)) di_print("=> recycle_list\n"); else di_print("=> post_ready_list\n"); } #endif return ret; } void di_vpu_dev_register(struct di_dev_s *vdevp) { vdevp->di_vpu_clk_gate_dev = vpu_dev_register(VPU_VPU_CLKB, DEVICE_NAME); vdevp->di_vpu_pd_dec = vpu_dev_register(VPU_AFBC_DEC, DEVICE_NAME); vdevp->di_vpu_pd_dec1 = vpu_dev_register(VPU_AFBC_DEC1, DEVICE_NAME); vdevp->di_vpu_pd_vd1 = vpu_dev_register(VPU_VIU_VD1, DEVICE_NAME); vdevp->di_vpu_pd_post = vpu_dev_register(VPU_DI_POST, DEVICE_NAME); } /* * di task */ static void di_unreg_process(void) { unsigned long start_jiffes = 0; if (reg_flag) { pr_dbg("%s unreg start %d.\n", __func__, reg_flag); start_jiffes = jiffies_64; vf_unreg_provider(&di_vf_prov); pr_dbg("%s vf unreg cost %u ms.\n", __func__, jiffies_to_msecs(jiffies_64 - start_jiffes)); unreg_cnt++; if (unreg_cnt > 0x3fffffff) unreg_cnt = 0; pr_dbg("%s unreg stop %d.\n", __func__, reg_flag); di_pre_stru.unreg_req_flag_irq = 1; reg_flag = 0; trigger_pre_di_process(TRIGGER_PRE_BY_UNREG); } else { di_pre_stru.force_unreg_req_flag = 0; di_pre_stru.disable_req_flag = 0; recovery_flag = 0; di_pre_stru.unreg_req_flag = 0; trigger_pre_di_process(TRIGGER_PRE_BY_UNREG); } } static void di_unreg_process_irq(void) { ulong irq_flag2 = 0; unsigned int mirror_disable = 0; #if (defined ENABLE_SPIN_LOCK_ALWAYS) ulong flags = 0; spin_lock_irqsave(&plist_lock, flags); #endif init_flag = 0; mirror_disable = get_blackout_policy(); di_lock_irqfiq_save(irq_flag2); di_print("%s: di_uninit_buf\n", __func__); di_uninit_buf(mirror_disable); #ifdef CONFIG_AMLOGIC_MEDIA_RDMA if (di_pre_rdma_enable) rdma_clear(de_devp->rdma_handle); #endif adpative_combing_exit(); enable_di_pre_mif(false, mcpre_en); /*disable afbc module when afbc working in DI*/ afbc_reg_unreg_flag = 0; #if 0 if (IS_COMP_MODE(di_pre_stru.cur_inp_type) && (!needbypass_flag && !isbypass_flag)) { pr_info("DI: disable afbc\n"); afbc_reg_sw(false); afbc_input_sw(false); } #endif di_hw_uninit(); if (is_meson_txlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_sm1_cpu() || is_meson_tm2_cpu()) { di_pre_gate_control(false, mcpre_en); di_nr_opl()->nr_gate_control(false); } else if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXTVBB)) { DI_Wr(DI_CLKG_CTRL, 0x80f60000); DI_Wr(DI_PRE_CTRL, 0); } #ifndef CONFIG_AMLOGIC_REMOVE_OLD else DI_Wr(DI_CLKG_CTRL, 0xf60000); #endif /* nr/blend0/ei0/mtn0 clock gate */ if (mirror_disable) { di_hw_disable(mcpre_en); if (is_meson_txlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_sm1_cpu() || is_meson_tm2_cpu()) { enable_di_post_mif(GATE_OFF); di_post_gate_control(false); di_top_gate_control(false, false); } else { DI_Wr(DI_CLKG_CTRL, 0x80000000); } if (!is_meson_gxl_cpu() && !is_meson_gxm_cpu() && !is_meson_gxbb_cpu() && !is_meson_txlx_cpu()) vpu_dev_clk_gate_off(de_devp->di_vpu_clk_gate_dev); pr_info("%s disable di mirror image.\n", __func__); } if (post_wr_en && post_wr_support) diwr_set_power_control(0); di_unlock_irqfiq_restore(irq_flag2); #if (defined ENABLE_SPIN_LOCK_ALWAYS) spin_unlock_irqrestore(&plist_lock, flags); #endif di_patch_post_update_mc_sw(DI_MC_SW_REG, false); di_pre_stru.force_unreg_req_flag = 0; di_pre_stru.disable_req_flag = 0; recovery_flag = 0; di_pre_stru.cur_prog_flag = 0; di_pre_stru.unreg_req_flag = 0; di_pre_stru.unreg_req_flag_irq = 0; #ifdef CONFIG_CMA if (de_devp->flag_cma == 1 || (de_devp->flag_cma == 3) || (de_devp->flag_cma == 4)) { pr_dbg("%s:cma release req time: %d ms\n", __func__, jiffies_to_msecs(jiffies)); di_pre_stru.cma_release_req = 1; up(&di_sema); } #endif /*dbg*/ pr_info("di:retry cnt=%d\n", di_pre_stru.retry_cnt); } static void di_reg_process(void) { /*get vout information first time*/ if (reg_flag == 1) return; vf_reg_provider(&di_vf_prov); vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_START, NULL); reg_flag = 1; di_pre_stru.bypass_flag = false; reg_cnt++; if (reg_cnt > 0x3fffffff) reg_cnt = 0; di_print("########%s\n", __func__); } #ifdef CONFIG_AMLOGIC_MEDIA_RDMA /* di pre rdma operation */ static void di_rdma_irq(void *arg) { struct di_dev_s *di_devp = (struct di_dev_s *)arg; if (IS_ERR_OR_NULL(di_devp)) return; if (di_devp->rdma_handle <= 0) { pr_err("%s rdma handle %d error.\n", __func__, di_devp->rdma_handle); return; } if (di_printk_flag) pr_dbg("%s...%d.\n", __func__, di_pre_stru.field_count_for_cont); } static struct rdma_op_s di_rdma_op = { di_rdma_irq, NULL }; #endif static void di_load_pq_table(void) { struct di_pq_parm_s *pos = NULL, *tmp = NULL; if (atomic_read(&de_devp->pq_flag) == 0 && (de_devp->flags & DI_LOAD_REG_FLAG)) { atomic_set(&de_devp->pq_flag, 1); list_for_each_entry_safe(pos, tmp, &de_devp->pq_table_list, list) { di_load_regs(pos); list_del(&pos->list); di_pq_parm_destroy(pos); } de_devp->flags &= ~DI_LOAD_REG_FLAG; atomic_set(&de_devp->pq_flag, 0); } } static void di_pre_size_change(unsigned short width, unsigned short height, unsigned short vf_type) { unsigned int blkhsize = 0; int pps_w = 0, pps_h = 0; struct nr_cfg_s cfg_data; struct nr_cfg_s *cfg = &cfg_data; cfg->width = width; cfg->height = height; cfg->linkflag = 0; di_nr_opl()->nr_all_config(vf_type, cfg); #ifdef DET3D det3d_config(det3d_en ? 1 : 0); #endif if (pulldown_enable) { pulldown_init(width, height); init_field_mode(height); if (is_meson_txl_cpu() || is_meson_txlx_cpu() || is_meson_gxlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu()) film_mode_win_config(width, height); } if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL)) combing_pd22_window_config(width, height); RDMA_WR(DI_PRE_SIZE, (width - 1) | ((height - 1) << 16)); if (mcpre_en) { blkhsize = (width + 4) / 5; RDMA_WR(MCDI_HV_SIZEIN, height | (width << 16)); RDMA_WR(MCDI_HV_BLKSIZEIN, (overturn ? 3 : 0) << 30 | blkhsize << 16 | height); RDMA_WR(MCDI_BLKTOTAL, blkhsize * height); if (is_meson_gxlx_cpu()) { RDMA_WR(MCDI_PD_22_CHK_FLG_CNT, 0); RDMA_WR(MCDI_FIELD_MV, 0); } } di_load_pq_table(); if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) RDMA_WR(DI_PRE_GL_CTRL, 0x80000005); if (de_devp->nrds_enable) nr_ds_init(width, height); if (de_devp->pps_enable && pps_position) { pps_w = di_pre_stru.cur_width; pps_h = di_pre_stru.cur_height>>1; di_pps_config(1, pps_w, pps_h, pps_dstw, (pps_dsth>>1)); } if (de_devp->h_sc_down_en) { pps_w = di_pre_stru.cur_width; di_inp_hsc_setting(pps_w, pre_hsc_down_width); } else { di_inp_hsc_setting(di_pre_stru.cur_width, di_pre_stru.cur_width); } di_interrupt_ctrl(di_pre_stru.madi_enable, det3d_en?1:0, de_devp->nrds_enable, post_wr_en, di_pre_stru.mcdi_enable); } void di_vf_x_y(struct vframe_s *vf, unsigned int *x, unsigned int *y) { *x = 0; *y = 0; if (!vf) return; *x = vf->width; *y = vf->height; if (vf->type & VIDTYPE_COMPRESS) { *x = vf->compWidth; *y = vf->compHeight; } } static bool need_bypass(struct vframe_s *vf) { unsigned int x, y; needbypass_flag = true; di_vf_x_y(vf, &x, &y); if (vf->type & VIDTYPE_MVC) return true; if (vf->source_type == VFRAME_SOURCE_TYPE_PPMGR) return true; if (vf->type & VIDTYPE_VIU_444) return true; if (vf->type & VIDTYPE_PIC) return true; #if 0 if (vf->type & VIDTYPE_COMPRESS) return true; #else /*support G12A and TXLX platform*/ if (vf->type & VIDTYPE_COMPRESS) { if (!afbc_is_supported()) return true; if ((y > (default_height + 8)) || x > default_width) return true; } #endif if ((vf->width > default_width) || (vf->height > (default_height + 8))) return true; /*true bypass for game mode*/ if (vf->flag & VFRAME_FLAG_GAME_MODE) return true; needbypass_flag = false; return false; } static void di_reg_process_irq(void) { ulong irq_flag2 = 0; #ifndef RUN_DI_PROCESS_IN_IRQ ulong flags = 0; #endif vframe_t *vframe; unsigned short nr_height = 0, first_field_type; if ((pre_run_flag != DI_RUN_FLAG_RUN) && (pre_run_flag != DI_RUN_FLAG_STEP)) return; if (pre_run_flag == DI_RUN_FLAG_STEP) pre_run_flag = DI_RUN_FLAG_STEP_DONE; di_pre_stru.reg_irq_busy = true; vframe = vf_peek(VFM_NAME); if (vframe) { if (need_bypass(vframe) || ((di_debug_flag>>20) & 0x1)) { if (!di_pre_stru.bypass_flag) { pr_info("DI bypass all %ux%u-0x%x.", vframe->width, vframe->height, vframe->type); } di_pre_stru.bypass_flag = true; di_patch_post_update_mc_sw(DI_MC_SW_OTHER, false); di_pre_stru.reg_irq_busy = false; return; } else { di_pre_stru.bypass_flag = false; } /* patch for vdin progressive input */ if ((is_from_vdin(vframe) && is_progressive(vframe)) #ifdef DET3D || det3d_en #endif || (use_2_interlace_buff & 0x2) ) { use_2_interlace_buff = 1; nr_height = vframe->height; } else { use_2_interlace_buff = 0; nr_height = (vframe->height>>1); } de_devp->nrds_enable = nrds_en; de_devp->pps_enable = pps_en; /*di pre h scaling down: sm1 tm2*/ de_devp->h_sc_down_en = pre_hsc_down_en; vpu_dev_clk_gate_on(de_devp->di_vpu_clk_gate_dev); diwr_set_power_control_pst(1); if (post_wr_en && post_wr_support) diwr_set_power_control(1); /* up for vpu clkb rate change */ up(&di_sema); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) { if (!use_2_interlace_buff) { di_top_gate_control(true, true); di_post_gate_control(true); /* freerun for reg configuration */ enable_di_post_mif(GATE_AUTO); } else { di_top_gate_control(true, false); } de_devp->flags |= DI_VPU_CLKB_SET; enable_di_pre_mif(false, mcpre_en); di_pre_gate_control(true, mcpre_en); di_rst_protect(true);/*2019-01-22 by VLSI feng.wang*/ di_pre_nr_wr_done_sel(true); di_nr_opl()->nr_gate_control(true); } else { /* if mcdi enable DI_CLKG_CTRL should be 0xfef60000 */ DI_Wr(DI_CLKG_CTRL, 0xfef60001); /* nr/blend0/ei0/mtn0 clock gate */ } di_hdr2_hist_init(); if (di_printk_flag & 2) di_printk_flag = 1; di_print("%s: vframe come => di_init_buf\n", __func__); if (cpu_after_eq(MESON_CPU_MAJOR_ID_GXLX)) di_wr_cue_int(); if (is_progressive(vframe) && (prog_proc_config & 0x10)) { #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_lock_irqsave(&plist_lock, flags); #endif di_lock_irqfiq_save(irq_flag2); /* * 10 bit mode need 1.5 times buffer size of * 8 bit mode, init the buffer size as 10 bit * mode size, to make sure can switch bit mode * smoothly. */ di_init_buf(default_width, default_height, 1); di_unlock_irqfiq_restore(irq_flag2); #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_unlock_irqrestore(&plist_lock, flags); nr_height = vframe->height; #endif } else { #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_lock_irqsave(&plist_lock, flags); #endif di_lock_irqfiq_save(irq_flag2); /* * 10 bit mode need 1.5 times buffer size of * 8 bit mode, init the buffer size as 10 bit * mode size, to make sure can switch bit mode * smoothly. */ di_init_buf(default_width, default_height, 0); di_unlock_irqfiq_restore(irq_flag2); #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_unlock_irqrestore(&plist_lock, flags); #endif } if (!reg_flag) pr_err("di: warning unreg in reg irq\n"); calc_lmv_init(); first_field_type = (vframe->type & VIDTYPE_TYPEMASK); //pr_info("%s , %d\n", __func__, __LINE__); //pr_info("filed type:0x%x, in H=%d, V=%d\n", // first_field_type, vframe->width, nr_height); di_pre_size_change(vframe->width, nr_height, first_field_type); #ifdef CONFIG_AMLOGIC_MEDIA_TVIN di_pre_stru.mtn_status = adpative_combing_config(vframe->width, (vframe->height>>1), (vframe->source_type), is_progressive(vframe), vframe->sig_fmt); #endif di_patch_post_update_mc_sw(DI_MC_SW_REG, true); di_nr_opl()->cue_int(vframe); if (de_devp->flags & DI_LOAD_REG_FLAG) up(&di_sema); di_pre_stru.retry_en = false; di_pre_stru.retry_cnt = 0; di_pre_stru.retry_index = 0; init_flag = 1; /*di_pre_stru.reg_req_flag_irq = 1;*/ } di_pre_stru.reg_irq_busy = false; } static void di_process(void) { ulong irq_flag2 = 0; #ifndef RUN_DI_PROCESS_IN_IRQ ulong flags = 0; #endif if (init_flag && (recovery_flag == 0) && (dump_state_flag == 0)) { #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_lock_irqsave(&plist_lock, flags); #endif if (di_pre_stru.pre_de_busy == 0) { if (di_pre_stru.pre_de_process_done) { #if 0/*def CHECK_DI_DONE*/ /* also for NEW_DI ? 7/15/2013 */ unsigned int data32 = Rd(DI_INTR_CTRL); /*DI_INTR_CTRL[bit 0], NRWR_done, set by * hardware when NRWR is done,clear by write 1 * by code;[bit 1] * MTNWR_done, set by hardware when MTNWR * is done, clear by write 1 by code;these two * bits have nothing to do with * DI_INTR_CTRL[16](NRW irq mask, 0 to enable * irq) and DI_INTR_CTRL[17] * (MTN irq mask, 0 to enable irq).two * interrupts are raised if both * DI_INTR_CTRL[16] and DI_INTR_CTRL[17] are 0 */ if ( ((data32 & 0x1) && ((di_pre_stru.enable_mtnwr == 0) || (data32 & 0x2))) || (di_pre_stru.pre_de_clear_flag == 2)) { RDMA_WR(DI_INTR_CTRL, data32); #endif if (di_pre_stru.pre_de_clear_flag == 2) { di_pre_stru.retry_en = true; } else { pre_process_time = di_pre_stru.pre_de_busy_timer_count; pre_de_done_buf_config(); } di_pre_stru.pre_de_process_done = 0; di_pre_stru.pre_de_clear_flag = 0; #ifdef CHECK_DI_DONE } #endif } else if (di_pre_stru.pre_de_clear_flag == 1) { di_lock_irqfiq_save( irq_flag2); pre_de_done_buf_clear(); di_unlock_irqfiq_restore( irq_flag2); di_pre_stru.pre_de_process_done = 0; di_pre_stru.pre_de_clear_flag = 0; } } di_lock_irqfiq_save(irq_flag2); di_post_stru.check_recycle_buf_cnt = 0; while (check_recycle_buf() & 1) { if (di_post_stru.check_recycle_buf_cnt++ > MAX_IN_BUF_NUM) { di_pr_info("%s: check_recycle_buf time out!!\n", __func__); break; } } di_unlock_irqfiq_restore(irq_flag2); /************/ if (di_pre_stru.retry_en && (di_pre_stru.pre_de_busy == 0) && (di_pre_stru.pre_de_process_done == 0) && !atomic_read(&di_flag_unreg) && (di_pre_stru.pre_de_process_flag == 0)) { di_pre_stru.retry_index = di_pre_stru.field_count_for_cont; di_pre_stru.field_count_for_cont--; di_print("di:retry set%d\n", di_pre_stru.retry_index); pre_de_process(); di_pre_stru.retry_en = false; di_pre_stru.retry_cnt++; } if ((di_pre_stru.pre_de_busy == 0) && (di_pre_stru.pre_de_process_done == 0)) { if ((pre_run_flag == DI_RUN_FLAG_RUN) || (pre_run_flag == DI_RUN_FLAG_STEP)) { if (pre_run_flag == DI_RUN_FLAG_STEP) pre_run_flag = DI_RUN_FLAG_STEP_DONE; if ((!atomic_read(&di_flag_unreg)) && (di_pre_stru.pre_de_process_flag == 0) && pre_de_buf_config()) pre_de_process(); } } di_post_stru.di_post_process_cnt = 0; while (process_post_vframe()) { if (di_post_stru.di_post_process_cnt++ > MAX_POST_BUF_NUM) { di_pr_info("%s: process_post_vframe time out!!\n", __func__); break; } } if (post_wr_en && post_wr_support) { if (di_post_stru.post_de_busy == 0 && di_post_stru.de_post_process_done) { post_de_done_buf_config(); di_post_stru.de_post_process_done = 0; } di_post_process(); } #if (!(defined RUN_DI_PROCESS_IN_IRQ)) || (defined ENABLE_SPIN_LOCK_ALWAYS) spin_unlock_irqrestore(&plist_lock, flags); #endif } } static void di_pre_trigger_work(struct di_pre_stru_s *pre_stru_p) { if (pre_stru_p->pre_de_busy && init_flag) { pre_stru_p->pre_de_busy_timer_count++; if (pre_stru_p->pre_de_busy_timer_count >= nr_done_check_cnt && ((cur_to_msecs() - di_pre_stru.irq_time[1]) > (10*nr_done_check_cnt))) { if (di_dbg_mask & 4) { dump_mif_size_state(&di_pre_stru, &di_post_stru); } ddbg_mod_save(eDI_DBG_MOD_PRE_TIMEOUT, 0, 0); enable_di_pre_mif(false, mcpre_en); if (de_devp->nrds_enable) nr_ds_hw_ctrl(false); pre_stru_p->pre_de_busy_timer_count = 0; pre_stru_p->pre_de_irq_timeout_count++; pre_stru_p->pre_de_process_done = 1; pre_stru_p->pre_de_busy = 0; pre_stru_p->pre_de_clear_flag = 2; if ((pre_stru_p->field_count_for_cont < 10) || (di_dbg_mask&0x2)) { pr_info("DI*****wait %d timeout 0x%x(%d ms)*****\n", pre_stru_p->field_count_for_cont, Rd(DI_INTR_CTRL), (unsigned int)(cur_to_msecs() - di_pre_stru.irq_time[1])); pr_info("AFBCD0_MISC_CTRL=0x%x\n", RDMA_RD(VD1_AFBCD0_MISC_CTRL)); } } } else { pre_stru_p->pre_de_busy_timer_count = 0; } /* if(force_trig){ */ trigger_pre_di_process(TRIGGER_PRE_BY_TIMER); /* } */ if (force_recovery) { if (recovery_flag || (force_recovery & 0x2)) { force_recovery_count++; if (init_flag) { pr_err("====== DI force recovery %u-%u =========\n", recovery_log_reason, recovery_log_queue_idx); print_di_buf(recovery_log_di_buf, 2); force_recovery &= (~0x2); recovery_flag = 0; } } } } static int di_task_handle(void *data) { struct di_dev_s *devp; int ret = 0; devp = (struct di_dev_s *)data; if (!devp) return -1; while (1) { ret = down_interruptible(&di_sema); if (ret != 0) continue; if (!de_devp) continue; if (!active_flag) continue; if ((di_pre_stru.unreg_req_flag || di_pre_stru.force_unreg_req_flag || di_pre_stru.disable_req_flag) && (di_pre_stru.pre_de_busy == 0)) { di_unreg_process(); #if 0 /* if mirror mode, can't speed down the clk*/ /* set min rate for power saving */ if (de_devp->vpu_clkb) { clk_set_rate(de_devp->vpu_clkb, de_devp->clkb_min_rate); } #endif } if (/*di_pre_stru.reg_req_flag_irq ||*/ di_pre_stru.reg_req_flag) { di_reg_process(); di_pre_stru.reg_req_flag = 0; /*di_pre_stru.reg_req_flag_irq = 0;*/ } #ifdef CONFIG_CMA /* mutex_lock(&de_devp->cma_mutex);*/ if (di_pre_stru.cma_release_req) { atomic_set(&devp->mem_flag, 0); di_cma_release(devp); di_pre_stru.cma_release_req = 0; di_pre_stru.cma_alloc_done = 0; } if (di_pre_stru.cma_alloc_req) { if (di_cma_alloc(devp)) atomic_set(&devp->mem_flag, 1); else atomic_set(&devp->mem_flag, 0); di_pre_stru.cma_alloc_req = 0; di_pre_stru.cma_alloc_done = 1; } if (di_free_mem_pre()) { di_cma_release(devp); di_pr_info("release mirror\n"); atomic_set(&di_trig_free_mem, 0); } /* mutex_unlock(&de_devp->cma_mutex); */ #endif if (de_devp->flags & DI_VPU_CLKB_SET) { if (is_meson_txlx_cpu()) { if (!use_2_interlace_buff) { #ifdef CLK_TREE_SUPPORT clk_set_rate(de_devp->vpu_clkb, de_devp->clkb_min_rate); #endif } else { #ifdef CLK_TREE_SUPPORT clk_set_rate(de_devp->vpu_clkb, de_devp->clkb_max_rate); #endif } } if (is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu()) { #ifdef CLK_TREE_SUPPORT clk_set_rate(de_devp->vpu_clkb, de_devp->clkb_max_rate); #endif } de_devp->flags &= (~DI_VPU_CLKB_SET); } } return 0; } static void di_pre_process_irq(struct di_pre_stru_s *pre_stru_p) { int i; if (active_flag) { /* must wait pre de done or time out to clear the de_busy * otherwise may appear watch dog reboot probability * caused by disable mif in unreg_process_irq */ if (pre_stru_p->unreg_req_flag_irq && (di_pre_stru.pre_de_busy == 0)) di_unreg_process_irq(); if (init_flag == 0 /*&& pre_stru_p->reg_req_flag_irq == 0*/ && (!atomic_read(&di_flag_unreg))) di_reg_process_irq(); } for (i = 0; i < 2; i++) { if (active_flag) di_process(); } log_buffer_state("pro"); } static struct hrtimer di_pre_hrtimer; static void pre_tasklet(unsigned long arg) { unsigned int hrtimer_time = 0; hrtimer_time = jiffies_to_msecs(jiffies_64 - de_devp->jiffy); if (hrtimer_time > 10) pr_dbg("DI: tasklet schedule cost %ums.\n", hrtimer_time); di_pre_process_irq((struct di_pre_stru_s *)arg); } static enum hrtimer_restart di_pre_hrtimer_func(struct hrtimer *timer) { if (!di_pre_stru.bypass_flag) di_pre_trigger_work(&di_pre_stru); hrtimer_forward_now(&di_pre_hrtimer, ms_to_ktime(10)); /*di_patch_post_update_mc();*/ return HRTIMER_RESTART; } static void post_display_buf_clear(void) { struct di_buf_s *p = NULL; int itmp; pr_info("%s:\n", __func__); queue_for_each_entry(p, ptmp, QUEUE_DISPLAY, list) { pr_info("\t%s,%d\n", vframe_type_name[p->type], p->index); if (p->type == VFRAME_TYPE_POST) { if (!atomic_dec_and_test(&p->di_cnt)) di_print("%s,di_cnt > 0\n", __func__); recycle_vframe_type_post(p); } else { queue_in(p, QUEUE_RECYCLE); di_print("%s: %s[%d] =>recycle_list\n", __func__, vframe_type_name[p->type], p->index); } } } static void dbg_check_list(void) { unsigned int post_display; post_display = list_count(QUEUE_DISPLAY); di_pr_info("display:%d\n", post_display); } /* * provider/receiver interface */ char *vf_get_receiver_name(const char *provider_name); static int di_receiver_event_fun(int type, void *data, void *arg) { int i; ulong flags; char *provider_name = (char *)data; if (type == VFRAME_EVENT_PROVIDER_QUREY_VDIN2NR) { return di_pre_stru.vdin2nr; } else if (type == VFRAME_EVENT_PROVIDER_UNREG) { mutex_lock(&di_event_mutex); atomic_set(&di_flag_unreg, 1); //ary 2019-05-27 pr_dbg("%s , is_bypass() %d trick_mode %d bypass_all %d\n", __func__, is_bypass(NULL), trick_mode, bypass_all); di_pre_stru.vdin_source = false; pr_info("DI: %s: unreg\n", __func__); pr_info("DI: provider name:%s\n", provider_name); ddbg_mod_save(eDI_DBG_MOD_UNREGB, 0, 0); di_pre_stru.unreg_req_flag = 1; di_pre_stru.vdin_source = false; /*di_request_afbc(false);*/ trigger_pre_di_process(TRIGGER_PRE_BY_PROVIDER_UNREG); di_pre_stru.unreg_req_flag_cnt = 0; //wait 10ms: if (di_pre_stru.pre_de_process_flag || di_pre_stru.pre_de_busy) { pr_info("di:w10\n"); usleep_range(10000, 10001); } else { pr_info("di:no w\n"); } di_pre_stru.wait_afbc = false; while (di_pre_stru.unreg_req_flag || di_pre_stru.reg_irq_busy) { usleep_range(1000, 1001); if (di_pre_stru.unreg_req_flag_cnt++ > di_reg_unreg_cnt) { reg_unreg_timeout_cnt++; pr_err("%s:unreg_reg_flag timeout!!!\n", __func__); di_unreg_process(); break; } } #ifdef SUPPORT_MPEG_TO_VDIN if (mpeg2vdin_flag) { struct vdin_arg_s vdin_arg; struct vdin_v4l2_ops_s *vdin_ops = get_vdin_v4l2_ops(); vdin_arg.cmd = VDIN_CMD_MPEGIN_STOP; if (vdin_ops->tvin_vdin_func) vdin_ops->tvin_vdin_func(0, &vdin_arg); mpeg2vdin_flag = 0; } #endif bypass_state = 1; #ifdef RUN_DI_PROCESS_IN_IRQ if (di_pre_stru.vdin_source) DI_Wr_reg_bits(VDIN_WR_CTRL, 0x3, 24, 3); #endif ddbg_mod_save(eDI_DBG_MOD_UNREGE, 0, 0); mutex_unlock(&di_event_mutex); pr_info("DI: unreg f\n"); } else if (type == VFRAME_EVENT_PROVIDER_RESET) { /*di_blocking = 1;*/ mutex_lock(&di_event_mutex); pr_info("%s: VFRAME_EVENT_PROVIDER_RESET\n", __func__); if (is_bypass(NULL) || bypass_state || di_pre_stru.bypass_flag) { /* only if di is bypassed, then we send the message of * VFRAME_EVENT_PROVIDER_RESET to video and notify * it to keep the last canvas buffer which was * alloced by codec not by di. */ vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); di_blocking = 1; spin_lock_irqsave(&plist_lock, flags); post_display_buf_clear(); spin_unlock_irqrestore(&plist_lock, flags); } di_blocking = 1; /*-----------------------------------*/ spin_lock_irqsave(&plist_lock, flags); for (i = 0; i < MAX_IN_BUF_NUM; i++) { if (vframe_in[i]) pr_info("DI:clear vframe_in[%d]\n", i); vframe_in[i] = NULL; } spin_unlock_irqrestore(&plist_lock, flags); di_blocking = 0; mutex_unlock(&di_event_mutex); pr_info("\treset:end\n"); /*goto light_unreg;*/ } else if (type == VFRAME_EVENT_PROVIDER_LIGHT_UNREG) { mutex_lock(&di_event_mutex); di_blocking = 1; pr_info("%s: LIGHT_UNREG\n", __func__); /*light_unreg:*/ #if 1 spin_lock_irqsave(&plist_lock, flags); for (i = 0; i < MAX_IN_BUF_NUM; i++) { if (vframe_in[i]) pr_info("DI:clear vframe_in[%d]\n", i); vframe_in[i] = NULL; } spin_unlock_irqrestore(&plist_lock, flags); #endif di_blocking = 0; mutex_unlock(&di_event_mutex); pr_info("\tlight unreg:end\n"); } else if (type == VFRAME_EVENT_PROVIDER_LIGHT_UNREG_RETURN_VFRAME) { unsigned char vf_put_flag = 0; pr_info("%s:LIGHT_UNREG_RETURN_VFRAME\n", __func__); /* * do not display garbage when 2d->3d or 3d->2d */ spin_lock_irqsave(&plist_lock, flags); for (i = 0; i < MAX_IN_BUF_NUM; i++) { if (vframe_in[i]) { vf_put(vframe_in[i], VFM_NAME); pr_dbg("DI:clear vframe_in[%d]\n", i); vf_put_flag = 1; } vframe_in[i] = NULL; } if (vf_put_flag) vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); spin_unlock_irqrestore(&plist_lock, flags); } else if (type == VFRAME_EVENT_PROVIDER_VFRAME_READY) { if (di_pre_stru.bypass_flag) vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); trigger_pre_di_process(TRIGGER_PRE_BY_VFRAME_READY); #ifdef RUN_DI_PROCESS_IN_IRQ #define INPUT2PRE_2_BYPASS_SKIP_COUNT 4 if (active_flag && di_pre_stru.vdin_source) { if (is_bypass(NULL)) { if (di_pre_stru.pre_de_busy == 0) { DI_Wr_reg_bits(VDIN_WR_CTRL, 0x3, 24, 3); di_pre_stru.vdin2nr = 0; } if (di_pre_stru.bypass_start_count < INPUT2PRE_2_BYPASS_SKIP_COUNT) { vframe_t *vframe_tmp = vf_get(VFM_NAME); if (vframe_tmp != NULL) { vf_put(vframe_tmp, VFM_NAME); vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); } di_pre_stru.bypass_start_count++; } } else if (is_input2pre()) { di_pre_stru.bypass_start_count = 0; if ((di_pre_stru.pre_de_busy != 0) && (input2pre_miss_policy == 1 && frame_count < 30)) { di_pre_stru.pre_de_clear_flag = 1; di_pre_stru.pre_de_busy = 0; input2pre_buf_miss_count++; } if (di_pre_stru.pre_de_busy == 0) { DI_Wr_reg_bits(VDIN_WR_CTRL, 0x5, 24, 3); di_pre_stru.vdin2nr = 1; di_process(); log_buffer_state("pr_"); if (di_pre_stru.pre_de_busy == 0) input2pre_proc_miss_count++; } else { vframe_t *vframe_tmp = vf_get(VFM_NAME); if (vframe_tmp != NULL) { vf_put(vframe_tmp, VFM_NAME); vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); } input2pre_buf_miss_count++; if ((di_pre_stru.cur_width > 720 && di_pre_stru.cur_height > 576) || (input2pre_throw_count & 0x10000)) di_pre_stru.pre_throw_flag = input2pre_throw_count & 0xffff; } } else { if (di_pre_stru.pre_de_busy == 0) { DI_Wr_reg_bits(VDIN_WR_CTRL, 0x3, 24, 3); di_pre_stru.vdin2nr = 0; } di_pre_stru.bypass_start_count = INPUT2PRE_2_BYPASS_SKIP_COUNT; } } #endif } else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) { /*int in_buf_num = 0;*/ struct vframe_states states; if (recovery_flag) return RECEIVER_INACTIVE; #if 1/*fix for ucode reset method be break by di.20151230*/ di_vf_states(&states, NULL); if (states.buf_avail_num > 0) return RECEIVER_ACTIVE; if (vf_notify_receiver( VFM_NAME, VFRAME_EVENT_PROVIDER_QUREY_STATE, NULL) == RECEIVER_ACTIVE) return RECEIVER_ACTIVE; return RECEIVER_INACTIVE; #else for (i = 0; i < MAX_IN_BUF_NUM; i++) { if (vframe_in[i] != NULL) in_buf_num++; if (bypass_state == 1) { if (in_buf_num > 1) return RECEIVER_ACTIVE; else return RECEIVER_INACTIVE; } else { if (in_buf_num > 0) return RECEIVER_ACTIVE; else return RECEIVER_INACTIVE; } #endif } else if (type == VFRAME_EVENT_PROVIDER_REG) { char *receiver_name = NULL; mutex_lock(&di_event_mutex); if (de_devp->flags & DI_SUSPEND_FLAG) { pr_err("[DI] reg event device hasn't resumed\n"); mutex_unlock(&di_event_mutex); return -1; } if (reg_flag) { pr_err("[DI] no muti instance.\n"); mutex_unlock(&di_event_mutex); dbg_check_list(); return -1; } pr_info("%s: vframe provider reg %s\n", __func__, provider_name); ddbg_mod_save(eDI_DBG_MOD_REGB, 0, 0); di_pre_stru.wait_afbc = false; atomic_set(&di_flag_unreg, 0); //ary bypass_state = 0; di_pre_stru.reg_req_flag = 1; trigger_pre_di_process(TRIGGER_PRE_BY_PROVIDER_REG); /*check unreg process*/ di_pre_stru.reg_req_flag_cnt = 0; while (di_pre_stru.reg_req_flag) { usleep_range(1000, 1001); if (di_pre_stru.reg_req_flag_cnt++ > di_reg_unreg_cnt) { reg_unreg_timeout_cnt++; pr_dbg("%s:reg_req_flag timeout!!!\n", __func__); break; } } if (strncmp(provider_name, "vdin0", 4) == 0) { di_pre_stru.vdin_source = true; } else { di_pre_stru.vdin_source = false; } receiver_name = vf_get_receiver_name(VFM_NAME); if (receiver_name) { if (!strcmp(receiver_name, "amvideo")) { di_post_stru.run_early_proc_fun_flag = 0; if (post_wr_en && post_wr_support) di_post_stru.run_early_proc_fun_flag = 1; } else { di_post_stru.run_early_proc_fun_flag = 1; pr_info("set run_early_proc_fun_flag to 1\n"); } } else { pr_info("%s error receiver is null.\n", __func__); } ddbg_mod_save(eDI_DBG_MOD_REGE, 0, 0); mutex_unlock(&di_event_mutex); pr_info("DI: reg f\n"); } #ifdef DET3D else if (type == VFRAME_EVENT_PROVIDER_SET_3D_VFRAME_INTERLEAVE) { int flag = (long)data; di_pre_stru.vframe_interleave_flag = flag; } #endif else if (type == VFRAME_EVENT_PROVIDER_FR_HINT) { vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, data); } else if (type == VFRAME_EVENT_PROVIDER_FR_END_HINT) { vf_notify_receiver(VFM_NAME, VFRAME_EVENT_PROVIDER_FR_END_HINT, data); } return 0; } #if 0 static void fast_process(void) { int i; ulong flags = 0, irq_flag2 = 0; if (active_flag && is_bypass(NULL) && (BYPASS_GET_MAX_BUF_NUM <= 1) && init_flag && (recovery_flag == 0) && (dump_state_flag == 0)) { if (vf_peek(VFM_NAME) == NULL) return; for (i = 0; i < 2; i++) { spin_lock_irqsave(&plist_lock, flags); if (di_pre_stru.pre_de_process_done) { pre_de_done_buf_config(); di_pre_stru.pre_de_process_done = 0; } di_lock_irqfiq_save(irq_flag2); di_post_stru.check_recycle_buf_cnt = 0; while (check_recycle_buf() & 1) { if (di_post_stru.check_recycle_buf_cnt++ > MAX_IN_BUF_NUM) { di_pr_info("%s: check_recycle_buf time out!!\n", __func__); break; } } di_unlock_irqfiq_restore(irq_flag2); if ((di_pre_stru.pre_de_busy == 0) && (di_pre_stru.pre_de_process_done == 0)) { if ((pre_run_flag == DI_RUN_FLAG_RUN) || (pre_run_flag == DI_RUN_FLAG_STEP)) { if (pre_run_flag == DI_RUN_FLAG_STEP) pre_run_flag = DI_RUN_FLAG_STEP_DONE; if (pre_de_buf_config() && (di_pre_stru.pre_de_process_flag == 0)) pre_de_process(); } } di_post_stru.di_post_process_cnt = 0; while (process_post_vframe()) { if (di_post_stru.di_post_process_cnt++ > MAX_POST_BUF_NUM) { pr_info("%s: process_post_vframe time out!!\n", __func__); break; } } spin_unlock_irqrestore(&plist_lock, flags); } } } #endif static vframe_t *di_vf_peek(void *arg) { vframe_t *vframe_ret = NULL; struct di_buf_s *di_buf = NULL; video_peek_cnt++; if (di_pre_stru.bypass_flag) return vf_peek(VFM_NAME); if ((init_flag == 0) || recovery_flag || di_blocking || di_pre_stru.unreg_req_flag || dump_state_flag) return NULL; if ((run_flag == DI_RUN_FLAG_PAUSE) || (run_flag == DI_RUN_FLAG_STEP_DONE)) return NULL; log_buffer_state("pek"); /*fast_process();*/ #ifdef SUPPORT_START_FRAME_HOLD if ((disp_frame_count == 0) && (is_bypass(NULL) == 0)) { int ready_count = list_count(QUEUE_POST_READY); if (ready_count > start_frame_hold_count) { di_buf = get_di_buf_head(QUEUE_POST_READY); if (di_buf) vframe_ret = di_buf->vframe; } } else #endif { if (!queue_empty(QUEUE_POST_READY)) { di_buf = get_di_buf_head(QUEUE_POST_READY); if (di_buf) vframe_ret = di_buf->vframe; } } #ifdef DI_BUFFER_DEBUG if (vframe_ret) di_print("%s: %s[%d]:%x\n", __func__, vframe_type_name[di_buf->type], di_buf->index, vframe_ret); #endif return vframe_ret; } /*recycle the buffer for keeping buffer*/ static void recycle_keep_buffer(void) { ulong irq_flag2 = 0; int i = 0; struct di_buf_s *keep_buf; keep_buf = di_post_stru.keep_buf; if (!IS_ERR_OR_NULL(keep_buf)) { if (keep_buf->type == VFRAME_TYPE_POST) { pr_dbg("%s recycle keep cur di_buf %d (", __func__, keep_buf->index); di_lock_irqfiq_save(irq_flag2); for (i = 0; i < USED_LOCAL_BUF_MAX; i++) { if (keep_buf->di_buf_dup_p[i]) { queue_in( keep_buf->di_buf_dup_p[i], QUEUE_RECYCLE); pr_dbg(" %d ", keep_buf->di_buf_dup_p[i]->index); } } queue_in(keep_buf, QUEUE_POST_FREE); di_unlock_irqfiq_restore(irq_flag2); pr_dbg(")\n"); } di_post_stru.keep_buf = NULL; } } static bool show_nrwr; static vframe_t *di_vf_get(void *arg) { vframe_t *vframe_ret = NULL; struct di_buf_s *di_buf = NULL, *nr_buf = NULL; ulong irq_flag2 = 0; if (di_pre_stru.bypass_flag) return vf_get(VFM_NAME); if ((init_flag == 0) || recovery_flag || di_blocking || di_pre_stru.unreg_req_flag || dump_state_flag) return NULL; if ((run_flag == DI_RUN_FLAG_PAUSE) || (run_flag == DI_RUN_FLAG_STEP_DONE)) return NULL; #ifdef SUPPORT_START_FRAME_HOLD if ((disp_frame_count == 0) && (is_bypass(NULL) == 0)) { int ready_count = list_count(QUEUE_POST_READY); if (ready_count > start_frame_hold_count) goto get_vframe; } else #endif if (!queue_empty(QUEUE_POST_READY)) { #ifdef SUPPORT_START_FRAME_HOLD get_vframe: #endif log_buffer_state("ge_"); di_lock_irqfiq_save(irq_flag2); di_buf = get_di_buf_head(QUEUE_POST_READY); if (check_di_buf(di_buf, 21)) { di_unlock_irqfiq_restore(irq_flag2); return NULL; } queue_out(di_buf); queue_in(di_buf, QUEUE_DISPLAY); /* add it into display_list */ di_unlock_irqfiq_restore(irq_flag2); if (di_buf) { vframe_ret = di_buf->vframe; nr_buf = di_buf->di_buf_dup_p[1]; if ((post_wr_en && post_wr_support) && (di_buf->process_fun_index != PROCESS_FUN_NULL)) { #ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC vframe_ret->canvas0_config[0].phy_addr = di_buf->nr_adr; vframe_ret->canvas0_config[0].width = di_buf->canvas_width[NR_CANVAS], vframe_ret->canvas0_config[0].height = di_buf->canvas_height; vframe_ret->canvas0_config[0].block_mode = 0; vframe_ret->plane_num = 1; vframe_ret->canvas0Addr = -1; vframe_ret->canvas1Addr = -1; if (show_nrwr) { vframe_ret->canvas0_config[0].phy_addr = nr_buf->nr_adr; vframe_ret->canvas0_config[0].width = nr_buf->canvas_width[NR_CANVAS]; vframe_ret->canvas0_config[0].height = nr_buf->canvas_height; } #else config_canvas_idx(di_buf, di_wr_idx, -1); vframe_ret->canvas0Addr = di_buf->nr_canvas_idx; vframe_ret->canvas1Addr = di_buf->nr_canvas_idx; if (show_nrwr) { config_canvas_idx(nr_buf, di_wr_idx, -1); vframe_ret->canvas0Addr = di_wr_idx; vframe_ret->canvas1Addr = di_wr_idx; } #endif vframe_ret->early_process_fun = do_post_wr_fun; vframe_ret->process_fun = NULL; } di_buf->seq = disp_frame_count; atomic_set(&di_buf->di_cnt, 1); } disp_frame_count++; if (run_flag == DI_RUN_FLAG_STEP) run_flag = DI_RUN_FLAG_STEP_DONE; log_buffer_state("get"); } if (vframe_ret) { di_print("%s: %s[%d]:0x%p %u ms\n", __func__, vframe_type_name[di_buf->type], di_buf->index, vframe_ret, jiffies_to_msecs (jiffies_64 - vframe_ret->ready_jiffies64)); } if (!post_wr_en && di_post_stru.run_early_proc_fun_flag && vframe_ret) { if (vframe_ret->early_process_fun == do_pre_only_fun) vframe_ret->early_process_fun( vframe_ret->private_data, vframe_ret); } return vframe_ret; } static void di_vf_put(vframe_t *vf, void *arg) { struct di_buf_s *di_buf = NULL; ulong irq_flag2 = 0; if (di_pre_stru.bypass_flag) { vf_put(vf, VFM_NAME); vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); #if 0 if (!IS_ERR_OR_NULL(di_post_stru.keep_buf)) recycle_keep_buffer(); #endif return; } /* struct di_buf_s *p = NULL; */ /* int itmp = 0; */ if ((init_flag == 0) || recovery_flag || IS_ERR_OR_NULL(vf)) { di_print("%s: 0x%p\n", __func__, vf); return; } #ifdef DI_KEEP_HIS if (di_blocking) return; #endif log_buffer_state("pu_"); di_buf = (struct di_buf_s *)vf->private_data; if (IS_ERR_OR_NULL(di_buf)) { pr_info("%s: get vframe %p without di buf\n", __func__, vf); return; } if (di_post_stru.keep_buf && (di_post_stru.keep_buf == di_buf || di_last_display != di_post_stru.keep_buf->index)) { /*if (di_post_stru.keep_buf == di_buf) {*/ pr_info("[DI]recycle buffer %d, get cnt %d.\n", di_post_stru.keep_buf->index, disp_frame_count); recycle_keep_buffer(); } if (di_buf->type == VFRAME_TYPE_POST) { di_lock_irqfiq_save(irq_flag2); if (is_in_queue(di_buf, QUEUE_DISPLAY)) { if (!atomic_dec_and_test(&di_buf->di_cnt)) di_print("%s,di_cnt > 0\n", __func__); recycle_vframe_type_post(di_buf); } else { di_print("%s: %s[%d] not in display list\n", __func__, vframe_type_name[di_buf->type], di_buf->index); } di_unlock_irqfiq_restore(irq_flag2); #ifdef DI_BUFFER_DEBUG recycle_vframe_type_post_print(di_buf, __func__, __LINE__); #endif } else { di_lock_irqfiq_save(irq_flag2); queue_in(di_buf, QUEUE_RECYCLE); di_unlock_irqfiq_restore(irq_flag2); di_print("%s: %s[%d] =>recycle_list\n", __func__, vframe_type_name[di_buf->type], di_buf->index); } trigger_pre_di_process(TRIGGER_PRE_BY_PUT); } static int di_event_cb(int type, void *data, void *private_data) { if (type == VFRAME_EVENT_RECEIVER_FORCE_UNREG) { pr_info("%s: RECEIVER_FORCE_UNREG return\n", __func__); return 0; } return 0; } static int di_vf_states(struct vframe_states *states, void *arg) { if (!states) return -1; states->vf_pool_size = de_devp->buf_num_avail; states->buf_free_num = list_count(QUEUE_LOCAL_FREE); states->buf_avail_num = list_count(QUEUE_POST_READY); states->buf_recycle_num = list_count(QUEUE_RECYCLE); if (di_dbg_mask&0x1) { di_pr_info("di-pre-ready-num:%d\n", list_count(QUEUE_PRE_READY)); di_pr_info("di-display-num:%d\n", list_count(QUEUE_DISPLAY)); } return 0; } /***************************** * di driver file_operations * ******************************/ static int di_open(struct inode *node, struct file *file) { di_dev_t *di_in_devp; /* Get the per-device structure that contains this cdev */ di_in_devp = container_of(node->i_cdev, di_dev_t, cdev); file->private_data = di_in_devp; return 0; } static int di_release(struct inode *node, struct file *file) { /* di_dev_t *di_in_devp = file->private_data; */ /* Reset file pointer */ /* Release some other fields */ file->private_data = NULL; return 0; } static struct di_pq_parm_s *di_pq_parm_create(struct am_pq_parm_s *pq_parm_p) { struct di_pq_parm_s *pq_ptr = NULL; struct am_reg_s *am_reg_p = NULL; size_t mem_size = 0; pq_ptr = vzalloc(sizeof(struct di_pq_parm_s)); mem_size = sizeof(struct am_pq_parm_s); memcpy(&pq_ptr->pq_parm, pq_parm_p, mem_size); mem_size = sizeof(struct am_reg_s)*pq_parm_p->table_len; am_reg_p = vzalloc(mem_size); if (!am_reg_p) { vfree(pq_ptr); pr_err("[DI] alloc pq table memory errors\n"); return NULL; } pq_ptr->regs = am_reg_p; return pq_ptr; } static void di_pq_parm_destroy(struct di_pq_parm_s *pq_ptr) { if (!pq_ptr) { pr_err("[DI] %s pq parm pointer null.\n", __func__); return; } vfree(pq_ptr->regs); vfree(pq_ptr); } static long di_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = 0, tab_flag = 0; di_dev_t *di_devp; void __user *argp = (void __user *)arg; size_t mm_size = 0; struct am_pq_parm_s tmp_pq_s = {0}; struct di_pq_parm_s *di_pq_ptr = NULL; if (_IOC_TYPE(cmd) != _DI_) { pr_err("%s invalid command: %u\n", __func__, cmd); return -EFAULT; } di_devp = file->private_data; if (!di_devp || (di_devp->flags & DI_SUSPEND_FLAG)) return -EFAULT; switch (cmd) { case AMDI_IOC_SET_PQ_PARM: mm_size = sizeof(struct am_pq_parm_s); /*coverity[TAINTED_SCALAR] not a tainted data*/ if (copy_from_user(&tmp_pq_s, argp, mm_size)) { pr_err("[DI] set pq parm errors\n"); return -EFAULT; } if (tmp_pq_s.table_len >= TABLE_LEN_MAX) { pr_err("[DI] load 0x%x wrong pq table_len.\n", tmp_pq_s.table_len); return -EFAULT; } tab_flag = TABLE_NAME_DI | TABLE_NAME_NR | TABLE_NAME_MCDI | TABLE_NAME_DEBLOCK | TABLE_NAME_DEMOSQUITO; if (tmp_pq_s.table_name & tab_flag) { pr_info("[DI] load 0x%x pq table len %u %s.\n", tmp_pq_s.table_name, tmp_pq_s.table_len, init_flag?"directly":"later"); } else { pr_err("[DI] load 0x%x wrong pq table.\n", tmp_pq_s.table_name); return -EFAULT; } di_pq_ptr = di_pq_parm_create(&tmp_pq_s); if (!di_pq_ptr) { pr_err("[DI] allocat pq parm struct error.\n"); return -EFAULT; } argp = (void __user *)tmp_pq_s.table_ptr; mm_size = tmp_pq_s.table_len * sizeof(struct am_reg_s); if (copy_from_user(di_pq_ptr->regs, argp, mm_size)) { pr_err("[DI] user copy pq table errors\n"); return -EFAULT; } if (init_flag) { di_load_regs(di_pq_ptr); di_pq_parm_destroy(di_pq_ptr); break; } if (atomic_read(&de_devp->pq_flag) == 0) { atomic_set(&de_devp->pq_flag, 1); if (di_devp->flags & DI_LOAD_REG_FLAG) { struct di_pq_parm_s *pos = NULL, *tmp = NULL; list_for_each_entry_safe(pos, tmp, &di_devp->pq_table_list, list) { if (di_pq_ptr->pq_parm.table_name == pos->pq_parm.table_name) { pr_info("[DI] remove 0x%x table.\n", pos->pq_parm.table_name); list_del(&pos->list); di_pq_parm_destroy(pos); } } } list_add_tail(&di_pq_ptr->list, &di_devp->pq_table_list); di_devp->flags |= DI_LOAD_REG_FLAG; atomic_set(&de_devp->pq_flag, 0); } else { pr_err("[DI] please retry table name 0x%x.\n", di_pq_ptr->pq_parm.table_name); di_pq_parm_destroy(di_pq_ptr); ret = -EFAULT; } break; default: break; } return ret; } #ifdef CONFIG_COMPAT static long di_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned long ret; arg = (unsigned long)compat_ptr(arg); ret = di_ioctl(file, cmd, arg); return ret; } #endif static const struct file_operations di_fops = { .owner = THIS_MODULE, .open = di_open, .release = di_release, .unlocked_ioctl = di_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = di_compat_ioctl, #endif }; static ssize_t show_frame_format(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; if (init_flag) ret += sprintf(buf + ret, "%s\n", di_pre_stru.cur_prog_flag ? "progressive" : "interlace"); else ret += sprintf(buf + ret, "%s\n", "null"); return ret; } static DEVICE_ATTR(frame_format, 0444, show_frame_format, NULL); #if 0 /*move to di_local.c*/ static int __init rmem_di_device_init(struct reserved_mem *rmem, struct device *dev) { di_dev_t *di_devp = dev_get_drvdata(dev); if (di_devp) { di_devp->mem_start = rmem->base; di_devp->mem_size = rmem->size; #ifdef MARK_HIS//mark for ko can't use the reserved mem if (!of_get_flat_dt_prop(rmem->fdt_node, "no-map", NULL)) di_devp->flags |= DI_MAP_FLAG; #endif pr_dbg("di reverse memory 0x%lx, size %uMB.\n", di_devp->mem_start, (di_devp->mem_size >> 20)); return 0; } /* pr_dbg("di reverse memory 0x%x, size %u B.\n", * rmem->base, rmem->size); */ return 1; } static void rmem_di_device_release(struct reserved_mem *rmem, struct device *dev) { di_dev_t *di_devp = dev_get_drvdata(dev); if (di_devp) { di_devp->mem_start = 0; di_devp->mem_size = 0; } } #endif #ifdef CONFIG_AMLOGIC_MEDIA_RDMA unsigned int RDMA_RD_BITS(unsigned int adr, unsigned int start, unsigned int len) { if (de_devp->rdma_handle && di_pre_rdma_enable) return rdma_read_reg(de_devp->rdma_handle, adr) & (((1 << len) - 1) << start); else return Rd_reg_bits(adr, start, len); } unsigned int RDMA_WR(unsigned int adr, unsigned int val) { if (is_need_stop_reg(adr)) return 0; if (de_devp->rdma_handle > 0 && di_pre_rdma_enable) { if (di_pre_stru.field_count_for_cont < 1) DI_Wr(adr, val); else rdma_write_reg(de_devp->rdma_handle, adr, val); return 0; } DI_Wr(adr, val); return 1; } unsigned int RDMA_RD(unsigned int adr) { if (de_devp->rdma_handle > 0 && di_pre_rdma_enable) return rdma_read_reg(de_devp->rdma_handle, adr); else return Rd(adr); } unsigned int RDMA_WR_BITS(unsigned int adr, unsigned int val, unsigned int start, unsigned int len) { if (is_need_stop_reg(adr)) return 0; if (de_devp->rdma_handle > 0 && di_pre_rdma_enable) { if (di_pre_stru.field_count_for_cont < 1) DI_Wr_reg_bits(adr, val, start, len); else rdma_write_reg_bits(de_devp->rdma_handle, adr, val, start, len); return 0; } DI_Wr_reg_bits(adr, val, start, len); return 1; } #else unsigned int RDMA_RD_BITS(unsigned int adr, unsigned int start, unsigned int len) { return Rd_reg_bits(adr, start, len); } unsigned int RDMA_WR(unsigned int adr, unsigned int val) { DI_Wr(adr, val); return 1; } unsigned int RDMA_RD(unsigned int adr) { return Rd(adr); } unsigned int RDMA_WR_BITS(unsigned int adr, unsigned int val, unsigned int start, unsigned int len) { DI_Wr_reg_bits(adr, val, start, len); return 1; } #endif static void set_di_flag(void) { if (is_meson_txl_cpu() || is_meson_txlx_cpu() || is_meson_gxlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu()) { mcpre_en = true; mc_mem_alloc = true; pulldown_enable = false; di_pre_rdma_enable = false; /* * txlx atsc 1080i ei only will cause flicker * when full to small win in home screen */ di_vscale_skip_enable = (is_meson_txlx_cpu() || is_meson_txhd_cpu())?12:4; use_2_interlace_buff = is_meson_gxlx_cpu()?0:1; if (is_meson_txl_cpu() || is_meson_txlx_cpu() || is_meson_gxlx_cpu() || is_meson_txhd_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu()) { full_422_pack = true; } if (nr10bit_support) di_force_bit_mode = 10; else { di_force_bit_mode = 8; full_422_pack = false; } post_hold_line = (is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_sm1_cpu())?10:17; } else { post_hold_line = 8; /*2019-01-10: from VLSI feijun*/ mcpre_en = false; pulldown_enable = false; di_pre_rdma_enable = false; di_vscale_skip_enable = 4; use_2_interlace_buff = 0; di_force_bit_mode = 8; } if (is_meson_tl1_cpu() || is_meson_tm2_cpu() || is_meson_g12a_cpu() || is_meson_g12b_cpu() || is_meson_sm1_cpu()) pulldown_enable = true; if (cpu_after_eq(MESON_CPU_MAJOR_ID_G12A)) intr_mode = 3; if (di_pre_rdma_enable) { pldn_dly = 1; pldn_dly1 = 1; } else { pldn_dly = 2; pldn_dly1 = 2; } mtn_int_combing_glbmot(); } #if 0 /*move to di_local.c*/ static const struct reserved_mem_ops rmem_di_ops = { .device_init = rmem_di_device_init, .device_release = rmem_di_device_release, }; static int __init rmem_di_setup(struct reserved_mem *rmem) { rmem->ops = &rmem_di_ops; /* rmem->priv = cma; */ di_pr_info( "DI reserved memory: created CMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M); return 0; } RESERVEDMEM_OF_DECLARE(di, "amlogic, di-mem", rmem_di_setup); #endif static void di_get_vpu_clkb(struct device *dev, struct di_dev_s *pdev) { int ret = 0; unsigned int tmp_clk[2] = {0, 0}; struct clk *vpu_clk = NULL; struct clk *clkb_tmp_comp = NULL; vpu_clk = clk_get(dev, "vpu_mux"); if (IS_ERR(vpu_clk)) pr_err("%s: get clk vpu error.\n", __func__); else clk_prepare_enable(vpu_clk); ret = of_property_read_u32_array(dev->of_node, "clock-range", tmp_clk, 2); if (ret) { pdev->clkb_min_rate = 250000000; pdev->clkb_max_rate = 500000000; } else { pdev->clkb_min_rate = tmp_clk[0]*1000000; pdev->clkb_max_rate = tmp_clk[1]*1000000; } pr_info("DI: vpu clkb <%lu, %lu>\n", pdev->clkb_min_rate, pdev->clkb_max_rate); #ifdef CLK_TREE_SUPPORT pdev->vpu_clkb = clk_get(dev, "vpu_clkb"); if (is_meson_tl1_cpu()) { clkb_tmp_comp = clk_get(dev, "vpu_clkb_tmp"); if (IS_ERR(clkb_tmp_comp)) pr_err("clkb_tmp_comp error\n"); else { if (!IS_ERR(vpu_clk)) /*coverity[var_deref_mode]*/ clk_set_parent(clkb_tmp_comp, vpu_clk); } } if (IS_ERR(pdev->vpu_clkb)) pr_err("%s: get vpu clkb gate error.\n", __func__); else { clk_set_rate(pdev->vpu_clkb, pdev->clkb_min_rate); pr_info("get clkb rate:%ld\n", clk_get_rate(pdev->vpu_clkb)); } #endif } static int di_probe(struct platform_device *pdev) { int ret = 0; struct di_dev_s *di_devp = NULL; di_pr_info("%s:\n", __func__); #if 1 /*move from init*/ ret = alloc_chrdev_region(&di_devno, 0, DI_COUNT, DEVICE_NAME); if (ret < 0) { pr_err("%s: failed to allocate major number\n", __func__); goto fail_alloc_cdev_region; } di_pr_info("%s: major %d\n", __func__, MAJOR(di_devno)); di_clsp = class_create(CLASS_NAME); if (IS_ERR(di_clsp)) { ret = PTR_ERR(di_clsp); pr_err("%s: failed to create class\n", __func__); goto fail_class_create; } #endif di_devp = kmalloc(sizeof(struct di_dev_s), GFP_KERNEL); if (!di_devp) { pr_err("%s fail to allocate memory.\n", __func__); goto fail_kmalloc_dev; } de_devp = di_devp; memset(di_devp, 0, sizeof(struct di_dev_s)); di_devp->flags |= DI_SUSPEND_FLAG; cdev_init(&(di_devp->cdev), &di_fops); di_devp->cdev.owner = THIS_MODULE; ret = cdev_add(&(di_devp->cdev), di_devno, DI_COUNT); if (ret) goto fail_cdev_add; di_devp->devt = MKDEV(MAJOR(di_devno), 0); di_devp->dev = device_create(di_clsp, &pdev->dev, di_devp->devt, di_devp, "di%d", 0); if (di_devp->dev == NULL) { pr_error("device_create create error\n"); ret = -EEXIST; return ret; } dev_set_drvdata(di_devp->dev, di_devp); platform_set_drvdata(pdev, di_devp); ret = of_reserved_mem_device_init(&pdev->dev); if (ret != 0) pr_info("DI no reserved mem.\n"); ret = of_property_read_u32(pdev->dev.of_node, "flag_cma", &(di_devp->flag_cma)); if (ret) pr_err("DI-%s: get flag_cma error.\n", __func__); ret = of_property_read_u32(pdev->dev.of_node, "nrds-enable", &(di_devp->nrds_enable)); ret = of_property_read_u32(pdev->dev.of_node, "pps-enable", &(di_devp->pps_enable)); /*di pre h scaling down :sm1 tm2*/ di_devp->h_sc_down_en = pre_hsc_down_en; pr_info("di:flag_cma=%d\n", di_devp->flag_cma); if (di_devp->flag_cma >= 1) { #ifdef CONFIG_CMA di_devp->pdev = pdev; di_devp->flags |= DI_MAP_FLAG; if (di_devp->flag_cma == 1 || di_devp->flag_cma == 2) di_devp->mem_size = dma_get_cma_size_int_byte(&pdev->dev); if (di_devp->mem_size <= 0x800000) { di_devp->mem_size = 0x2800000; //(flag_cma ? 3) reserved in //codec mm : cma in codec mm if (di_devp->flag_cma != 3) { //no di cma, try use //cma from codec mm di_devp->flag_cma = 4; } } pr_info("DI: CMA size 0x%x.\n", di_devp->mem_size); if (di_devp->flag_cma == 2) { if (di_cma_alloc_total(di_devp)) atomic_set(&di_devp->mem_flag, 1); } else #endif atomic_set(&di_devp->mem_flag, 0); } else { atomic_set(&di_devp->mem_flag, 1); } /* mutex_init(&di_devp->cma_mutex); */ INIT_LIST_HEAD(&di_devp->pq_table_list); atomic_set(&di_devp->pq_flag, 0); di_devp->pre_irq = irq_of_parse_and_map(pdev->dev.of_node, 0); pr_info("pre_irq:%d\n", di_devp->pre_irq); di_devp->post_irq = irq_of_parse_and_map(pdev->dev.of_node, 1); pr_info("post_irq:%d\n", di_devp->post_irq); #ifdef CONFIG_AMLOGIC_MEDIA_RDMA /* rdma handle */ if (di_pre_rdma_enable) { di_rdma_op.arg = di_devp; di_devp->rdma_handle = rdma_register(&di_rdma_op, di_devp, RDMA_TABLE_SIZE); } #endif di_pr_info("%s allocate rdma channel %d.\n", __func__, di_devp->rdma_handle); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL)) { di_get_vpu_clkb(&pdev->dev, di_devp); #ifdef CLK_TREE_SUPPORT clk_prepare_enable(di_devp->vpu_clkb); pr_info("DI:enable vpu clkb.\n"); #else aml_write_hiubus(HHI_VPU_CLKB_CNTL, 0x1000100); #endif } di_devp->flags &= (~DI_SUSPEND_FLAG); ret = of_property_read_u32(pdev->dev.of_node, "buffer-size", &(di_devp->buffer_size)); if (ret) pr_err("DI-%s: get buffer size error.\n", __func__); /* set flag to indicate that post_wr is supportted */ ret = of_property_read_u32(pdev->dev.of_node, "post-wr-support", &(di_devp->post_wr_support)); if (ret) post_wr_support = 0; else post_wr_support = di_devp->post_wr_support; ret = of_property_read_u32(pdev->dev.of_node, "nr10bit-support", &(di_devp->nr10bit_support)); if (ret) nr10bit_support = 0; else nr10bit_support = di_devp->nr10bit_support; memset(&di_post_stru, 0, sizeof(di_post_stru)); di_post_stru.next_canvas_id = 1; #ifdef DI_USE_FIXED_CANVAS_IDX if (di_get_canvas()) { pr_dbg("DI get canvas error.\n"); ret = -EEXIST; return ret; } #endif device_create_file(di_devp->dev, &dev_attr_config); device_create_file(di_devp->dev, &dev_attr_debug); device_create_file(di_devp->dev, &dev_attr_dump_pic); device_create_file(di_devp->dev, &dev_attr_log); device_create_file(di_devp->dev, &dev_attr_provider_vframe_status); device_create_file(di_devp->dev, &dev_attr_frame_format); device_create_file(di_devp->dev, &dev_attr_tvp_region); pd_device_files_add(di_devp->dev); di_nr_opl()->nr_drv_init(di_devp->dev); mutex_init(&di_event_mutex); di_vpu_dev_register(di_devp); init_flag = 0; reg_flag = 0; di_devp->buf_num_avail = di_devp->mem_size / di_devp->buffer_size; if (di_devp->buf_num_avail > MAX_LOCAL_BUF_NUM) di_devp->buf_num_avail = MAX_LOCAL_BUF_NUM; vf_receiver_init(&di_vf_recv, VFM_NAME, &di_vf_receiver, NULL); vf_reg_receiver(&di_vf_recv); vf_provider_init(&di_vf_prov, VFM_NAME, &deinterlace_vf_provider, NULL); /*active_flag = 1;*/ sema_init(&di_sema, 1); ret = request_irq(di_devp->pre_irq, &de_irq, IRQF_SHARED, "pre_di", (void *)"pre_di"); if (di_devp->post_wr_support) { ret = request_irq(di_devp->post_irq, &post_irq, IRQF_SHARED, "post_di", (void *)"post_di"); } //sema_init(&di_sema, 1); di_sema_init_flag = 1; di_hw_init(pulldown_enable, mcpre_en); set_di_flag(); atomic_set(&di_flag_unreg, 0); /* Disable MCDI when code does not support MCDI */ if (!mcpre_en) DI_VSYNC_WR_MPEG_REG_BITS(MCDI_MC_CRTL, 0, 0, 1); tasklet_init(&di_pre_tasklet, pre_tasklet, (unsigned long)(&di_pre_stru)); tasklet_disable(&di_pre_tasklet); hrtimer_init(&di_pre_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); di_pre_hrtimer.function = di_pre_hrtimer_func; hrtimer_start(&di_pre_hrtimer, ms_to_ktime(10), HRTIMER_MODE_REL); tasklet_enable(&di_pre_tasklet); di_pr_info("%s:Di use HRTIMER\n", __func__); di_devp->task = kthread_run(di_task_handle, di_devp, "kthread_di"); if (IS_ERR(di_devp->task)) pr_err("%s create kthread error.\n", __func__); active_flag = 1; di_debugfs_init(); /*2018-07-18 add debugfs*/ di_patch_post_update_mc_sw(DI_MC_SW_IC, true); dil_attach_ext_api(&di_ext); di_patch_mov_ini(); dil_set_diff_ver_flag(0); di_pr_info("%s:ok\n", __func__); return ret; fail_cdev_add: pr_info("%s:fail_cdev_add\n", __func__); kfree(di_devp); fail_kmalloc_dev: #if 1 /*move from init*/ class_destroy(di_clsp); fail_class_create: unregister_chrdev_region(di_devno, DI_COUNT); fail_alloc_cdev_region: return ret; #endif } static void di_remove(struct platform_device *pdev) { struct di_dev_s *di_devp = NULL; di_pr_info("%s:\n", __func__); di_devp = platform_get_drvdata(pdev); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXLX)) clk_disable_unprepare(di_devp->vpu_clkb); di_hw_uninit(); di_devp->di_event = 0xff; kthread_stop(di_devp->task); hrtimer_cancel(&di_pre_hrtimer); tasklet_kill(&di_pre_tasklet); //ary.sui tasklet_disable(&di_pre_tasklet); #ifdef CONFIG_AMLOGIC_MEDIA_RDMA /* rdma handle */ if (di_devp->rdma_handle > 0) rdma_unregister(di_devp->rdma_handle); #endif vf_unreg_provider(&di_vf_prov); vf_unreg_receiver(&di_vf_recv); di_uninit_buf(1); /* Remove the cdev */ device_remove_file(di_devp->dev, &dev_attr_config); device_remove_file(di_devp->dev, &dev_attr_debug); device_remove_file(di_devp->dev, &dev_attr_log); device_remove_file(di_devp->dev, &dev_attr_dump_pic); device_remove_file(di_devp->dev, &dev_attr_provider_vframe_status); device_remove_file(di_devp->dev, &dev_attr_frame_format); device_remove_file(di_devp->dev, &dev_attr_tvp_region); pd_device_files_del(di_devp->dev); di_nr_opl()->nr_drv_uninit(di_devp->dev); cdev_del(&di_devp->cdev); if (di_devp->flag_cma == 2) { if (dma_release_from_contiguous(&(pdev->dev), di_devp->total_pages, di_devp->mem_size >> PAGE_SHIFT)) { di_devp->total_pages = NULL; di_devp->mem_start = 0; pr_dbg("DI CMA total release ok.\n"); } else { pr_dbg("DI CMA total release fail.\n"); } if (de_devp->nrds_enable) { nr_ds_buf_uninit(de_devp->flag_cma, &(pdev->dev)); } } device_destroy(di_clsp, di_devno); #if 1 /*move from exit*/ class_destroy(di_clsp); di_debugfs_exit(); unregister_chrdev_region(di_devno, DI_COUNT); #endif kfree(di_devp); /* free drvdata */ dev_set_drvdata(&pdev->dev, NULL); platform_set_drvdata(pdev, NULL); di_pr_info("%s:ok\n", __func__); } static void di_shutdown(struct platform_device *pdev) { struct di_dev_s *di_devp = NULL; int ret = 0; active_flag = 0; di_devp = platform_get_drvdata(pdev); ret = hrtimer_cancel(&di_pre_hrtimer); pr_info("di pre hrtimer cancel %d.\n", ret); tasklet_kill(&di_pre_tasklet); tasklet_disable(&di_pre_tasklet); init_flag = 0; if (is_meson_txlx_cpu()) di_top_gate_control(true, true); else DI_Wr(DI_CLKG_CTRL, 0x2); if (!is_meson_txlx_cpu()) vpu_dev_clk_gate_off(di_devp->di_vpu_clk_gate_dev); kfree(di_devp); di_devp = NULL; pr_info("[DI] shutdown done.\n"); } #ifdef CONFIG_PM static void di_clear_for_suspend(struct di_dev_s *di_devp) { pr_info("%s\n", __func__); di_unreg_process();/*have flag*/ if (di_pre_stru.unreg_req_flag_irq) di_unreg_process_irq(); #ifdef CONFIG_CMA if (di_pre_stru.cma_release_req) { pr_info("\tcma_release\n"); atomic_set(&di_devp->mem_flag, 0); di_cma_release(di_devp); di_pre_stru.cma_release_req = 0; di_pre_stru.cma_alloc_done = 0; } #endif #ifdef DI_KEEP_HIS hrtimer_cancel(&di_pre_hrtimer); tasklet_kill(&di_pre_tasklet); //ary.sui tasklet_disable(&di_pre_tasklet); #endif pr_info("%s end\n", __func__); } static int save_init_flag; /* must called after lcd */ static int di_suspend(struct device *dev) { struct di_dev_s *di_devp = NULL; di_devp = dev_get_drvdata(dev); di_devp->flags |= DI_SUSPEND_FLAG; di_clear_for_suspend(di_devp);//add active_flag = 0; /* fix suspend/resume crash problem */ save_init_flag = init_flag; init_flag = 0; hrtimer_cancel(&di_pre_hrtimer); tasklet_kill(&di_pre_tasklet); tasklet_disable(&di_pre_tasklet); #if 0 /*2019-01-18*/ if (di_pre_stru.di_inp_buf) { if (vframe_in[di_pre_stru.di_inp_buf->index]) { vf_put(vframe_in[di_pre_stru.di_inp_buf->index], VFM_NAME); vframe_in[di_pre_stru.di_inp_buf->index] = NULL; vf_notify_provider(VFM_NAME, VFRAME_EVENT_RECEIVER_PUT, NULL); } } #endif if (!is_meson_txlx_cpu()) vpu_dev_clk_gate_off(di_devp->di_vpu_clk_gate_dev); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXHD)) clk_disable_unprepare(di_devp->vpu_clkb); pr_info("di: di_suspend\n"); return 0; } /* must called before lcd */ static int di_resume(struct device *dev) { struct di_dev_s *di_devp = NULL; di_devp = dev_get_drvdata(dev); if (cpu_after_eq(MESON_CPU_MAJOR_ID_TXL)) clk_prepare_enable(di_devp->vpu_clkb); init_flag = save_init_flag; di_devp->flags &= ~DI_SUSPEND_FLAG; /*2018-01-18*/ pr_info("%s\n", __func__); tasklet_init(&di_pre_tasklet, pre_tasklet, (unsigned long)(&di_pre_stru)); tasklet_disable(&di_pre_tasklet); hrtimer_init(&di_pre_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); di_pre_hrtimer.function = di_pre_hrtimer_func; hrtimer_start(&di_pre_hrtimer, ms_to_ktime(10), HRTIMER_MODE_REL); tasklet_enable(&di_pre_tasklet); active_flag = 1; /************/ pr_info("di: resume module\n"); return 0; } static const struct dev_pm_ops di_pm_ops = { .suspend_late = di_suspend, .resume_early = di_resume, }; #endif /* #ifdef CONFIG_USE_OF */ static const struct of_device_id amlogic_deinterlace_dt_match[] = { { .compatible = "amlogic, deinterlace", }, {}, }; /* #else */ /* #define amlogic_deinterlace_dt_match NULL */ /* #endif */ static struct platform_driver di_driver = { .probe = di_probe, .remove = di_remove, .shutdown = di_shutdown, .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, .of_match_table = amlogic_deinterlace_dt_match, #ifdef CONFIG_PM .pm = &di_pm_ops, #endif } }; int __init di_module_init(void) { int ret = 0; pr_dbg("%s ok.\n", __func__); #if 0 /*move to prob*/ ret = alloc_chrdev_region(&di_devno, 0, DI_COUNT, DEVICE_NAME); if (ret < 0) { pr_err("%s: failed to allocate major number\n", __func__); goto fail_alloc_cdev_region; } di_pr_info("%s: major %d\n", __func__, MAJOR(di_devno)); di_clsp = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(di_clsp)) { ret = PTR_ERR(di_clsp); pr_err("%s: failed to create class\n", __func__); goto fail_class_create; } #endif ret = platform_driver_register(&di_driver); if (ret != 0) { pr_err("%s: failed to register driver\n", __func__); return -ENODEV;//goto fail_pdrv_register; } return 0; #if 0 /*move to prob*/ fail_pdrv_register: class_destroy(di_clsp); fail_class_create: unregister_chrdev_region(di_devno, DI_COUNT); fail_alloc_cdev_region: return ret; #endif } void __exit di_module_exit(void) { #if 0 /*move to remove*/ class_destroy(di_clsp); di_debugfs_exit(); unregister_chrdev_region(di_devno, DI_COUNT); #endif platform_driver_unregister(&di_driver); } //MODULE_DESCRIPTION("AMLOGIC DEINTERLACE driver"); //MODULE_LICENSE("GPL"); //MODULE_VERSION("4.0.0");