diff --git a/drivers/media/vout/lcd/lcd_common.h b/drivers/media/vout/lcd/lcd_common.h index 508a6b2de..5969e3a48 100644 --- a/drivers/media/vout/lcd/lcd_common.h +++ b/drivers/media/vout/lcd/lcd_common.h @@ -44,7 +44,8 @@ /* 20230816: optimize clk accuracy*/ /* 20230821: update clk ss support*/ /* 20230823: add dma driver for tcon lut*/ -#define LCD_DRV_VERSION "20230823" +/* 20230824: support high resolution vsync measure debug*/ +#define LCD_DRV_VERSION "20230824" extern struct mutex lcd_vout_mutex; extern spinlock_t lcd_reg_spinlock; diff --git a/drivers/media/vout/lcd/lcd_debug.c b/drivers/media/vout/lcd/lcd_debug.c index 0a90c6551..cac8c43ca 100644 --- a/drivers/media/vout/lcd/lcd_debug.c +++ b/drivers/media/vout/lcd/lcd_debug.c @@ -4228,6 +4228,191 @@ static ssize_t lcd_debug_vinfo_show(struct device *dev, return len; } +//make sure 30min diff is less than half frame +#define LCD_VS_MSR_ERR_MAX 280 //unit:0.000001 + +#define LCD_VS_MSR_DUMP 1 +#define LCD_VS_MSR_AVG 2 +#define LCD_VS_MSR_SNAPSHOT 3 +static unsigned int lcd_vs_msr_sel; + +static ssize_t lcd_debug_vs_msr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aml_lcd_drv_s *pdrv = dev_get_drvdata(dev); + ssize_t len = 0, n = PR_BUF_MAX; + unsigned long long sum = 0, target; + unsigned int avg, err, i; + + if (!pdrv->vs_msr) + return sprintf(buf, "vs_msr buffer is NULL\n"); + + target = pdrv->config.timing.sync_duration_num; + target = lcd_do_div(target * 1000000, pdrv->config.timing.sync_duration_den); + switch (lcd_vs_msr_sel) { + case LCD_VS_MSR_DUMP: + for (i = 0; i < pdrv->vs_msr_cnt; i++) { + if (target >= pdrv->vs_msr[i]) + err = target - pdrv->vs_msr[i]; + else + err = pdrv->vs_msr[i] - target; + if (len >= PR_BUF_MAX) + return len; + n = PR_BUF_MAX - len; + len += snprintf(buf + len, n, "[%d]: %d, %d%s\n", + i, pdrv->vs_msr[i], + err, (err > pdrv->vs_msr_err_th) ? "(X)" : ""); + } + break; + case LCD_VS_MSR_AVG: + for (i = 0; i < pdrv->vs_msr_cnt; i++) + sum += pdrv->vs_msr[i]; + avg = lcd_do_div(sum, pdrv->vs_msr_cnt); + if (target >= avg) + err = target - avg; + else + err = avg - target; + len = sprintf(buf, "vs_msr avg: %d, err: %d%s\n", + avg, err, (err > pdrv->vs_msr_err_th) ? "(X)" : ""); + break; + case LCD_VS_MSR_SNAPSHOT: + for (i = 0; i < pdrv->vs_msr_i; i++) { + if (target >= pdrv->vs_msr_rt[i]) + err = target - pdrv->vs_msr_rt[i]; + else + err = pdrv->vs_msr_rt[i] - target; + if (len >= PR_BUF_MAX) + return len; + n = PR_BUF_MAX - len; + len += snprintf(buf + len, n, "[%d]: %d, %d%s\n", + i, pdrv->vs_msr_rt[i], + err, (err > pdrv->vs_msr_err_th) ? "(X)" : ""); + } + break; + default: + break; + } + + if (len >= PR_BUF_MAX) + return len; + n = PR_BUF_MAX - len; + len += snprintf(buf + len, n, + "vs_msr max:%d, min:%d\n" + "vs_msr en:%d, err_th:%d, i:%d, cnt:%d, cnt_max:%d\n", + pdrv->vs_msr_max, pdrv->vs_msr_min, + pdrv->vs_msr_en, pdrv->vs_msr_err_th, + pdrv->vs_msr_i, pdrv->vs_msr_cnt, pdrv->vs_msr_cnt_max); + + return len; +} + +static ssize_t lcd_debug_vs_msr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aml_lcd_drv_s *pdrv = dev_get_drvdata(dev); + char *buf_orig; + char *parm[8] = {NULL}; + unsigned int temp, msr_en; + int ret; + + if (!buf) + return count; + buf_orig = kstrdup(buf, GFP_KERNEL); + if (!buf_orig) { + LCDERR("%s: buf malloc error\n", __func__); + return count; + } + lcd_debug_parse_param(buf_orig, (char **)&parm); + + if (strcmp(parm[0], "init") == 0) { + if (!parm[1]) { + pr_err("invalid data\n"); + goto lcd_debug_vs_msr_store_next; + } + ret = kstrtouint(parm[1], 10, &temp); + if (ret) { + pr_err("invalid data\n"); + goto lcd_debug_vs_msr_store_next; + } + if (temp) { + pdrv->vs_msr_cnt_max = temp; + pdrv->vs_msr = kcalloc(pdrv->vs_msr_cnt_max, + sizeof(unsigned int), GFP_KERNEL); + if (!pdrv->vs_msr) + goto lcd_debug_vs_msr_store_next; + pdrv->vs_msr_rt = kcalloc(300, sizeof(unsigned int), GFP_KERNEL); + if (!pdrv->vs_msr_rt) + goto lcd_debug_vs_msr_store_next; + pr_info("vs_msr buffer init ok, size: %d\n", pdrv->vs_msr_cnt_max); + } else { + if (pdrv->vs_msr_en) { + pr_err("vs_msr is still enabled, can't release buffer\n"); + goto lcd_debug_vs_msr_store_next; + } + pdrv->vs_msr_i = 0; + pdrv->vs_msr_cnt = 0; + pdrv->vs_msr_cnt_max = 0; + + kfree(pdrv->vs_msr); + pdrv->vs_msr = NULL; + kfree(pdrv->vs_msr_rt); + pdrv->vs_msr_rt = NULL; + } + } else if (strcmp(parm[0], "en") == 0) { + if (parm[1]) { + ret = kstrtouint(parm[1], 10, &temp); + if (ret) { + pr_err("invalid data\n"); + goto lcd_debug_vs_msr_store_next; + } + if (temp) { + if (!pdrv->vs_msr || !pdrv->vs_msr_rt) { + pr_err("vs_msr buffer is NULL, can't enable\n"); + goto lcd_debug_vs_msr_store_next; + } + pdrv->vs_msr_cnt = 0; + pdrv->vs_msr_i = 0; + pdrv->vs_msr_max = 0; + pdrv->vs_msr_min = 0xffffffff; + msr_en = 1; //waiting for behind parameters + } else { + pdrv->vs_msr_en = 0; + } + } + if (parm[2]) { + ret = kstrtouint(parm[2], 10, &temp); + if (ret) { + pr_err("invalid data\n"); + goto lcd_debug_vs_msr_store_next; + } + pdrv->vs_msr_err_th = temp; + } else { + if (pdrv->vs_msr_err_th == 0) + pdrv->vs_msr_err_th = LCD_VS_MSR_ERR_MAX; + } + if (msr_en) + pdrv->vs_msr_en = 1; + pr_info("vs_msr en:%d, err_th:%d\n" + "i:%d, cnt:%d, cnt_max:%d\n", + pdrv->vs_msr_en, pdrv->vs_msr_err_th, + pdrv->vs_msr_i, pdrv->vs_msr_cnt, pdrv->vs_msr_cnt_max); + } else if (strcmp(parm[0], "dump") == 0) { + lcd_vs_msr_sel = LCD_VS_MSR_DUMP; + } else if (strcmp(parm[0], "avg") == 0) { + lcd_vs_msr_sel = LCD_VS_MSR_AVG; + } else if (strcmp(parm[0], "snapshot") == 0) { + lcd_vs_msr_sel = LCD_VS_MSR_SNAPSHOT; + } else { + LCDERR("invalid command\n"); + kfree(buf_orig); + return -EINVAL; + } + +lcd_debug_vs_msr_store_next: + kfree(buf_orig); + return count; +} + static struct device_attribute lcd_debug_attrs[] = { __ATTR(help, 0444, lcd_debug_common_help, NULL), __ATTR(debug, 0644, lcd_debug_show, lcd_debug_store), @@ -4251,7 +4436,8 @@ static struct device_attribute lcd_debug_attrs[] = { __ATTR(dump, 0644, lcd_debug_dump_show, lcd_debug_dump_store), __ATTR(print, 0644, lcd_debug_print_show, lcd_debug_print_store), __ATTR(cus_ctrl, 0444, lcd_debug_cus_ctrl_show, NULL), - __ATTR(vinfo, 0444, lcd_debug_vinfo_show, NULL) + __ATTR(vinfo, 0444, lcd_debug_vinfo_show, NULL), + __ATTR(vs_msr, 0644, lcd_debug_vs_msr_show, lcd_debug_vs_msr_store) }; static const char *lcd_rgb_debug_usage_str = { diff --git a/drivers/media/vout/lcd/lcd_vout.c b/drivers/media/vout/lcd/lcd_vout.c index dbe61d427..0fe2a8ef0 100644 --- a/drivers/media/vout/lcd/lcd_vout.c +++ b/drivers/media/vout/lcd/lcd_vout.c @@ -641,6 +641,7 @@ static int lcd_vsync_print_cnt; static inline void lcd_vsync_handler(struct aml_lcd_drv_s *pdrv) { unsigned long flags = 0; + unsigned int temp; if (!pdrv) return; @@ -682,6 +683,25 @@ static inline void lcd_vsync_handler(struct aml_lcd_drv_s *pdrv) pdrv->test_state = pdrv->test_flag; lcd_debug_test(pdrv, pdrv->test_state); } + + if (pdrv->vs_msr && pdrv->vs_msr_rt && pdrv->vs_msr_en) { + temp = vout_frame_rate_msr_high_res(pdrv->viu_sel); + pdrv->vs_msr_rt[pdrv->vs_msr_i++] = temp; + pdrv->vs_msr_sum_temp += temp; + if (temp > pdrv->vs_msr_max) + pdrv->vs_msr_max = temp; + if (temp < pdrv->vs_msr_min) + pdrv->vs_msr_min = temp; + if (pdrv->vs_msr_i >= pdrv->config.timing.frame_rate) { + pdrv->vs_msr[pdrv->vs_msr_cnt++] = + lcd_do_div(pdrv->vs_msr_sum_temp, pdrv->vs_msr_i); + pdrv->vs_msr_sum_temp = 0; + if (pdrv->vs_msr_cnt >= pdrv->vs_msr_cnt_max) + pdrv->vs_msr_en = 0; + else + pdrv->vs_msr_i = 0; + } + } spin_unlock_irqrestore(&pdrv->isr_lock, flags); if (lcd_vsync_print_cnt++ >= LCD_DEBUG_VSYNC_INTERVAL) { diff --git a/drivers/media/vout/vout_serve/vout_mux.c b/drivers/media/vout/vout_serve/vout_mux.c index f3aac7b7a..ddfb79889 100644 --- a/drivers/media/vout/vout_serve/vout_mux.c +++ b/drivers/media/vout/vout_serve/vout_mux.c @@ -29,6 +29,7 @@ struct vout_mux_data_s { struct clk *msr_clk; unsigned int (*vs_measure)(int index); + unsigned int (*vs_measure_high_res)(int index); void (*msr_ctrl_init)(struct platform_device *pdev, struct vout_mux_data_s *vdata); void (*update_viu_mux)(int index, unsigned int mux_sel); void (*clear_viu_mux)(int index, unsigned int mux_sel); @@ -132,6 +133,34 @@ static unsigned int vout_vs_measure_dft(int index) return fr; } +static unsigned int vout_vs_measure_high_res_dft(int index) +{ + unsigned int val[2], fr; + unsigned long long clk_msr; + + if (index > 1) + return 0; + if (vout_vdo_meas_init == 0) + return 0; + + clk_msr = VDIN_MEAS_CLK_FREQ; + + val[0] = vout_vcbus_read(VPP_VDO_MEAS_VS_COUNT_HI); + val[1] = vout_vcbus_read(VPP_VDO_MEAS_VS_COUNT_LO); + clk_msr *= 1000000; + if (val[0] & 0xffff) + return 0; + fr = vout_do_div(clk_msr, val[1]); + if (vout_debug_print) { + VOUTPR("%s: 0x%04x=0x%08x, clk_msr=%lld, fr=%d\n", + __func__, VPP_VDO_MEAS_VS_COUNT_LO, + vout_vcbus_read(VPP_VDO_MEAS_VS_COUNT_LO), + clk_msr, fr); + } + + return fr; +} + #ifndef CONFIG_AMLOGIC_ZAPPER_CUT static unsigned int vout_vs_measure_s5(int index) { @@ -163,6 +192,37 @@ static unsigned int vout_vs_measure_s5(int index) return fr; } + +static unsigned int vout_vs_measure_high_res_s5(int index) +{ + struct vinfo_s *vinfo = NULL; + unsigned int venc_index, val, fr; + unsigned long long clk_msr = VDIN_MEAS_CLK_FREQ; + + if (vout_vdo_meas_init == 0) + return 0; + if (index == 1) + vinfo = get_current_vinfo(); +#ifdef CONFIG_AMLOGIC_VOUT2_SERVE + else if (index == 2) + vinfo = get_current_vinfo2(); +#endif +#ifdef CONFIG_AMLOGIC_VOUT3_SERVE + else if (index == 3) + vinfo = get_current_vinfo3(); +#endif + if (!vinfo) + return 0; + venc_index = (vinfo->viu_mux >> 4) & 0xf; + if (venc_index >= 1) + return 0; + + clk_msr *= 1000000; + val = vout_vcbus_read(VPU_VENC_RO_MEAS0); + fr = vout_do_div(clk_msr, val); + + return fr; +} #endif unsigned int vout_frame_rate_measure(int index) @@ -176,6 +236,17 @@ unsigned int vout_frame_rate_measure(int index) return fr; } +unsigned int vout_frame_rate_msr_high_res(int index) +{ + unsigned int fr; + + if (!vout_mux_data || !vout_mux_data->vs_measure_high_res) + return 0; + + fr = vout_mux_data->vs_measure_high_res(index); + return fr; +} + /* ********************************************************** * vout venc mux * ********************************************************** @@ -366,6 +437,7 @@ void vout_viu_mux_clear(int index, unsigned int mux_sel) static struct vout_mux_data_s vout_mux_match_data = { .msr_clk = NULL, .vs_measure = vout_vs_measure_dft, + .vs_measure_high_res = vout_vs_measure_high_res_dft, .msr_ctrl_init = vout_meas_ctrl_init_dft, .update_viu_mux = vout_viu_mux_update_default, .clear_viu_mux = NULL, @@ -375,6 +447,7 @@ static struct vout_mux_data_s vout_mux_match_data = { static struct vout_mux_data_s vout_mux_match_data_t7 = { .msr_clk = NULL, .vs_measure = vout_vs_measure_dft, + .vs_measure_high_res = vout_vs_measure_high_res_dft, .msr_ctrl_init = vout_meas_ctrl_init_dft, .update_viu_mux = vout_viu_mux_update_t7, .clear_viu_mux = vout_viu_mux_clear_t7, @@ -383,6 +456,7 @@ static struct vout_mux_data_s vout_mux_match_data_t7 = { static struct vout_mux_data_s vout_mux_match_data_t3 = { .msr_clk = NULL, .vs_measure = vout_vs_measure_dft, + .vs_measure_high_res = vout_vs_measure_high_res_dft, .msr_ctrl_init = vout_meas_ctrl_init_dft, .update_viu_mux = vout_viu_mux_update_t3, .clear_viu_mux = vout_viu_mux_clear_t7, @@ -391,6 +465,7 @@ static struct vout_mux_data_s vout_mux_match_data_t3 = { static struct vout_mux_data_s vout_mux_match_data_t5w = { .msr_clk = NULL, .vs_measure = vout_vs_measure_dft, + .vs_measure_high_res = vout_vs_measure_high_res_dft, .msr_ctrl_init = vout_meas_ctrl_init_dft, .update_viu_mux = vout_viu_mux_update_t3, .clear_viu_mux = vout_viu_mux_clear_t7, @@ -399,6 +474,7 @@ static struct vout_mux_data_s vout_mux_match_data_t5w = { static struct vout_mux_data_s vout_mux_match_data_s5 = { .msr_clk = NULL, .vs_measure = vout_vs_measure_s5, + .vs_measure_high_res = vout_vs_measure_high_res_s5, .msr_ctrl_init = vout_meas_ctrl_init_s5, .update_viu_mux = vout_viu_mux_update_t3, .clear_viu_mux = vout_viu_mux_clear_t7, diff --git a/drivers/media/vout/vout_serve/vout_serve.c b/drivers/media/vout/vout_serve/vout_serve.c index d20a89af8..3ff0eb183 100644 --- a/drivers/media/vout/vout_serve/vout_serve.c +++ b/drivers/media/vout/vout_serve/vout_serve.c @@ -510,6 +510,18 @@ static ssize_t vout_frame_rate_show(struct class *class, return ret; } +static ssize_t vout_frame_rate_high_res_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + unsigned int fr; + int ret = 0; + + fr = vout_frame_rate_msr_high_res(1); + ret = sprintf(buf, "%d.%06d\n", (fr / 1000000), (fr % 1000000)); + + return ret; +} + static ssize_t vout_bist_show(struct class *class, struct class_attribute *attr, char *buf) { @@ -773,6 +785,7 @@ static struct class_attribute vout_class_attrs[] = { __ATTR(fr_hint, 0644, vout_fr_hint_show, vout_fr_hint_store), __ATTR(fr_range, 0644, vout_fr_range_show, NULL), __ATTR(frame_rate, 0644, vout_frame_rate_show, NULL), + __ATTR(fr_high_res, 0644, vout_frame_rate_high_res_show, NULL), __ATTR(bist, 0644, vout_bist_show, vout_bist_store), __ATTR(bl_brightness, 0644, vout_bl_brightness_show, vout_bl_brightness_store), __ATTR(vinfo, 0444, vout_vinfo_show, NULL), diff --git a/include/linux/amlogic/media/vout/lcd/lcd_vout.h b/include/linux/amlogic/media/vout/lcd/lcd_vout.h index a5da9966b..3f25aed67 100644 --- a/include/linux/amlogic/media/vout/lcd/lcd_vout.h +++ b/include/linux/amlogic/media/vout/lcd/lcd_vout.h @@ -704,6 +704,17 @@ struct aml_lcd_drv_s { #endif void *debug_info; + unsigned int *vs_msr_rt; + unsigned int *vs_msr; + unsigned int vs_msr_max; + unsigned int vs_msr_min; + unsigned long long vs_msr_sum_temp; + unsigned int vs_msr_i; + unsigned int vs_msr_cnt; + unsigned int vs_msr_cnt_max; + unsigned int vs_msr_err_th; + unsigned char vs_msr_en; + unsigned int vout_state; unsigned int fr_auto_policy; unsigned int fr_mode; diff --git a/include/linux/amlogic/media/vout/vout_notify.h b/include/linux/amlogic/media/vout/vout_notify.h index 9a4446c61..5803f9cd5 100644 --- a/include/linux/amlogic/media/vout/vout_notify.h +++ b/include/linux/amlogic/media/vout/vout_notify.h @@ -393,4 +393,5 @@ enum vmode_e validate_vmode(char *name, unsigned int frac); int set_current_vmode(enum vmode_e mode); void disable_vout_mode_set_sysfs(void); unsigned int vout_frame_rate_measure(int index); +unsigned int vout_frame_rate_msr_high_res(int index); #endif /* _VOUT_NOTIFY_H_ */