From cac0610c876f7368ea475a8806ea77346ce8aeb4 Mon Sep 17 00:00:00 2001 From: Jian Cao Date: Fri, 29 Dec 2023 07:51:18 +0000 Subject: [PATCH] ge2d: add support for bandwidth adjustment [1/1] PD#SWPL-151150 Problem: flickering screen occurs while playing images during rotation. Solution: use ge2d on and off mode to control the ratio between hardware processing and idle states Verify: t5 Change-Id: I4b45e5d9636310630092f09ddc5946a298277e1d Signed-off-by: Jian Cao --- drivers/media/common/ge2d/ge2d_main.c | 100 ++++++++++++++++++++++++ drivers/media/common/ge2d/ge2d_wq.c | 52 +++++++++++- include/linux/amlogic/media/ge2d/ge2d.h | 25 ++++++ 3 files changed, 174 insertions(+), 3 deletions(-) diff --git a/drivers/media/common/ge2d/ge2d_main.c b/drivers/media/common/ge2d/ge2d_main.c index 5d43c5f53..a8f881a6e 100644 --- a/drivers/media/common/ge2d/ge2d_main.c +++ b/drivers/media/common/ge2d/ge2d_main.c @@ -102,6 +102,12 @@ static ssize_t dump_reg_cnt_show(struct class *cla, static ssize_t dump_reg_cnt_store(struct class *cla, struct class_attribute *attr, const char *buf, size_t count); +static ssize_t onoff_mode_show(struct class *cla, + struct class_attribute *attr, + char *buf); +static ssize_t onoff_mode_store(struct class *cla, + struct class_attribute *attr, + const char *buf, size_t count); static const struct file_operations ge2d_fops = { .owner = THIS_MODULE, @@ -118,6 +124,7 @@ static CLASS_ATTR_RO(free_queue_status); static CLASS_ATTR_RW(log_level); static CLASS_ATTR_RW(dump_reg_enable); static CLASS_ATTR_RW(dump_reg_cnt); +static CLASS_ATTR_RW(onoff_mode); static struct attribute *ge2d_class_attrs[] = { &class_attr_work_queue_status.attr, @@ -125,6 +132,7 @@ static struct attribute *ge2d_class_attrs[] = { &class_attr_log_level.attr, &class_attr_dump_reg_enable.attr, &class_attr_dump_reg_cnt.attr, + &class_attr_onoff_mode.attr, NULL }; ATTRIBUTE_GROUPS(ge2d_class); @@ -146,6 +154,45 @@ static struct timer_data_s timer_data; #define TIMER_MS (2000) #endif +static int parse_para(const char *para, int para_num, int *result) +{ + char *token = NULL; + char *params, *params_base; + int *out = result; + int len = 0, count = 0; + int res = 0; + int ret = 0; + + if (!para) + return 0; + + params = kstrdup(para, GFP_KERNEL); + params_base = params; + token = params; + if (!token) + return 0; + len = strlen(token); + do { + token = strsep(¶ms, " "); + while (token && (isspace(*token) || + !isgraph(*token)) && len) { + token++; + len--; + } + if (len == 0 || !token) + break; + ret = kstrtoint(token, 0, &res); + if (ret < 0) + break; + len = strlen(token); + *out++ = res; + count++; + } while ((token) && (count < para_num) && (len > 0)); + + kfree(params_base); + return count; +} + static ssize_t dump_reg_enable_show(struct class *cla, struct class_attribute *attr, char *buf) @@ -209,6 +256,35 @@ static ssize_t log_level_store(struct class *cla, return count; } +static ssize_t onoff_mode_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + u32 onoff_mode, on_cnt, off_cnt; + + ge2d_get_onoff_mode(&onoff_mode, &on_cnt, &off_cnt); + + return snprintf(buf, 80, "onoff_mode:%u on_cnt:%u off_cnt:%u\n", + onoff_mode, on_cnt, off_cnt); +} + +static ssize_t onoff_mode_store(struct class *cla, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 parsed[3]; + + if (likely(parse_para(buf, 3, parsed) == 3)) { + ge2d_set_onoff_mode(parsed[0], parsed[1], parsed[2]); + ge2d_log_info("onoff_mode:%u on_cnt:%u off_cnt:%u\n", + parsed[0], parsed[1], parsed[2]); + } else { + ge2d_log_err("usage: echo onoff_mode on_cnt off_cnt > onoff_mode\n"); + } + + return count; +} + static int ge2d_open(struct inode *inode, struct file *file) { struct ge2d_context_s *context = NULL; @@ -447,6 +523,7 @@ static long ge2d_ioctl(struct file *filp, unsigned int cmd, unsigned long args) struct config_para_ex_ion_s *ge2d_config_ex_ion; struct ge2d_dmabuf_req_s ge2d_req_buf; struct ge2d_dmabuf_exp_s ge2d_exp_buf; + struct ge2d_onoff_mode_para_s ge2d_onoff_mode; int ret = 0; #ifdef CONFIG_COMPAT struct compat_config_para_s __user *uf; @@ -475,6 +552,7 @@ static long ge2d_ioctl(struct file *filp, unsigned int cmd, unsigned long args) memset(&ge2d_config, 0, sizeof(struct config_para_s)); memset(&ge2d_req_buf, 0, sizeof(struct ge2d_dmabuf_req_s)); memset(&ge2d_exp_buf, 0, sizeof(struct ge2d_dmabuf_exp_s)); + memset(&ge2d_onoff_mode, 0, sizeof(struct ge2d_onoff_mode_para_s)); #ifdef CONFIG_AMLOGIC_MEDIA_GE2D_MORE_SECURITY switch (cmd) { case GE2D_CONFIG: @@ -864,6 +942,15 @@ static long ge2d_ioctl(struct file *filp, unsigned int cmd, unsigned long args) pr_err("ioctl not support yed.\n"); ret = -EINVAL; goto release1; + case GE2D_SET_ONOFF_MODE: + ret = copy_from_user(&ge2d_onoff_mode, argp, + sizeof(struct ge2d_onoff_mode_para_s)); + if (ret < 0) { + pr_err("Error user param, %s %d\n", __func__, __LINE__); + ret = -EINVAL; + goto release1; + } + break; default: ret = copy_from_user(¶, argp, sizeof(struct ge2d_para_s)); break; @@ -1097,6 +1184,19 @@ static long ge2d_ioctl(struct file *filp, unsigned int cmd, unsigned long args) case GE2D_DETACH_DMA_FD: ge2d_ioctl_detach_dma_fd(context, data_type); break; + case GE2D_SET_ONOFF_MODE: + ret = ge2d_set_onoff_mode(ge2d_onoff_mode.onoff_mode, + ge2d_onoff_mode.on_cnt, + ge2d_onoff_mode.off_cnt); + break; + case GE2D_GET_ONOFF_MODE: + ge2d_get_onoff_mode(&ge2d_onoff_mode.onoff_mode, + &ge2d_onoff_mode.on_cnt, + &ge2d_onoff_mode.off_cnt); + + ret = copy_to_user(argp, &ge2d_onoff_mode, + sizeof(struct ge2d_onoff_mode_para_s)); + break; /* enqueue one cmd */ case GE2D_FILLRECTANGLE_ENQUEUE: ge2d_log_dbg("fill rect enqueue\t"); diff --git a/drivers/media/common/ge2d/ge2d_wq.c b/drivers/media/common/ge2d/ge2d_wq.c index b0b0ce625..c04b90f8b 100644 --- a/drivers/media/common/ge2d/ge2d_wq.c +++ b/drivers/media/common/ge2d/ge2d_wq.c @@ -51,11 +51,13 @@ #define GE2D_NO_POWER_OFF_OP 0x8 #define GE2D_NO_POWER_ON_OP 0x4 +#define GE2D_ONOFF_MODE_MAX_COUNT 32767 static struct ge2d_manager_s ge2d_manager; static int ge2d_irq = -ENXIO; static struct clk *ge2d_clk; static int backup_init_regs = 1; +static DEFINE_MUTEX(dp_ctrl_mutex); static const int bpp_type_lut[] = { #ifdef CONFIG_AMLOGIC_MEDIA_FB @@ -125,14 +127,58 @@ static const int default_ge2d_color_lut[] = { static int ge2d_buffer_get_phys(struct aml_dma_cfg *cfg, unsigned long *addr); +static u32 ge2d_onoff_mode, ge2d_on_cnt, ge2d_off_cnt; + +/* onoff_mode: + * 0. on_counter means how many pixels will output before ge2d turns off. + * 1. on_counter means how many clocks will ge2d turn on before ge2d turns off. + * + * on_cnt: on counter, range (0, 32767]. + * off_cnt: off counter, range (0, 32767]. + */ +int ge2d_set_onoff_mode(u32 onoff_mode, u32 on_cnt, u32 off_cnt) +{ + if ((onoff_mode != 0 && onoff_mode != 1) || + on_cnt > GE2D_ONOFF_MODE_MAX_COUNT || + off_cnt > GE2D_ONOFF_MODE_MAX_COUNT) { + ge2d_log_err("%s out of range, %u %u %u\n", + __func__, onoff_mode, on_cnt, off_cnt); + return -EINVAL; + } + mutex_lock(&dp_ctrl_mutex); + ge2d_onoff_mode = onoff_mode; + ge2d_on_cnt = on_cnt; + ge2d_off_cnt = off_cnt; + ge2d_log_dbg("%s, onoff_mode:%d on_cnt:%d off_cnt:%d\n", + __func__, onoff_mode, on_cnt, off_cnt); + mutex_unlock(&dp_ctrl_mutex); + + return 0; +} +EXPORT_SYMBOL(ge2d_set_onoff_mode); + +void ge2d_get_onoff_mode(u32 *onoff_mode, u32 *on_cnt, u32 *off_cnt) +{ + if (!onoff_mode || !on_cnt || !off_cnt) { + ge2d_log_err("%s err, onoff_mode:%p on_cnt:%p off_cnt:%p\n", + __func__, onoff_mode, on_cnt, off_cnt); + return; + } + + *onoff_mode = ge2d_onoff_mode; + *on_cnt = ge2d_on_cnt; + *off_cnt = ge2d_off_cnt; +} +EXPORT_SYMBOL(ge2d_get_onoff_mode); + static void ge2d_pre_init(void) { struct ge2d_gen_s ge2d_gen_cfg; ge2d_gen_cfg.interrupt_ctrl = 0x02; - ge2d_gen_cfg.dp_on_cnt = 0; - ge2d_gen_cfg.dp_off_cnt = 0; - ge2d_gen_cfg.dp_onoff_mode = 0; + ge2d_gen_cfg.dp_on_cnt = ge2d_on_cnt; + ge2d_gen_cfg.dp_off_cnt = ge2d_off_cnt; + ge2d_gen_cfg.dp_onoff_mode = ge2d_onoff_mode; ge2d_gen_cfg.vfmt_onoff_en = 0; /* fifo size control, 00: 512, 01: 256, 10: 128 11: 96 */ ge2d_gen_cfg.fifo_size = 0; diff --git a/include/linux/amlogic/media/ge2d/ge2d.h b/include/linux/amlogic/media/ge2d/ge2d.h index 7c5eefb81..a1dc85f68 100644 --- a/include/linux/amlogic/media/ge2d/ge2d.h +++ b/include/linux/amlogic/media/ge2d/ge2d.h @@ -1210,6 +1210,23 @@ struct ge2d_device_data_s { unsigned int cmd_queue_mode; /* cmd queue mode */ }; +/* To enable onoff_mode, set the following values for onoff_mode/on_cnt/off_cnt. + * onoff_mode: + * 0. on_counter means how many pixels will output before ge2d turns off. + * 1. on_counter means how many clocks will ge2d turn on before ge2d turns off. + * + * on_cnt: on counter value, range (0, 32767]. + * off_cnt: off counter value, range (0, 32767]. + * + * To disable onoff_mode, set all 0 for onoff_mode/on_cnt/off_cnt. + * + */ +struct ge2d_onoff_mode_para_s { + u32 onoff_mode; + u32 on_cnt; + u32 off_cnt; +}; + extern struct ge2d_device_data_s ge2d_meson_dev; #define GE2D_IOC_MAGIC 'G' @@ -1281,6 +1298,11 @@ extern struct ge2d_device_data_s ge2d_meson_dev; #define GE2D_POST_QUEUE_NOBLOCK \ _IO(GE2D_IOC_MAGIC, 0x15) +#define GE2D_SET_ONOFF_MODE \ + _IOW(GE2D_IOC_MAGIC, 0x16, struct ge2d_onoff_mode_para_s) +#define GE2D_GET_ONOFF_MODE \ + _IOR(GE2D_IOC_MAGIC, 0x17, struct ge2d_onoff_mode_para_s) + void ge2d_set_src1_data(struct ge2d_src1_data_s *cfg, u32 mask); void ge2d_set_src1_gen(struct ge2d_src1_gen_s *cfg, u32 mask); void ge2d_set_src2_dst_data(struct ge2d_src2_dst_data_s *cfg, u32 mask); @@ -1336,6 +1358,9 @@ void start_cmd_queue_process(u32 queue_cnt); void stop_cmd_queue_process(void); void dump_cmd_queue_regs(u32 queue_index); +int ge2d_set_onoff_mode(u32 onoff_mode, u32 on_cnt, u32 off_cnt); +void ge2d_get_onoff_mode(u32 *onoff_mode, u32 *on_cnt, u32 *off_cnt); + #include "ge2d_func.h" #endif