465 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (C) Rockchip Electronics Co., Ltd.
 | |
|  *
 | |
|  * Author: Huang Lee <Putin.li@rock-chips.com>
 | |
|  */
 | |
| 
 | |
| #include "rga_job.h"
 | |
| #include "rga_common.h"
 | |
| #include "rga_hw_config.h"
 | |
| #include "rga_debugger.h"
 | |
| 
 | |
| #define GET_GCD(n1, n2) \
 | |
| 	({ \
 | |
| 		int i; \
 | |
| 		int gcd = 1; \
 | |
| 		for (i = 1; i <= (n1) && i <= (n2); i++) { \
 | |
| 			if ((n1) % i == 0 && (n2) % i == 0) \
 | |
| 				gcd = i; \
 | |
| 		} \
 | |
| 		gcd; \
 | |
| 	})
 | |
| #define GET_LCM(n1, n2, gcd) (((n1) * (n2)) / gcd)
 | |
| 
 | |
| static int rga_set_feature(struct rga_req *rga_base)
 | |
| {
 | |
| 	int feature = 0;
 | |
| 
 | |
| 	if (rga_base->render_mode == COLOR_FILL_MODE)
 | |
| 		feature |= RGA_COLOR_FILL;
 | |
| 
 | |
| 	if (rga_base->render_mode == COLOR_PALETTE_MODE)
 | |
| 		feature |= RGA_COLOR_PALETTE;
 | |
| 
 | |
| 	if (rga_base->color_key_max > 0 || rga_base->color_key_min > 0)
 | |
| 		feature |= RGA_COLOR_KEY;
 | |
| 
 | |
| 	if ((rga_base->alpha_rop_flag >> 1) & 1)
 | |
| 		feature |= RGA_ROP_CALCULATE;
 | |
| 
 | |
| 	if ((rga_base->alpha_rop_flag >> 8) & 1)
 | |
| 		feature |= RGA_NN_QUANTIZE;
 | |
| 
 | |
| 	return feature;
 | |
| }
 | |
| 
 | |
| static bool rga_check_csc_constant(const struct rga_hw_data *data, struct rga_req *rga_base,
 | |
| 				   uint32_t mode, uint32_t flag)
 | |
| {
 | |
| 	if (mode & flag)
 | |
| 		return true;
 | |
| 
 | |
| 	if ((rga_base->full_csc.flag & 0x1) && (data->feature & RGA_FULL_CSC))
 | |
| 		return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static bool rga_check_csc(const struct rga_hw_data *data, struct rga_req *rga_base)
 | |
| {
 | |
| 	switch (rga_base->yuv2rgb_mode) {
 | |
| 	case 0x1:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_y2r_mode, RGA_MODE_CSC_BT601L);
 | |
| 	case 0x2:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_y2r_mode, RGA_MODE_CSC_BT601F);
 | |
| 	case 0x3:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_y2r_mode, RGA_MODE_CSC_BT709);
 | |
| 	case 0x1 << 2:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_r2y_mode, RGA_MODE_CSC_BT601F);
 | |
| 	case 0x2 << 2:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_r2y_mode, RGA_MODE_CSC_BT601L);
 | |
| 	case 0x3 << 2:
 | |
| 		return rga_check_csc_constant(data, rga_base,
 | |
| 					      data->csc_r2y_mode, RGA_MODE_CSC_BT709);
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if ((rga_base->full_csc.flag & 0x1)) {
 | |
| 		if (data->feature & RGA_FULL_CSC)
 | |
| 			return true;
 | |
| 		else
 | |
| 			return false;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static bool rga_check_resolution(const struct rga_rect_range *range, int width, int height)
 | |
| {
 | |
| 	if (width > range->max.width || height > range->max.height)
 | |
| 		return false;
 | |
| 
 | |
| 	if (width < range->min.width || height < range->min.height)
 | |
| 		return false;
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static bool rga_check_format(const struct rga_hw_data *data,
 | |
| 		int rd_mode, int format, int win_num)
 | |
| {
 | |
| 	int i;
 | |
| 	const uint32_t *formats;
 | |
| 	uint32_t format_count;
 | |
| 
 | |
| 	switch (rd_mode) {
 | |
| 	case RGA_RASTER_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_RASTER_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_RASTER_INDEX];
 | |
| 		break;
 | |
| 	case RGA_FBC_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_AFBC16x16_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_AFBC16x16_INDEX];
 | |
| 		break;
 | |
| 	case RGA_TILE_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_TILE8x8_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_TILE8x8_INDEX];
 | |
| 		break;
 | |
| 	case RGA_TILE4x4_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_TILE4x4_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_TILE4x4_INDEX];
 | |
| 		break;
 | |
| 	case RGA_RKFBC_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_RKFBC64x4_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_RKFBC64x4_INDEX];
 | |
| 		break;
 | |
| 	case RGA_AFBC32x8_MODE:
 | |
| 		formats = data->win[win_num].formats[RGA_AFBC32x8_INDEX];
 | |
| 		format_count = data->win[win_num].formats_count[RGA_AFBC32x8_INDEX];
 | |
| 		break;
 | |
| 	default:
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (formats == NULL || format_count == 0)
 | |
| 		return false;
 | |
| 
 | |
| 	for (i = 0; i < format_count; i++)
 | |
| 		if (format == formats[i])
 | |
| 			return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static bool rga_check_align(struct rga_job *job,
 | |
| 			    uint32_t byte_stride_align, uint32_t format, uint16_t w_stride)
 | |
| {
 | |
| 	int bit_stride, pixel_stride, align, gcd;
 | |
| 
 | |
| 	pixel_stride = rga_get_pixel_stride_from_format(format);
 | |
| 	if (pixel_stride <= 0)
 | |
| 		return false;
 | |
| 
 | |
| 	bit_stride = pixel_stride * w_stride;
 | |
| 
 | |
| 	if (bit_stride % (byte_stride_align * 8) == 0)
 | |
| 		return true;
 | |
| 
 | |
| 	if (DEBUGGER_EN(MSG)) {
 | |
| 		gcd = GET_GCD(pixel_stride, byte_stride_align * 8);
 | |
| 		align = GET_LCM(pixel_stride, byte_stride_align * 8, gcd) / pixel_stride;
 | |
| 		rga_job_log(job, "unsupported width stride %d, 0x%x should be %d aligned!",
 | |
| 			w_stride, format, align);
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| static bool rga_check_channel(struct rga_job *job, const struct rga_hw_data *data,
 | |
| 			      struct rga_img_info_t *img,
 | |
| 			      const char *name, int input, int win_num)
 | |
| {
 | |
| 	const struct rga_rect_range *range;
 | |
| 
 | |
| 	if (input)
 | |
| 		range = &data->input_range;
 | |
| 	else
 | |
| 		range = &data->output_range;
 | |
| 
 | |
| 	if (!rga_check_resolution(range, img->act_w, img->act_h)) {
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			rga_job_log(job, "%s resolution check error, input range[%dx%d ~ %dx%d], [w,h] = [%d, %d]\n",
 | |
| 				name,
 | |
| 				data->input_range.min.width, data->input_range.min.height,
 | |
| 				data->input_range.max.width, data->input_range.max.height,
 | |
| 				img->act_w, img->act_h);
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (data == &rga3_data &&
 | |
| 	    !rga_check_resolution(&data->input_range,
 | |
| 				  img->act_w + img->x_offset,
 | |
| 				  img->act_h + img->y_offset)) {
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			rga_job_log(job, "%s RGA3 resolution check error, input range[%dx%d ~ %dx%d], [w+x,h+y] = [%d, %d]\n",
 | |
| 				name,
 | |
| 				data->input_range.min.width, data->input_range.min.height,
 | |
| 				data->input_range.max.width, data->input_range.max.height,
 | |
| 				img->act_w + img->x_offset,
 | |
| 				img->act_h + img->y_offset);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (!rga_check_format(data, img->rd_mode, img->format, win_num)) {
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			rga_job_log(job, "%s format check error, mode = %#x, format = %#x\n",
 | |
| 				name, img->rd_mode, img->format);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	if (!rga_check_align(job, data->byte_stride_align, img->format, img->vir_w)) {
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			rga_job_log(job, "%s align check error, byte_stride_align[%d], format[%#x], vir_w[%d]\n",
 | |
| 				name, data->byte_stride_align, img->format, img->vir_w);
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| static bool rga_check_scale(struct rga_job *job, const struct rga_hw_data *data,
 | |
| 			    struct rga_req *rga_base)
 | |
| {
 | |
| 	struct rga_img_info_t *src0 = &rga_base->src;
 | |
| 	struct rga_img_info_t *dst = &rga_base->dst;
 | |
| 
 | |
| 	int sw, sh;
 | |
| 	int dw, dh;
 | |
| 
 | |
| 	sw = src0->act_w;
 | |
| 	sh = src0->act_h;
 | |
| 	dw = dst->act_w;
 | |
| 	dh = dst->act_h;
 | |
| 
 | |
| 	if (sw > dw) {
 | |
| 		if ((sw >> data->max_downscale_factor) > dw)
 | |
| 			goto check_error;
 | |
| 	} else if (sw < dw) {
 | |
| 		if ((sw << data->max_upscale_factor) < dw)
 | |
| 			goto check_error;
 | |
| 	}
 | |
| 
 | |
| 	if (sh > dh) {
 | |
| 		if ((sh >> data->max_downscale_factor) > dh)
 | |
| 			goto check_error;
 | |
| 	} else if (sh < dh) {
 | |
| 		if ((sh << data->max_upscale_factor) < dh)
 | |
| 			goto check_error;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| check_error:
 | |
| 	if (DEBUGGER_EN(MSG))
 | |
| 		rga_job_log(job, "scale check error, scale limit[1/%d ~ %d], src[%d, %d], dst[%d, %d]\n",
 | |
| 			(1 << data->max_downscale_factor), (1 << data->max_upscale_factor),
 | |
| 			sw, sh, dw, dh);
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| int rga_job_assign(struct rga_job *job)
 | |
| {
 | |
| 	struct rga_img_info_t *src0 = &job->rga_command_base.src;
 | |
| 	struct rga_img_info_t *src1 = &job->rga_command_base.pat;
 | |
| 	struct rga_img_info_t *dst = &job->rga_command_base.dst;
 | |
| 
 | |
| 	struct rga_req *rga_base = &job->rga_command_base;
 | |
| 	const struct rga_hw_data *data;
 | |
| 	struct rga_scheduler_t *scheduler = NULL;
 | |
| 
 | |
| 	int feature;
 | |
| 	int core = RGA_NONE_CORE;
 | |
| 	int optional_cores = RGA_NONE_CORE;
 | |
| 	int specified_cores = RGA_NONE_CORE;
 | |
| 	int i;
 | |
| 	int min_of_job_count = -1;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	/* assigned by userspace */
 | |
| 	if (rga_base->core > RGA_NONE_CORE) {
 | |
| 		if (rga_base->core > RGA_CORE_MASK) {
 | |
| 			rga_job_err(job, "invalid setting core by user\n");
 | |
| 			return -1;
 | |
| 		} else if (rga_base->core & RGA_CORE_MASK)
 | |
| 			specified_cores = rga_base->core;
 | |
| 	}
 | |
| 
 | |
| 	feature = rga_set_feature(rga_base);
 | |
| 
 | |
| 	/* function */
 | |
| 	for (i = 0; i < rga_drvdata->num_of_scheduler; i++) {
 | |
| 		data = rga_drvdata->scheduler[i]->data;
 | |
| 		scheduler = rga_drvdata->scheduler[i];
 | |
| 
 | |
| 		if ((specified_cores != RGA_NONE_CORE) &&
 | |
| 			(!(scheduler->core & specified_cores)))
 | |
| 			continue;
 | |
| 
 | |
| 		if (DEBUGGER_EN(MSG))
 | |
| 			rga_job_log(job, "start policy on %s(%#x)",
 | |
| 				rga_get_core_name(scheduler->core), scheduler->core);
 | |
| 
 | |
| 		if (scheduler->data->mmu == RGA_MMU &&
 | |
| 		    job->flags & RGA_JOB_UNSUPPORT_RGA_MMU) {
 | |
| 			if (DEBUGGER_EN(MSG))
 | |
| 				rga_job_log(job, "RGA2 only support under 4G memory!\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if (feature > 0) {
 | |
| 			if (!(feature & data->feature)) {
 | |
| 				if (DEBUGGER_EN(MSG))
 | |
| 					rga_job_log(job, "%s(%#x), break on feature\n",
 | |
| 						rga_get_core_name(scheduler->core),
 | |
| 						scheduler->core);
 | |
| 				continue;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* only colorfill need single win (colorpalette?) */
 | |
| 		if (!(feature & 1)) {
 | |
| 			if (src1->yrgb_addr > 0) {
 | |
| 				if (!(src0->rd_mode & data->win[0].rd_mode)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), src0 break on %s(%#x)\n",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core,
 | |
| 							rga_get_store_mode_str(src0->rd_mode),
 | |
| 							src0->rd_mode);
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (!(src1->rd_mode & data->win[1].rd_mode)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), src1 break on %s(%#x)\n",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core,
 | |
| 							rga_get_store_mode_str(src1->rd_mode),
 | |
| 							src1->rd_mode);
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (!(dst->rd_mode & data->win[2].rd_mode)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), dst break on %s(%#x)\n",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core,
 | |
| 							rga_get_store_mode_str(dst->rd_mode),
 | |
| 							dst->rd_mode);
 | |
| 					continue;
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (!(src0->rd_mode & data->win[0].rd_mode)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), src break on %s(%#x)\n",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core,
 | |
| 							rga_get_store_mode_str(src0->rd_mode),
 | |
| 							src0->rd_mode);
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (!(dst->rd_mode & data->win[2].rd_mode)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), dst break on %s(%#x)\n",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core,
 | |
| 							rga_get_store_mode_str(dst->rd_mode),
 | |
| 							dst->rd_mode);
 | |
| 					continue;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!rga_check_scale(job, data, rga_base)) {
 | |
| 				if (DEBUGGER_EN(MSG))
 | |
| 					rga_job_log(job, "%s(%#x), break on rga_check_scale",
 | |
| 						rga_get_core_name(scheduler->core),
 | |
| 						scheduler->core);
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			if (!rga_check_channel(job, data, src0, "src0", true, 0)) {
 | |
| 				if (DEBUGGER_EN(MSG))
 | |
| 					rga_job_log(job, "%s(%#x), break on src0",
 | |
| 						rga_get_core_name(scheduler->core),
 | |
| 						scheduler->core);
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			if (src1->yrgb_addr > 0) {
 | |
| 				if (!rga_check_channel(job, data, src1, "src1", true, 1)) {
 | |
| 					if (DEBUGGER_EN(MSG))
 | |
| 						rga_job_log(job, "%s(%#x), break on src1",
 | |
| 							rga_get_core_name(scheduler->core),
 | |
| 							scheduler->core);
 | |
| 					continue;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (!rga_check_channel(job, data, dst, "dst", false, 2)) {
 | |
| 			if (DEBUGGER_EN(MSG))
 | |
| 				rga_job_log(job, "%s(%#x), break on dst",
 | |
| 					rga_get_core_name(scheduler->core),
 | |
| 					scheduler->core);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		if (!rga_check_csc(data, rga_base)) {
 | |
| 			if (DEBUGGER_EN(MSG))
 | |
| 				rga_job_log(job, "%s(%#x), break on rga_check_csc",
 | |
| 					rga_get_core_name(scheduler->core),
 | |
| 					scheduler->core);
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		optional_cores |= scheduler->core;
 | |
| 	}
 | |
| 
 | |
| 	if (optional_cores == 0) {
 | |
| 		rga_job_err(job, "no core match\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < rga_drvdata->num_of_scheduler; i++) {
 | |
| 		scheduler = rga_drvdata->scheduler[i];
 | |
| 
 | |
| 		if (optional_cores & scheduler->core) {
 | |
| 			spin_lock_irqsave(&scheduler->irq_lock, flags);
 | |
| 
 | |
| 			if (scheduler->running_job == NULL) {
 | |
| 				core = scheduler->core;
 | |
| 				job->scheduler = scheduler;
 | |
| 				spin_unlock_irqrestore(&scheduler->irq_lock,
 | |
| 							 flags);
 | |
| 				break;
 | |
| 			} else {
 | |
| 				if ((min_of_job_count == -1) ||
 | |
| 				    (min_of_job_count > scheduler->job_count)) {
 | |
| 					min_of_job_count = scheduler->job_count;
 | |
| 					core = scheduler->core;
 | |
| 					job->scheduler = scheduler;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			spin_unlock_irqrestore(&scheduler->irq_lock, flags);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* TODO: need consider full load */
 | |
| 	if (DEBUGGER_EN(MSG))
 | |
| 		rga_job_log(job, "matched cores = %#x, assign core: %s(%#x)\n",
 | |
| 			optional_cores,
 | |
| 			rga_get_core_name(core), core);
 | |
| 
 | |
| 	return core;
 | |
| }
 |