diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 0adb1e2fa..954f3de7d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -46,6 +46,7 @@ source "drivers/gpu/drm/Kconfig" menu "Frame buffer Devices" source "drivers/video/fbdev/Kconfig" +source "drivers/video/rockchip/Kconfig" endmenu source "drivers/video/backlight/Kconfig" @@ -90,4 +91,5 @@ endif source "drivers/gpu/trace/Kconfig" +source "drivers/video/rockchip/Kconfig" endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ffbac4387..bd44d988c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o ifeq ($(CONFIG_OF),y) obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o endif +obj-y += rockchip/ diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig new file mode 100644 index 000000000..39e01f133 --- /dev/null +++ b/drivers/video/rockchip/Kconfig @@ -0,0 +1 @@ +source "drivers/video/rockchip/rga3/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile new file mode 100644 index 000000000..53958b85c --- /dev/null +++ b/drivers/video/rockchip/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3/ diff --git a/drivers/video/rockchip/rga3/Kconfig b/drivers/video/rockchip/rga3/Kconfig new file mode 100644 index 000000000..c8c96b2d6 --- /dev/null +++ b/drivers/video/rockchip/rga3/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig ROCKCHIP_MULTI_RGA + tristate "MULTI_RGA" + depends on ARCH_ROCKCHIP + help + multi_rga module. + +if ROCKCHIP_MULTI_RGA + +config ROCKCHIP_RGA_ASYNC + bool "Enable async mode" + depends on SYNC_FILE + default y + help + Asynchronous calls will be supported. + +config ROCKCHIP_RGA_PROC_FS + bool "Enable RGA procfs" + select ROCKCHIP_RGA_DEBUGGER + depends on PROC_FS + help + Enable procfs to debug multi RGA driver. + +config ROCKCHIP_RGA_DEBUG_FS + bool "Enable RGA debugfs" + select ROCKCHIP_RGA_DEBUGGER + depends on DEBUG_FS + default y + help + Enable debugfs to debug multi RGA driver. + +config ROCKCHIP_RGA_DEBUGGER + bool + help + Enabling the debugger of multi RGA, you can use procfs and debugfs for debugging. + +endif diff --git a/drivers/video/rockchip/rga3/Makefile b/drivers/video/rockchip/rga3/Makefile new file mode 100644 index 000000000..11f401de2 --- /dev/null +++ b/drivers/video/rockchip/rga3/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I$(srctree)/$(src)/include + +rga3-y := rga_drv.o rga_common.o rga3_reg_info.o rga_iommu.o rga_dma_buf.o rga_job.o rga_hw_config.o rga2_reg_info.o rga_policy.o rga_mm.o +rga3-$(CONFIG_ROCKCHIP_RGA_ASYNC) += rga_fence.o +rga3-$(CONFIG_ROCKCHIP_RGA_DEBUGGER) += rga_debugger.o + +obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3.o diff --git a/drivers/video/rockchip/rga3/include/rga.h b/drivers/video/rockchip/rga3/include/rga.h new file mode 100644 index 000000000..2a60a1ae7 --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga.h @@ -0,0 +1,944 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _RGA_DRIVER_H_ +#define _RGA_DRIVER_H_ + +#include +#include + +/* Use 'r' as magic number */ +#define RGA_IOC_MAGIC 'r' +#define RGA_IOW(nr, type) _IOW(RGA_IOC_MAGIC, nr, type) +#define RGA_IOR(nr, type) _IOR(RGA_IOC_MAGIC, nr, type) +#define RGA_IOWR(nr, type) _IOWR(RGA_IOC_MAGIC, nr, type) + +#define RGA_IOC_GET_DRVIER_VERSION RGA_IOR(0x1, struct rga_version_t) +#define RGA_IOC_GET_HW_VERSION RGA_IOR(0x2, struct rga_hw_versions_t) +#define RGA_IOC_IMPORT_BUFFER RGA_IOWR(0x3, struct rga_buffer_pool) +#define RGA_IOC_RELEASE_BUFFER RGA_IOW(0x4, struct rga_buffer_pool) +#define RGA_IOC_REQUEST_CREATE RGA_IOR(0x5, uint32_t) +#define RGA_IOC_REQUEST_SUBMIT RGA_IOWR(0x6, struct rga_user_request) +#define RGA_IOC_REQUEST_CONFIG RGA_IOWR(0x7, struct rga_user_request) +#define RGA_IOC_REQUEST_CANCEL RGA_IOWR(0x8, uint32_t) + +#define RGA_BLIT_SYNC 0x5017 +#define RGA_BLIT_ASYNC 0x5018 +#define RGA_FLUSH 0x5019 +#define RGA_GET_RESULT 0x501a +#define RGA_GET_VERSION 0x501b +#define RGA_CACHE_FLUSH 0x501c + +#define RGA2_GET_VERSION 0x601b +#define RGA_IMPORT_DMA 0x601d +#define RGA_RELEASE_DMA 0x601e + +#define RGA_TASK_NUM_MAX 50 + +#define RGA_OUT_OF_RESOURCES -10 +#define RGA_MALLOC_ERROR -11 + +#define SCALE_DOWN_LARGE 1 +#define SCALE_UP_LARGE 1 + +#define RGA_BUFFER_POOL_SIZE_MAX 40 + +#define RGA3_MAJOR_VERSION_MASK (0xF0000000) +#define RGA3_MINOR_VERSION_MASK (0x0FF00000) +#define RGA3_SVN_VERSION_MASK (0x000FFFFF) + +#define RGA2_MAJOR_VERSION_MASK (0xFF000000) +#define RGA2_MINOR_VERSION_MASK (0x00F00000) +#define RGA2_SVN_VERSION_MASK (0x000FFFFF) + +#define RGA_MODE_ROTATE_0 (1<<0) +#define RGA_MODE_ROTATE_90 (1<<1) +#define RGA_MODE_ROTATE_180 (1<<2) +#define RGA_MODE_ROTATE_270 (1<<3) +#define RGA_MODE_X_MIRROR (1<<4) +#define RGA_MODE_Y_MIRROR (1<<5) + +#define RGA_MODE_CSC_BT601L (1<<0) +#define RGA_MODE_CSC_BT601F (1<<1) +#define RGA_MODE_CSC_BT709 (1<<2) +#define RGA_MODE_CSC_BT2020 (1<<3) + +#define RGA_MODE_ROTATE_MASK (\ + RGA_MODE_ROTATE_0 | \ + RGA_MODE_ROTATE_90 | \ + RGA_MODE_ROTATE_180 | \ + RGA_MODE_ROTATE_270 | \ + RGA_MODE_X_MIRROR | \ + RGA_MODE_Y_MIRROR) + +enum rga_memory_type { + RGA_DMA_BUFFER = 0, + RGA_VIRTUAL_ADDRESS, + RGA_PHYSICAL_ADDRESS, + RGA_DMA_BUFFER_PTR, +}; + +enum rga_scale_up_mode { + RGA_SCALE_UP_NONE = 0x0, + RGA_SCALE_UP_BIC = 0x1, +}; + +enum rga_scale_down_mode { + RGA_SCALE_DOWN_NONE = 0x0, + RGA_SCALE_DOWN_AVG = 0x1, +}; + +enum RGA_SCHEDULER_CORE { + RGA_SCHEDULER_RGA3_CORE0 = 1 << 0, + RGA_SCHEDULER_RGA3_CORE1 = 1 << 1, + RGA_SCHEDULER_RGA2_CORE0 = 1 << 2, +}; + +/* RGA process mode enum */ +enum { + BITBLT_MODE = 0x0, + COLOR_PALETTE_MODE = 0x1, + COLOR_FILL_MODE = 0x2, + /* used by rga2 */ + UPDATE_PALETTE_TABLE_MODE = 0x6, + UPDATE_PATTEN_BUF_MODE = 0x7, +}; /*render mode*/ + +/* RGA rd_mode */ +enum { + RGA_RASTER_MODE = 0x1 << 0, + RGA_FBC_MODE = 0x1 << 1, + RGA_TILE_MODE = 0x1 << 2, +}; + +enum { + RGA_10BIT_COMPACT = 0x0, + RGA_10BIT_INCOMPACT = 0x1, +}; + +enum { + RGA_CONTEXT_NONE = 0x0, + RGA_CONTEXT_SRC_FIX_ENABLE = 0x1 << 0, + RGA_CONTEXT_SRC_CACHE_INFO = 0x1 << 1, + RGA_CONTEXT_SRC_MASK = RGA_CONTEXT_SRC_FIX_ENABLE | + RGA_CONTEXT_SRC_CACHE_INFO, + RGA_CONTEXT_PAT_FIX_ENABLE = 0x1 << 2, + RGA_CONTEXT_PAT_CACHE_INFO = 0x1 << 3, + RGA_CONTEXT_PAT_MASK = RGA_CONTEXT_PAT_FIX_ENABLE | + RGA_CONTEXT_PAT_CACHE_INFO, + RGA_CONTEXT_DST_FIX_ENABLE = 0x1 << 4, + RGA_CONTEXT_DST_CACHE_INFO = 0x1 << 5, + RGA_CONTEXT_DST_MASK = RGA_CONTEXT_DST_FIX_ENABLE | + RGA_CONTEXT_DST_CACHE_INFO, +}; + +/* RGA feature */ +enum { + RGA_COLOR_FILL = 0x1 << 0, + RGA_COLOR_PALETTE = 0x1 << 1, + RGA_COLOR_KEY = 0x1 << 2, + RGA_ROP_CALCULATE = 0x1 << 3, + RGA_NN_QUANTIZE = 0x1 << 4, + RGA_OSD_BLEND = 0x1 << 5, + RGA_DITHER = 0x1 << 6, + RGA_MOSAIC = 0x1 << 7, + RGA_YIN_YOUT = 0x1 << 8, + RGA_YUV_HDS = 0x1 << 9, + RGA_YUV_VDS = 0x1 << 10, + RGA_OSD = 0x1 << 11, + RGA_PRE_INTR = 0x1 << 12, + RGA_FULL_CSC = 0x1 << 13, +}; + +enum rga_surf_format { + RGA_FORMAT_RGBA_8888 = 0x0, + RGA_FORMAT_RGBX_8888 = 0x1, + RGA_FORMAT_RGB_888 = 0x2, + RGA_FORMAT_BGRA_8888 = 0x3, + RGA_FORMAT_RGB_565 = 0x4, + RGA_FORMAT_RGBA_5551 = 0x5, + RGA_FORMAT_RGBA_4444 = 0x6, + RGA_FORMAT_BGR_888 = 0x7, + + RGA_FORMAT_YCbCr_422_SP = 0x8, + RGA_FORMAT_YCbCr_422_P = 0x9, + RGA_FORMAT_YCbCr_420_SP = 0xa, + RGA_FORMAT_YCbCr_420_P = 0xb, + + RGA_FORMAT_YCrCb_422_SP = 0xc, + RGA_FORMAT_YCrCb_422_P = 0xd, + RGA_FORMAT_YCrCb_420_SP = 0xe, + RGA_FORMAT_YCrCb_420_P = 0xf, + + RGA_FORMAT_BPP1 = 0x10, + RGA_FORMAT_BPP2 = 0x11, + RGA_FORMAT_BPP4 = 0x12, + RGA_FORMAT_BPP8 = 0x13, + + RGA_FORMAT_Y4 = 0x14, + RGA_FORMAT_YCbCr_400 = 0x15, + + RGA_FORMAT_BGRX_8888 = 0x16, + + RGA_FORMAT_YVYU_422 = 0x18, + RGA_FORMAT_YVYU_420 = 0x19, + RGA_FORMAT_VYUY_422 = 0x1a, + RGA_FORMAT_VYUY_420 = 0x1b, + RGA_FORMAT_YUYV_422 = 0x1c, + RGA_FORMAT_YUYV_420 = 0x1d, + RGA_FORMAT_UYVY_422 = 0x1e, + RGA_FORMAT_UYVY_420 = 0x1f, + + RGA_FORMAT_YCbCr_420_SP_10B = 0x20, + RGA_FORMAT_YCrCb_420_SP_10B = 0x21, + RGA_FORMAT_YCbCr_422_SP_10B = 0x22, + RGA_FORMAT_YCrCb_422_SP_10B = 0x23, + + RGA_FORMAT_BGR_565 = 0x24, + RGA_FORMAT_BGRA_5551 = 0x25, + RGA_FORMAT_BGRA_4444 = 0x26, + + RGA_FORMAT_ARGB_8888 = 0x28, + RGA_FORMAT_XRGB_8888 = 0x29, + RGA_FORMAT_ARGB_5551 = 0x2a, + RGA_FORMAT_ARGB_4444 = 0x2b, + RGA_FORMAT_ABGR_8888 = 0x2c, + RGA_FORMAT_XBGR_8888 = 0x2d, + RGA_FORMAT_ABGR_5551 = 0x2e, + RGA_FORMAT_ABGR_4444 = 0x2f, + + RGA_FORMAT_RGBA_2BPP = 0x30, + + RGA_FORMAT_UNKNOWN = 0x100, +}; + +enum rga_alpha_mode { + RGA_ALPHA_STRAIGHT = 0, + RGA_ALPHA_INVERSE = 1, +}; + +enum rga_global_blend_mode { + RGA_ALPHA_GLOBAL = 0, + RGA_ALPHA_PER_PIXEL = 1, + RGA_ALPHA_PER_PIXEL_GLOBAL = 2, +}; + +enum rga_alpha_cal_mode { + RGA_ALPHA_SATURATION = 0, + RGA_ALPHA_NO_SATURATION = 1, +}; + +enum rga_factor_mode { + RGA_ALPHA_ZERO = 0, + RGA_ALPHA_ONE = 1, + /* + * When used as a factor for the SRC channel, it indicates + * the use of the DST channel's alpha value, and vice versa. + */ + RGA_ALPHA_OPPOSITE = 2, + RGA_ALPHA_OPPOSITE_INVERSE = 3, + RGA_ALPHA_OWN = 4, +}; + +enum rga_color_mode { + RGA_ALPHA_PRE_MULTIPLIED = 0, + RGA_ALPHA_NO_PRE_MULTIPLIED = 1, +}; + +enum rga_alpha_blend_mode { + RGA_ALPHA_NONE = 0, + RGA_ALPHA_BLEND_SRC, + RGA_ALPHA_BLEND_DST, + RGA_ALPHA_BLEND_SRC_OVER, + RGA_ALPHA_BLEND_DST_OVER, + RGA_ALPHA_BLEND_SRC_IN, + RGA_ALPHA_BLEND_DST_IN, + RGA_ALPHA_BLEND_SRC_OUT, + RGA_ALPHA_BLEND_DST_OUT, + RGA_ALPHA_BLEND_SRC_ATOP, + RGA_ALPHA_BLEND_DST_ATOP, + RGA_ALPHA_BLEND_XOR, + RGA_ALPHA_BLEND_CLEAR, +}; + +#define RGA_SCHED_PRIORITY_DEFAULT 0 +#define RGA_SCHED_PRIORITY_MAX 6 + +#define RGA_VERSION_SIZE 16 +#define RGA_HW_SIZE 5 + +struct rga_version_t { + uint32_t major; + uint32_t minor; + uint32_t revision; + uint8_t str[RGA_VERSION_SIZE]; +}; + +struct rga_hw_versions_t { + struct rga_version_t version[RGA_HW_SIZE]; + uint32_t size; +}; + +struct rga_memory_parm { + uint32_t width; + uint32_t height; + uint32_t format; + + uint32_t size; +}; + +struct rga_external_buffer { + uint64_t memory; + uint32_t type; + + uint32_t handle; + struct rga_memory_parm memory_parm; + + uint8_t reserve[252]; +}; + +struct rga_buffer_pool { + uint64_t buffers_ptr; + uint32_t size; +}; + +struct rga_mmu_info_t { + unsigned long src0_base_addr; + unsigned long src1_base_addr; + unsigned long dst_base_addr; + unsigned long els_base_addr; + + /* [0] mmu enable [1] flush [2] prefetch_en [3] prefetch dir */ + u8 src0_mmu_flag; + u8 src1_mmu_flag; + u8 dst_mmu_flag; + u8 els_mmu_flag; +}; + +struct rga_color_fill_t { + int16_t gr_x_a; + int16_t gr_y_a; + int16_t gr_x_b; + int16_t gr_y_b; + int16_t gr_x_g; + int16_t gr_y_g; + int16_t gr_x_r; + int16_t gr_y_r; +}; + +/***************************************/ +/* porting from rga.h for msg convert */ +/***************************************/ + +struct rga_fading_t { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t res; +}; + +struct rga_mmu_t { + uint8_t mmu_en; + uint64_t base_addr; + /* + * [0] mmu enable [1] src_flush [2] dst_flush + * [3] CMD_flush [4~5] page size + */ + uint32_t mmu_flag; +}; + +struct rga_rect_t { + uint16_t xmin; + /* width - 1 */ + uint16_t xmax; + uint16_t ymin; + /* height - 1 */ + uint16_t ymax; +}; + +struct rga_point_t { + uint16_t x; + uint16_t y; +}; + +struct rga_line_draw_t { + /* LineDraw_start_point */ + struct rga_point_t start_point; + /* LineDraw_end_point */ + struct rga_point_t end_point; + /* LineDraw_color */ + uint32_t color; + /* (enum) LineDrawing mode sel */ + uint32_t flag; + /* range 1~16 */ + uint32_t line_width; +}; + +/* color space convert coefficient. */ +struct rga_csc_coe { + int16_t r_v; + int16_t g_y; + int16_t b_u; + int32_t off; +}; + +struct rga_full_csc { + uint8_t flag; + struct rga_csc_coe coe_y; + struct rga_csc_coe coe_u; + struct rga_csc_coe coe_v; +}; + +struct rga_csc_range { + uint16_t max; + uint16_t min; +}; + +struct rga_csc_clip { + struct rga_csc_range y; + struct rga_csc_range uv; +}; + +struct rga_mosaic_info { + uint8_t enable; + uint8_t mode; +}; + +/* MAX(min, (max - channel_value)) */ +struct rga_osd_invert_factor { + uint8_t alpha_max; + uint8_t alpha_min; + uint8_t yg_max; + uint8_t yg_min; + uint8_t crb_max; + uint8_t crb_min; +}; + +struct rga_color { + union { + struct { + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; + }; + uint32_t value; + }; +}; + +struct rga_osd_bpp2 { + uint8_t ac_swap; // ac swap flag + // 0: CA + // 1: AC + uint8_t endian_swap; // rgba2bpp endian swap + // 0: Big endian + // 1: Little endian + struct rga_color color0; + struct rga_color color1; +}; + +struct rga_osd_mode_ctrl { + uint8_t mode; // OSD cal mode: + // 0b'1: statistics mode + // 1b'1: auto inversion overlap mode + uint8_t direction_mode; // horizontal or vertical + // 0: horizontal + // 1: vertical + uint8_t width_mode; // using @fix_width or LUT width + // 0: fix width + // 1: LUT width + uint16_t block_fix_width; // OSD block fixed width + // real width = (fix_width + 1) * 2 + uint8_t block_num; // OSD block num + uint16_t flags_index; // auto invert flags index + + /* invertion config */ + uint8_t color_mode; // selete color + // 0: src1 color + // 1: config data color + uint8_t invert_flags_mode; // invert flag selete + // 0: use RAM flag + // 1: usr last result + uint8_t default_color_sel; // default color mode + // 0: default is bright + // 1: default is dark + uint8_t invert_enable; // invert channel enable + // 1 << 0: alpha enable + // 1 << 1: Y/G disable + // 1 << 3: C/RB disable + uint8_t invert_mode; // invert cal mode + // 0: normal(max-data) + // 1: swap + uint8_t invert_thresh; // if luma > thresh, osd_flag to be 1 + uint8_t unfix_index; // OSD width config index +}; + +struct rga_osd_info { + uint8_t enable; + + struct rga_osd_mode_ctrl mode_ctrl; + struct rga_osd_invert_factor cal_factor; + struct rga_osd_bpp2 bpp2_info; + + union { + struct { + uint32_t last_flags0; + uint32_t last_flags1; + }; + uint64_t last_flags; + }; + + union { + struct { + uint32_t cur_flags0; + uint32_t cur_flags1; + }; + uint64_t cur_flags; + }; +}; + +struct rga_pre_intr_info { + uint8_t enable; + + uint8_t read_intr_en; + uint8_t write_intr_en; + uint8_t read_hold_en; + uint32_t read_threshold; + uint32_t write_start; + uint32_t write_step; +}; + +struct rga_win_info_t { + /* yrgb mem addr */ + unsigned long yrgb_addr; + /* cb/cr mem addr */ + unsigned long uv_addr; + /* cr mem addr */ + unsigned long v_addr; + /* definition by RK_FORMAT */ + unsigned int format; + + unsigned short src_act_w; + unsigned short src_act_h; + + unsigned short dst_act_w; + unsigned short dst_act_h; + + unsigned short x_offset; + unsigned short y_offset; + + unsigned short vir_w; + unsigned short vir_h; + + unsigned short y2r_mode; + unsigned short r2y_mode; + + unsigned short rotate_mode; + /* RASTER or FBCD or TILE */ + unsigned short rd_mode; + + unsigned short is_10b_compact; + unsigned short is_10b_endian; + + unsigned short enable; +}; + +struct rga_img_info_t { + /* yrgb mem addr */ + uint64_t yrgb_addr; + /* cb/cr mem addr */ + uint64_t uv_addr; + /* cr mem addr */ + uint64_t v_addr; + /* definition by RK_FORMAT */ + uint32_t format; + + uint16_t act_w; + uint16_t act_h; + uint16_t x_offset; + uint16_t y_offset; + + uint16_t vir_w; + uint16_t vir_h; + + uint16_t endian_mode; + /* useless */ + uint16_t alpha_swap; + + /* used by RGA3 */ + uint16_t rotate_mode; + uint16_t rd_mode; + + uint16_t compact_mode; + uint16_t is_10b_endian; + + uint16_t enable; +}; + +struct rga_feature { + uint32_t global_alpha_en:1; + uint32_t full_csc_clip_en:1; + uint32_t user_close_fence:1; +}; + +struct rga_req { + /* (enum) process mode sel */ + uint8_t render_mode; + + struct rga_img_info_t src; + struct rga_img_info_t dst; + struct rga_img_info_t pat; + + /* rop4 mask addr */ + uint64_t rop_mask_addr; + /* LUT addr */ + uint64_t LUT_addr; + + /* dst clip window default value is dst_vir */ + /* value from [0, w-1] / [0, h-1]*/ + struct rga_rect_t clip; + + /* dst angle default value 0 16.16 scan from table */ + int32_t sina; + /* dst angle default value 0 16.16 scan from table */ + int32_t cosa; + + /* alpha rop process flag */ + /* ([0] = 1 alpha_rop_enable) */ + /* ([1] = 1 rop enable) */ + /* ([2] = 1 fading_enable) */ + /* ([3] = 1 PD_enable) */ + /* ([4] = 1 alpha cal_mode_sel) */ + /* ([5] = 1 dither_enable) */ + /* ([6] = 1 gradient fill mode sel) */ + /* ([7] = 1 AA_enable) */ + uint16_t alpha_rop_flag; + + /* 0 nearst / 1 bilnear / 2 bicubic */ + uint8_t scale_mode; + + /* color key max */ + uint32_t color_key_max; + /* color key min */ + uint32_t color_key_min; + + /* foreground color */ + uint32_t fg_color; + /* background color */ + uint32_t bg_color; + + /* color fill use gradient */ + struct rga_color_fill_t gr_color; + + struct rga_line_draw_t line_draw_info; + + struct rga_fading_t fading; + + /* porter duff alpha mode sel */ + uint8_t PD_mode; + + /* legacy: global alpha value */ + uint8_t alpha_global_value; + + /* rop2/3/4 code scan from rop code table*/ + uint16_t rop_code; + + /* [2] 0 blur 1 sharp / [1:0] filter_type*/ + uint8_t bsfilter_flag; + + /* (enum) color palette 0/1bpp, 1/2bpp 2/4bpp 3/8bpp*/ + uint8_t palette_mode; + + /* (enum) BT.601 MPEG / BT.601 JPEG / BT.709 */ + uint8_t yuv2rgb_mode; + + /* 0/big endian 1/little endian*/ + uint8_t endian_mode; + + /* (enum) rotate mode */ + /* 0x0, no rotate */ + /* 0x1, rotate */ + /* 0x2, x_mirror */ + /* 0x3, y_mirror */ + uint8_t rotate_mode; + + /* 0 solid color / 1 pattern color */ + uint8_t color_fill_mode; + + /* mmu information */ + struct rga_mmu_t mmu_info; + + /* ([0~1] alpha mode) */ + /* ([2~3] rop mode) */ + /* ([4] zero mode en) */ + /* ([5] dst alpha mode) */ + /* ([6] alpha output mode sel) 0 src / 1 dst*/ + uint8_t alpha_rop_mode; + + uint8_t src_trans_mode; + + uint8_t dither_mode; + + /* full color space convert */ + struct rga_full_csc full_csc; + + int32_t in_fence_fd; + uint8_t core; + uint8_t priority; + int32_t out_fence_fd; + + uint8_t handle_flag; + + /* RGA2 1106 add */ + struct rga_mosaic_info mosaic_info; + + uint8_t uvhds_mode; + uint8_t uvvds_mode; + + struct rga_osd_info osd_info; + + struct rga_pre_intr_info pre_intr_info; + + /* global alpha */ + uint8_t fg_global_alpha; + uint8_t bg_global_alpha; + + struct rga_feature feature; + + struct rga_csc_clip full_csc_clip; + + uint8_t reservr[43]; +}; + +struct rga_alpha_config { + bool enable; + bool fg_pre_multiplied; + bool bg_pre_multiplied; + bool fg_pixel_alpha_en; + bool bg_pixel_alpha_en; + bool fg_global_alpha_en; + bool bg_global_alpha_en; + uint16_t fg_global_alpha_value; + uint16_t bg_global_alpha_value; + enum rga_alpha_blend_mode mode; +}; + +struct rga2_req { + /* (enum) process mode sel */ + u8 render_mode; + + /* active window */ + struct rga_img_info_t src; + struct rga_img_info_t src1; + struct rga_img_info_t dst; + struct rga_img_info_t pat; + + /* rop4 mask addr */ + unsigned long rop_mask_addr; + /* LUT addr */ + unsigned long LUT_addr; + + u32 rop_mask_stride; + + /* 0: SRC + DST => DST */ + /* 1: SRC + SRC1 => DST */ + u8 bitblt_mode; + + /* [1:0] */ + /* 0 degree 0x0 */ + /* 90 degree 0x1 */ + /* 180 degree 0x2 */ + /* 270 degree 0x3 */ + /* [5:4] */ + /* none 0x0 */ + /* x_mirror 0x1 */ + /* y_mirror 0x2 */ + /* x_mirror + y_mirror 0x3 */ + u8 rotate_mode; + + /* alpha rop process flag */ + /* ([0] = 1 alpha_rop_enable) */ + /* ([1] = 1 rop enable) */ + /* ([2] = 1 fading_enable) */ + /* ([3] = 1 alpha cal_mode_sel) */ + /* ([4] = 1 src_dither_up_enable) */ + /* ([5] = 1 dst_dither_up_enable) */ + /* ([6] = 1 dither_down_enable) */ + /* ([7] = 1 gradient fill mode sel) */ + u16 alpha_rop_flag; + + struct rga_alpha_config alpha_config; + + /* 0 1 2 3 */ + u8 scale_bicu_mode; + + u32 color_key_max; + u32 color_key_min; + + /* foreground color */ + u32 fg_color; + /* background color */ + u32 bg_color; + + u8 color_fill_mode; + /* color fill use gradient */ + struct rga_color_fill_t gr_color; + + /* Fading value */ + u8 fading_alpha_value; + u8 fading_r_value; + u8 fading_g_value; + u8 fading_b_value; + + /* src global alpha value */ + u8 src_a_global_val; + /* dst global alpha value */ + u8 dst_a_global_val; + + /* rop mode select 0 : rop2 1 : rop3 2 : rop4 */ + u8 rop_mode; + /* rop2/3/4 code */ + u16 rop_code; + + /* (enum) color palette 0/1bpp, 1/2bpp 2/4bpp 3/8bpp*/ + u8 palette_mode; + + /* (enum) BT.601 MPEG / BT.601 JPEG / BT.709 */ + u8 yuv2rgb_mode; + + u8 full_csc_en; + + /* 0/little endian 1/big endian */ + u8 endian_mode; + + u8 CMD_fin_int_enable; + + /* mmu information */ + struct rga_mmu_info_t mmu_info; + + u8 alpha_zero_key; + u8 src_trans_mode; + + /* useless */ + u8 alpha_swp; + u8 dither_mode; + + u8 rgb2yuv_mode; + + /* RGA2 1106 add */ + struct rga_mosaic_info mosaic_info; + + uint8_t yin_yout_en; + + uint8_t uvhds_mode; + uint8_t uvvds_mode; + + struct rga_osd_info osd_info; +}; + +struct rga3_req { + /* (enum) process mode sel */ + u8 render_mode; + + struct rga_win_info_t win0; + struct rga_win_info_t wr; + struct rga_win_info_t win1; + + /* rop4 mask addr */ + unsigned long rop_mask_addr; + unsigned long LUT_addr; + + u32 rop_mask_stride; + + u8 bitblt_mode; + u8 rotate_mode; + + u16 alpha_rop_flag; + + struct rga_alpha_config alpha_config; + + /* for abb mode presever alpha. */ + bool abb_alpha_pass; + + u8 scale_bicu_mode; + + u32 color_key_max; + u32 color_key_min; + + u32 fg_color; + u32 bg_color; + + u8 color_fill_mode; + struct rga_color_fill_t gr_color; + + u8 fading_alpha_value; + u8 fading_r_value; + u8 fading_g_value; + u8 fading_b_value; + + /* win0 global alpha value */ + u8 win0_a_global_val; + /* win1 global alpha value */ + u8 win1_a_global_val; + + u8 rop_mode; + u16 rop_code; + + u8 palette_mode; + + u8 yuv2rgb_mode; + + u8 endian_mode; + + u8 CMD_fin_int_enable; + + struct rga_mmu_info_t mmu_info; + + u8 alpha_zero_key; + u8 src_trans_mode; + + u8 alpha_swp; + u8 dither_mode; + + u8 rgb2yuv_mode; +}; + +struct rga_video_frame_info { + uint32_t x_offset; + uint32_t y_offset; + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t vir_w; + uint32_t vir_h; + uint32_t rd_mode; +}; + +struct rga_mpi_job_t { + struct dma_buf *dma_buf_src0; + struct dma_buf *dma_buf_src1; + struct dma_buf *dma_buf_dst; + + struct rga_video_frame_info *src; + struct rga_video_frame_info *pat; + struct rga_video_frame_info *dst; + struct rga_video_frame_info *output; + + int ctx_id; +}; + +struct rga_user_request { + uint64_t task_ptr; + uint32_t task_num; + uint32_t id; + uint32_t sync_mode; + uint32_t release_fence_fd; + + uint32_t mpi_config_flags; + + uint32_t acquire_fence_fd; + + uint8_t reservr[120]; +}; + +int rga_mpi_commit(struct rga_mpi_job_t *mpi_job); + +#endif /*_RGA_DRIVER_H_*/ diff --git a/drivers/video/rockchip/rga3/include/rga2_reg_info.h b/drivers/video/rockchip/rga3/include/rga2_reg_info.h new file mode 100644 index 000000000..add2f41bd --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga2_reg_info.h @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __REG2_INFO_H__ +#define __REG2_INFO_H__ + +#include "rga_drv.h" + +#define RGA2_SYS_REG_BASE 0x000 +#define RGA2_CSC_REG_BASE 0x060 +#define RGA2_CMD_REG_BASE 0x100 + +/* sys reg */ +#define RGA2_SYS_CTRL 0x000 +#define RGA2_CMD_CTRL 0x004 +#define RGA2_CMD_BASE 0x008 +#define RGA2_STATUS1 0x00c +#define RGA2_INT 0x010 +#define RGA2_MMU_CTRL0 0x014 +#define RGA2_MMU_CMD_BASE 0x018 +#define RGA2_STATUS2 0x01c +#define RGA2_VERSION_NUM 0x028 +#define RGA2_READ_LINE_CNT 0x030 +#define RGA2_WRITE_LINE_CNT 0x034 +#define RGA2_LINE_CNT 0x038 +#define RGA2_PERF_CTRL0 0x040 + +/* full csc reg */ +#define RGA2_DST_CSC_00 0x060 +#define RGA2_DST_CSC_01 0x064 +#define RGA2_DST_CSC_02 0x068 +#define RGA2_DST_CSC_OFF0 0x06c +#define RGA2_DST_CSC_10 0x070 +#define RGA2_DST_CSC_11 0x074 +#define RGA2_DST_CSC_12 0x078 +#define RGA2_DST_CSC_OFF1 0x07c +#define RGA2_DST_CSC_20 0x080 +#define RGA2_DST_CSC_21 0x084 +#define RGA2_DST_CSC_22 0x088 +#define RGA2_DST_CSC_OFF2 0x08c + +/* osd read-back reg */ +#define RGA2_OSD_CUR_FLAGS0 0x090 +#define RGA2_OSD_CUR_FLAGS1 0x09c + +/* mode ctrl */ +#define RGA2_MODE_CTRL_OFFSET 0x000 +#define RGA2_SRC_INFO_OFFSET 0x004 +#define RGA2_SRC_BASE0_OFFSET 0x008 +#define RGA2_SRC_BASE1_OFFSET 0x00c +#define RGA2_SRC_BASE2_OFFSET 0x010 +#define RGA2_SRC_BASE3_OFFSET 0x014 +#define RGA2_SRC_VIR_INFO_OFFSET 0x018 +#define RGA2_SRC_ACT_INFO_OFFSET 0x01c +#define RGA2_SRC_X_FACTOR_OFFSET 0x020 +#define RGA2_OSD_CTRL0_OFFSET 0x020 // repeat +#define RGA2_SRC_Y_FACTOR_OFFSET 0x024 +#define RGA2_OSD_CTRL1_OFFSET 0x024 // repeat +#define RGA2_SRC_BG_COLOR_OFFSET 0x028 +#define RGA2_OSD_COLOR0_OFFSET 0x028 // repeat +#define RGA2_SRC_FG_COLOR_OFFSET 0x02c +#define RGA2_OSD_COLOR1_OFFSET 0x02c // repeat +#define RGA2_SRC_TR_COLOR0_OFFSET 0x030 +#define RGA2_CF_GR_A_OFFSET 0x030 // repeat +#define RGA2_OSD_LAST_FLAGS0_OFFSET 0x030 // repeat +#define RGA2_MOSAIC_MODE_OFFSET 0x030 // repeat +#define RGA2_SRC_TR_COLOR1_OFFSET 0x034 +#define RGA2_CF_GR_B_OFFSET 0x034 // repeat +#define RGA2_OSD_LAST_FLAGS1_OFFSET 0x034 // repeat +#define RGA2_DST_INFO_OFFSET 0x038 +#define RGA2_DST_BASE0_OFFSET 0x03c +#define RGA2_DST_BASE1_OFFSET 0x040 +#define RGA2_DST_BASE2_OFFSET 0x044 +#define RGA2_DST_VIR_INFO_OFFSET 0x048 +#define RGA2_DST_ACT_INFO_OFFSET 0x04c +#define RGA2_ALPHA_CTRL0_OFFSET 0x050 +#define RGA2_ALPHA_CTRL1_OFFSET 0x054 +#define RGA2_FADING_CTRL_OFFSET 0x058 +#define RGA2_PAT_CON_OFFSET 0x05c +#define RGA2_ROP_CTRL0_OFFSET 0x060 +#define RGA2_CF_GR_G_OFFSET 0x060 // repeat +#define RGA2_DST_Y4MAP_LUT0_OFFSET 0x060 // repeat +#define RGA2_DST_QUANTIZE_SCALE_OFFSET 0x060 // repeat +#define RGA2_OSD_INVERTSION_CAL0_OFFSET 0x060 // repeat +#define RGA2_ROP_CTRL1_OFFSET 0x064 +#define RGA2_CF_GR_R_OFFSET 0x064 // repeat +#define RGA2_DST_Y4MAP_LUT1_OFFSET 0x064 // repeat +#define RGA2_DST_QUANTIZE_OFFSET_OFFSET 0x064 // repeat +#define RGA2_OSD_INVERTSION_CAL1_OFFSET 0x064 // repeat +#define RGA2_MASK_BASE_OFFSET 0x068 +#define RGA2_MMU_CTRL1_OFFSET 0x06c +#define RGA2_MMU_SRC_BASE_OFFSET 0x070 +#define RGA2_MMU_SRC1_BASE_OFFSET 0x074 +#define RGA2_MMU_DST_BASE_OFFSET 0x078 +#define RGA2_MMU_ELS_BASE_OFFSET 0x07c + +/*RGA_SYS*/ +#define m_RGA2_SYS_CTRL_SRC0YUV420SP_RD_OPT_DIS (0x1 << 12) +#define m_RGA2_SYS_CTRL_DST_WR_OPT_DIS (0x1 << 11) +#define m_RGA2_SYS_CTRL_CMD_CONTINUE_P (0x1 << 10) +#define m_RGA2_SYS_CTRL_HOLD_MODE_EN (0x1 << 9) +#define m_RGA2_SYS_CTRL_RST_HANDSAVE_P (0x1 << 7) +#define m_RGA2_SYS_CTRL_RST_PROTECT_P (0x1 << 6) +#define m_RGA2_SYS_CTRL_AUTO_RST (0x1 << 5) +#define m_RGA2_SYS_CTRL_CCLK_SRESET_P (0x1 << 4) +#define m_RGA2_SYS_CTRL_ACLK_SRESET_P (0x1 << 3) +#define m_RGA2_SYS_CTRL_AUTO_CKG (0x1 << 2) +#define m_RGA2_SYS_CTRL_CMD_MODE (0x1 << 1) +#define m_RGA2_SYS_CTRL_CMD_OP_ST_P (0x1 << 0) + +#define s_RGA2_SYS_CTRL_CMD_CONTINUE(x) ((x & 0x1) << 10) +#define s_RGA2_SYS_CTRL_HOLD_MODE_EN(x) ((x & 0x1) << 9) +#define s_RGA2_SYS_CTRL_CMD_MODE(x) ((x & 0x1) << 1) + +/* RGA_CMD_CTRL */ +#define m_RGA2_CMD_CTRL_INCR_NUM (0x3ff << 3) +#define m_RGA2_CMD_CTRL_STOP (0x1 << 2) +#define m_RGA2_CMD_CTRL_INCR_VALID_P (0x1 << 1) +#define m_RGA2_CMD_CTRL_CMD_LINE_ST_P (0x1 << 0) + +/* RGA_STATUS1 */ +#define m_RGA2_STATUS1_SW_CMD_TOTAL_NUM (0xfff << 8) +#define m_RGA2_STATUS1_SW_CMD_CUR_NUM (0xfff << 8) +#define m_RGA2_STATUS1_SW_RGA_STA (0x1 << 0) + +/*RGA_INT*/ +#define m_RGA2_INT_LINE_WR_CLEAR (1 << 16) +#define m_RGA2_INT_LINE_RD_CLEAR (1 << 15) +#define m_RGA2_INT_LINE_WR_EN (1 << 14) +#define m_RGA2_INT_LINE_RD_EN (1 << 13) +#define m_RGA2_INT_WRITE_CNT_FLAG (1 << 12) +#define m_RGA2_INT_READ_CNT_FLAG (1 << 11) +#define m_RGA2_INT_ALL_CMD_DONE_INT_EN (1 << 10) +#define m_RGA2_INT_MMU_INT_EN (1 << 9) +#define m_RGA2_INT_ERROR_INT_EN (1 << 8) +#define m_RGA2_INT_NOW_CMD_DONE_INT_CLEAR (1 << 7) +#define m_RGA2_INT_ALL_CMD_DONE_INT_CLEAR (1 << 6) +#define m_RGA2_INT_MMU_INT_CLEAR (1 << 5) +#define m_RGA2_INT_ERROR_INT_CLEAR (1 << 4) +#define m_RGA2_INT_CUR_CMD_DONE_INT_FLAG (1 << 3) +#define m_RGA2_INT_ALL_CMD_DONE_INT_FLAG (1 << 2) +#define m_RGA2_INT_MMU_INT_FLAG (1 << 1) +#define m_RGA2_INT_ERROR_INT_FLAG (1 << 0) + +#define m_RGA2_INT_ERROR_FLAG_MASK \ + ( \ + m_RGA2_INT_MMU_INT_FLAG | \ + m_RGA2_INT_ERROR_INT_FLAG \ + ) +#define m_RGA2_INT_ERROR_CLEAR_MASK \ + ( \ + m_RGA2_INT_MMU_INT_CLEAR | \ + m_RGA2_INT_ERROR_INT_CLEAR \ +) +#define m_RGA2_INT_ERROR_ENABLE_MASK \ + ( \ + m_RGA2_INT_MMU_INT_EN | \ + m_RGA2_INT_ERROR_INT_EN \ + ) + +#define s_RGA2_INT_LINE_WR_CLEAR(x) ((x & 0x1) << 16) +#define s_RGA2_INT_LINE_RD_CLEAR(x) ((x & 0x1) << 15) +#define s_RGA2_INT_LINE_WR_EN(x) ((x & 0x1) << 14) +#define s_RGA2_INT_LINE_RD_EN(x) ((x & 0x1) << 13) +#define s_RGA2_INT_ALL_CMD_DONE_INT_EN(x) ((x & 0x1) << 10) +#define s_RGA2_INT_MMU_INT_EN(x) ((x & 0x1) << 9) +#define s_RGA2_INT_ERROR_INT_EN(x) ((x & 0x1) << 8) +#define s_RGA2_INT_NOW_CMD_DONE_INT_CLEAR(x) ((x & 0x1) << 7) +#define s_RGA2_INT_ALL_CMD_DONE_INT_CLEAR(x) ((x & 0x1) << 6) +#define s_RGA2_INT_MMU_INT_CLEAR(x) ((x & 0x1) << 5) +#define s_RGA2_INT_ERROR_INT_CLEAR(x) ((x & 0x1) << 4) + +/* RGA_STATUS2 hardware status */ +#define m_RGA2_STATUS2_RPP_MKRAM_RREADY (0x2 << 11) +#define m_RGA2_STATUS2_DSTRPP_OUTBUF_RREADY (0x1f << 6) +#define m_RGA2_STATUS2_SRCRPP_OUTBUF_RREADY (0xf << 2) +#define m_RGA2_STATUS2_BUS_ERROR (0x1 << 1) +#define m_RGA2_STATUS2_RPP_ERROR (0x1 << 0) + +/* RGA_READ_LINE_CNT_TH */ +#define m_RGA2_READ_LINE_SW_INTR_LINE_RD_TH (0x1fff << 0) + +#define s_RGA2_READ_LINE_SW_INTR_LINE_RD_TH(x) ((x & 0x1fff) << 0) + +/* RGA_WRITE_LINE_CNT_TN */ +#define m_RGA2_WRITE_LINE_SW_INTR_LINE_WR_START (0x1fff << 0) +#define m_RGA2_WRITE_LINE_SW_INTR_LINE_WR_STEP (0x1fff << 16) + +#define s_RGA2_WRITE_LINE_SW_INTR_LINE_WR_START(x) ((x & 0x1fff) << 0) +#define s_RGA2_WRITE_LINE_SW_INTR_LINE_WR_STEP(x) ((x & 0x1fff) << 16) + +/* RGA_MODE_CTRL */ +#define m_RGA2_MODE_CTRL_SW_RENDER_MODE (0x7 << 0) +#define m_RGA2_MODE_CTRL_SW_BITBLT_MODE (0x1 << 3) +#define m_RGA2_MODE_CTRL_SW_CF_ROP4_PAT (0x1 << 4) +#define m_RGA2_MODE_CTRL_SW_ALPHA_ZERO_KET (0x1 << 5) +#define m_RGA2_MODE_CTRL_SW_GRADIENT_SAT (0x1 << 6) +#define m_RGA2_MODE_CTRL_SW_INTR_CF_E (0x1 << 7) +#define m_RGA2_MODE_CTRL_SW_OSD_E (0x1<<8) +#define m_RGA2_MODE_CTRL_SW_MOSAIC_EN (0x1<<9) +#define m_RGA2_MODE_CTRL_SW_YIN_YOUT_EN (0x1<<10) + +#define s_RGA2_MODE_CTRL_SW_RENDER_MODE(x) ((x & 0x7) << 0) +#define s_RGA2_MODE_CTRL_SW_BITBLT_MODE(x) ((x & 0x1) << 3) +#define s_RGA2_MODE_CTRL_SW_CF_ROP4_PAT(x) ((x & 0x1) << 4) +#define s_RGA2_MODE_CTRL_SW_ALPHA_ZERO_KET(x) ((x & 0x1) << 5) +#define s_RGA2_MODE_CTRL_SW_GRADIENT_SAT(x) ((x & 0x1) << 6) +#define s_RGA2_MODE_CTRL_SW_INTR_CF_E(x) ((x & 0x1) << 7) +#define s_RGA2_MODE_CTRL_SW_OSD_E(x) ((x & 0x1) << 8) +#define s_RGA2_MODE_CTRL_SW_MOSAIC_EN(x) ((x & 0x1) << 9) +#define s_RGA2_MODE_CTRL_SW_YIN_YOUT_EN(x) ((x & 0x1) << 10) +/* RGA_SRC_INFO */ +#define m_RGA2_SRC_INFO_SW_SRC_FMT (0xf << 0) +#define m_RGA2_SRC_INFO_SW_SW_SRC_RB_SWAP (0x1 << 4) +#define m_RGA2_SRC_INFO_SW_SW_SRC_ALPHA_SWAP (0x1 << 5) +#define m_RGA2_SRC_INFO_SW_SW_SRC_UV_SWAP (0x1 << 6) +#define m_RGA2_SRC_INFO_SW_SW_CP_ENDIAN (0x1 << 7) +#define m_RGA2_SRC_INFO_SW_SW_SRC_CSC_MODE (0x3 << 8) +#define m_RGA2_SRC_INFO_SW_SW_SRC_ROT_MODE (0x3 << 10) +#define m_RGA2_SRC_INFO_SW_SW_SRC_MIR_MODE (0x3 << 12) +#define m_RGA2_SRC_INFO_SW_SW_SRC_HSCL_MODE (0x3 << 14) +#define m_RGA2_SRC_INFO_SW_SW_SRC_VSCL_MODE (0x3 << 16) +#define m_RGA2_SRC_INFO_SW_SW_SRC_TRANS_MODE (0x1 << 18) +#define m_RGA2_SRC_INFO_SW_SW_SRC_TRANS_E (0xf << 19) +#define m_RGA2_SRC_INFO_SW_SW_SRC_DITHER_UP_E (0x1 << 23) +#define m_RGA2_SRC_INFO_SW_SW_SRC_SCL_FILTER (0x3 << 24) +#define m_RGA2_SRC_INFO_SW_SW_VSP_MODE_SEL (0x1 << 26) +#define m_RGA2_SRC_INFO_SW_SW_YUV10_E (0x1 << 27) +#define m_RGA2_SRC_INFO_SW_SW_YUV10_ROUND_E (0x1 << 28) + + +#define s_RGA2_SRC_INFO_SW_SRC_FMT(x) ((x & 0xf) << 0) +#define s_RGA2_SRC_INFO_SW_SW_SRC_RB_SWAP(x) ((x & 0x1) << 4) +#define s_RGA2_SRC_INFO_SW_SW_SRC_ALPHA_SWAP(x) ((x & 0x1) << 5) +#define s_RGA2_SRC_INFO_SW_SW_SRC_UV_SWAP(x) ((x & 0x1) << 6) +#define s_RGA2_SRC_INFO_SW_SW_CP_ENDAIN(x) ((x & 0x1) << 7) +#define s_RGA2_SRC_INFO_SW_SW_SRC_CSC_MODE(x) ((x & 0x3) << 8) +#define s_RGA2_SRC_INFO_SW_SW_SRC_ROT_MODE(x) ((x & 0x3) << 10) +#define s_RGA2_SRC_INFO_SW_SW_SRC_MIR_MODE(x) ((x & 0x3) << 12) +#define s_RGA2_SRC_INFO_SW_SW_SRC_HSCL_MODE(x) ((x & 0x3) << 14) +#define s_RGA2_SRC_INFO_SW_SW_SRC_VSCL_MODE(x) ((x & 0x3) << 16) + +#define s_RGA2_SRC_INFO_SW_SW_SRC_TRANS_MODE(x) ((x & 0x1) << 18) +#define s_RGA2_SRC_INFO_SW_SW_SRC_TRANS_E(x) ((x & 0xf) << 19) +#define s_RGA2_SRC_INFO_SW_SW_SRC_DITHER_UP_E(x) ((x & 0x1) << 23) +#define s_RGA2_SRC_INFO_SW_SW_SRC_SCL_FILTER(x) ((x & 0x3) << 24) +#define s_RGA2_SRC_INFO_SW_SW_VSP_MODE_SEL(x) ((x & 0x1) << 26) +#define s_RGA2_SRC_INFO_SW_SW_YUV10_E(x) ((x & 0x1) << 27) +#define s_RGA2_SRC_INFO_SW_SW_YUV10_ROUND_E(x) ((x & 0x1) << 28) + +/* RGA_SRC_VIR_INFO */ +#define m_RGA2_SRC_VIR_INFO_SW_SRC_VIR_STRIDE (0x7fff << 0) +#define m_RGA2_SRC_VIR_INFO_SW_MASK_VIR_STRIDE (0x3ff << 16) + +#define s_RGA2_SRC_VIR_INFO_SW_SRC_VIR_STRIDE(x) ((x & 0x7fff) << 0) +#define s_RGA2_SRC_VIR_INFO_SW_MASK_VIR_STRIDE(x) ((x & 0x3ff) << 16) + + +/* RGA_SRC_ACT_INFO */ +#define m_RGA2_SRC_ACT_INFO_SW_SRC_ACT_WIDTH (0x1fff << 0) +#define m_RGA2_SRC_ACT_INFO_SW_SRC_ACT_HEIGHT (0x1fff << 16) + +#define s_RGA2_SRC_ACT_INFO_SW_SRC_ACT_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA2_SRC_ACT_INFO_SW_SRC_ACT_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA2_OSD_CTRL0 */ +#define m_RGA2_OSD_CTRL0_SW_OSD_MODE (0x3 << 0) +#define m_RGA2_OSD_CTRL0_SW_OSD_VER_MODE (0x1 << 2) +#define m_RGA2_OSD_CTRL0_SW_OSD_WIDTH_MODE (0x1 << 3) +#define m_RGA2_OSD_CTRL0_SW_OSD_BLK_NUM (0x1f << 4) +#define m_RGA2_OSD_CTRL0_SW_OSD_FLAGS_INDEX (0x3f << 10) +#define m_RGA2_OSD_CTRL0_SW_OSD_FIX_WIDTH (0x3f << 20) +#define m_RGA2_OSD_CTRL0_SW_OSD_2BPP_MODE (0x1 << 30) + +#define s_RGA2_OSD_CTRL0_SW_OSD_MODE(x) ((x & 0x3) << 0) +#define s_RGA2_OSD_CTRL0_SW_OSD_VER_MODE(x) ((x & 0x1) << 2) +#define s_RGA2_OSD_CTRL0_SW_OSD_WIDTH_MODE(x) ((x & 0x1) << 3) +#define s_RGA2_OSD_CTRL0_SW_OSD_BLK_NUM(x) ((x & 0x1f) << 4) +#define s_RGA2_OSD_CTRL0_SW_OSD_FLAGS_INDEX(x) ((x & 0x3ff) << 10) +#define s_RGA2_OSD_CTRL0_SW_OSD_FIX_WIDTH(x) ((x & 0x3ff) << 20) +#define s_RGA2_OSD_CTRL0_SW_OSD_2BPP_MODE(x) ((x & 0x1) << 30) + +/* RGA2_OSD_CTRL1 */ +#define m_RGA2_OSD_CTRL1_SW_OSD_COLOR_SEL (0x1 << 0) +#define m_RGA2_OSD_CTRL1_SW_OSD_FLAG_SEL (0x1 << 1) +#define m_RGA2_OSD_CTRL1_SW_OSD_DEFAULT_COLOR (0x1 << 2) +#define m_RGA2_OSD_CTRL1_SW_OSD_AUTO_INVERST_MODE (0x1 << 3) +#define m_RGA2_OSD_CTRL1_SW_OSD_THRESH (0xff << 4) +#define m_RGA2_OSD_CTRL1_SW_OSD_INVERT_A_EN (0x1 << 12) +#define m_RGA2_OSD_CTRL1_SW_OSD_INVERT_Y_DIS (0x1 << 13) +#define m_RGA2_OSD_CTRL1_SW_OSD_INVERT_C_DIS (0x1 << 14) +#define m_RGA2_OSD_CTRL1_SW_OSD_UNFIX_INDEX (0xf << 16) + +#define s_RGA2_OSD_CTRL1_SW_OSD_COLOR_SEL(x) ((x & 0x1) << 0) +#define s_RGA2_OSD_CTRL1_SW_OSD_FLAG_SEL(x) ((x & 0x1) << 1) +#define s_RGA2_OSD_CTRL1_SW_OSD_DEFAULT_COLOR(x) ((x & 0x1) << 2) +#define s_RGA2_OSD_CTRL1_SW_OSD_AUTO_INVERST_MODE(x) ((x & 0x1) << 3) +#define s_RGA2_OSD_CTRL1_SW_OSD_THRESH(x) ((x & 0xff) << 4) +#define s_RGA2_OSD_CTRL1_SW_OSD_INVERT_A_EN(x) ((x & 0x1) << 12) +#define s_RGA2_OSD_CTRL1_SW_OSD_INVERT_Y_DIS(x) ((x & 0x1) << 13) +#define s_RGA2_OSD_CTRL1_SW_OSD_INVERT_C_DIS(x) ((x & 0x1) << 14) +#define s_RGA2_OSD_CTRL1_SW_OSD_UNFIX_INDEX(x) ((x & 0xf) << 16) + +/* RGA_DST_INFO */ +#define m_RGA2_DST_INFO_SW_DST_FMT (0xf << 0) +#define m_RGA2_DST_INFO_SW_DST_RB_SWAP (0x1 << 4) +#define m_RGA2_DST_INFO_SW_ALPHA_SWAP (0x1 << 5) +#define m_RGA2_DST_INFO_SW_DST_UV_SWAP (0x1 << 6) +#define m_RGA2_DST_INFO_SW_SRC1_FMT (0x7 << 7) +#define m_RGA2_DST_INFO_SW_SRC1_RB_SWP (0x1 << 10) +#define m_RGA2_DST_INFO_SW_SRC1_ALPHA_SWP (0x1 << 11) +#define m_RGA2_DST_INFO_SW_DITHER_UP_E (0x1 << 12) +#define m_RGA2_DST_INFO_SW_DITHER_DOWN_E (0x1 << 13) +#define m_RGA2_DST_INFO_SW_DITHER_MODE (0x3 << 14) +#define m_RGA2_DST_INFO_SW_DST_CSC_MODE (0x3 << 16) +#define m_RGA2_DST_INFO_SW_CSC_CLIP_MODE (0x1 << 18) +#define m_RGA2_DST_INFO_SW_DST_CSC_MODE_2 (0x1 << 19) +#define m_RGA2_DST_INFO_SW_SRC1_CSC_MODE (0x3 << 20) +#define m_RGA2_DST_INFO_SW_SRC1_CSC_CLIP_MODE (0x1 << 22) +#define m_RGA2_DST_INFO_SW_DST_UVHDS_MODE (0x1 << 23) +#define m_RGA2_DST_INFO_SW_DST_FMT_YUV400_EN (0x1 << 24) +#define m_RGA2_DST_INFO_SW_DST_FMT_Y4_EN (0x1 << 25) +#define m_RGA2_DST_INFO_SW_DST_NN_QUANTIZE_EN (0x1 << 26) +#define m_RGA2_DST_INFO_SW_DST_UVVDS_MODE (0x1 << 27) + +#define s_RGA2_DST_INFO_SW_DST_FMT(x) ((x & 0xf) << 0) +#define s_RGA2_DST_INFO_SW_DST_RB_SWAP(x) ((x & 0x1) << 4) +#define s_RGA2_DST_INFO_SW_ALPHA_SWAP(x) ((x & 0x1) << 5) +#define s_RGA2_DST_INFO_SW_DST_UV_SWAP(x) ((x & 0x1) << 6) +#define s_RGA2_DST_INFO_SW_SRC1_FMT(x) ((x & 0x7) << 7) +#define s_RGA2_DST_INFO_SW_SRC1_RB_SWP(x) ((x & 0x1) << 10) +#define s_RGA2_DST_INFO_SW_SRC1_ALPHA_SWP(x) ((x & 0x1) << 11) +#define s_RGA2_DST_INFO_SW_DITHER_UP_E(x) ((x & 0x1) << 12) +#define s_RGA2_DST_INFO_SW_DITHER_DOWN_E(x) ((x & 0x1) << 13) +#define s_RGA2_DST_INFO_SW_DITHER_MODE(x) ((x & 0x3) << 14) +#define s_RGA2_DST_INFO_SW_DST_CSC_MODE(x) ((x & 0x3) << 16) +#define s_RGA2_DST_INFO_SW_CSC_CLIP_MODE(x) ((x & 0x1) << 18) +#define s_RGA2_DST_INFO_SW_DST_CSC_MODE_2(x) ((x & 0x1) << 19) +#define s_RGA2_DST_INFO_SW_SRC1_CSC_MODE(x) ((x & 0x3) << 20) +#define s_RGA2_DST_INFO_SW_SRC1_CSC_CLIP_MODE(x) ((x & 0x1) << 22) +#define s_RGA2_DST_INFO_SW_DST_UVHDS_MODE(x) ((x & 0x1) << 23) +#define s_RGA2_DST_INFO_SW_DST_FMT_YUV400_EN(x) ((x & 0x1) << 24) +#define s_RGA2_DST_INFO_SW_DST_FMT_Y4_EN(x) ((x & 0x1) << 25) +#define s_RGA2_DST_INFO_SW_DST_NN_QUANTIZE_EN(x) ((x & 0x1) << 26) +#define s_RGA2_DST_INFO_SW_DST_UVVDS_MODE(x) ((x & 0x1) << 27) + + +/* RGA_ALPHA_CTRL0 */ +#define m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0 (0x1 << 0) +#define m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL (0x1 << 1) +#define m_RGA2_ALPHA_CTRL0_SW_ROP_MODE (0x3 << 2) +#define m_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA (0xff << 4) +#define m_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA (0xff << 12) +#define m_RGA2_ALPHA_CTRLO_SW_MASK_ENDIAN (0x1 << 20) + +#define s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0(x) ((x & 0x1) << 0) +#define s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL(x) ((x & 0x1) << 1) +#define s_RGA2_ALPHA_CTRL0_SW_ROP_MODE(x) ((x & 0x3) << 2) +#define s_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA(x) ((x & 0xff) << 4) +#define s_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA(x) ((x & 0xff) << 12) +#define s_RGA2_ALPHA_CTRLO_SW_MASK_ENDIAN(x) ((x & 0x1) << 20) + + + +/* RGA_ALPHA_CTRL1 */ +#define m_RGA2_ALPHA_CTRL1_SW_DST_COLOR_M0 (0x1 << 0) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_COLOR_M0 (0x1 << 1) +#define m_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M0 (0x7 << 2) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M0 (0x7 << 5) +#define m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M0 (0x1 << 8) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M0 (0x1 << 9) +#define m_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M0 (0x3 << 10) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M0 (0x3 << 12) +#define m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M0 (0x1 << 14) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M0 (0x1 << 15) +#define m_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M1 (0x7 << 16) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M1 (0x7 << 19) +#define m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M1 (0x1 << 22) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M1 (0x1 << 23) +#define m_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M1 (0x3 << 24) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M1 (0x3 << 26) +#define m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M1 (0x1 << 28) +#define m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M1 (0x1 << 29) + +#define s_RGA2_ALPHA_CTRL1_SW_DST_COLOR_M0(x) ((x & 0x1) << 0) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_COLOR_M0(x) ((x & 0x1) << 1) +#define s_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M0(x) ((x & 0x7) << 2) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M0(x) ((x & 0x7) << 5) +#define s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M0(x) ((x & 0x1) << 8) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M0(x) ((x & 0x1) << 9) +#define s_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M0(x) ((x & 0x3) << 10) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M0(x) ((x & 0x3) << 12) +#define s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M0(x) ((x & 0x1) << 14) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M0(x) ((x & 0x1) << 15) +#define s_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M1(x) ((x & 0x7) << 16) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M1(x) ((x & 0x7) << 19) +#define s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M1(x) ((x & 0x1) << 22) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M1(x) ((x & 0x1) << 23) +#define s_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M1(x) ((x & 0x3) << 24) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M1(x) ((x & 0x3) << 26) +#define s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M1(x) ((x & 0x1) << 28) +#define s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M1(x) ((x & 0x1) << 29) + + + +/* RGA_MMU_CTRL1 */ +#define m_RGA2_MMU_CTRL1_SW_SRC_MMU_EN (0x1 << 0) +#define m_RGA2_MMU_CTRL1_SW_SRC_MMU_FLUSH (0x1 << 1) +#define m_RGA2_MMU_CTRL1_SW_SRC_MMU_PREFETCH_EN (0x1 << 2) +#define m_RGA2_MMU_CTRL1_SW_SRC_MMU_PREFETCH_DIR (0x1 << 3) +#define m_RGA2_MMU_CTRL1_SW_SRC1_MMU_EN (0x1 << 4) +#define m_RGA2_MMU_CTRL1_SW_SRC1_MMU_FLUSH (0x1 << 5) +#define m_RGA2_MMU_CTRL1_SW_SRC1_MMU_PREFETCH_EN (0x1 << 6) +#define m_RGA2_MMU_CTRL1_SW_SRC1_MMU_PREFETCH_DIR (0x1 << 7) +#define m_RGA2_MMU_CTRL1_SW_DST_MMU_EN (0x1 << 8) +#define m_RGA2_MMU_CTRL1_SW_DST_MMU_FLUSH (0x1 << 9) +#define m_RGA2_MMU_CTRL1_SW_DST_MMU_PREFETCH_EN (0x1 << 10) +#define m_RGA2_MMU_CTRL1_SW_DST_MMU_PREFETCH_DIR (0x1 << 11) +#define m_RGA2_MMU_CTRL1_SW_ELS_MMU_EN (0x1 << 12) +#define m_RGA2_MMU_CTRL1_SW_ELS_MMU_FLUSH (0x1 << 13) + +#define s_RGA2_MMU_CTRL1_SW_SRC_MMU_EN(x) ((x & 0x1) << 0) +#define s_RGA2_MMU_CTRL1_SW_SRC_MMU_FLUSH(x) ((x & 0x1) << 1) +#define s_RGA2_MMU_CTRL1_SW_SRC_MMU_PREFETCH_EN(x) ((x & 0x1) << 2) +#define s_RGA2_MMU_CTRL1_SW_SRC_MMU_PREFETCH_DIR(x) ((x & 0x1) << 3) +#define s_RGA2_MMU_CTRL1_SW_SRC1_MMU_EN(x) ((x & 0x1) << 4) +#define s_RGA2_MMU_CTRL1_SW_SRC1_MMU_FLUSH(x) ((x & 0x1) << 5) +#define s_RGA2_MMU_CTRL1_SW_SRC1_MMU_PREFETCH_EN(x) ((x & 0x1) << 6) +#define s_RGA2_MMU_CTRL1_SW_SRC1_MMU_PREFETCH_DIR(x) ((x & 0x1) << 7) +#define s_RGA2_MMU_CTRL1_SW_DST_MMU_EN(x) ((x & 0x1) << 8) +#define s_RGA2_MMU_CTRL1_SW_DST_MMU_FLUSH(x) ((x & 0x1) << 9) +#define s_RGA2_MMU_CTRL1_SW_DST_MMU_PREFETCH_EN(x) ((x & 0x1) << 10) +#define s_RGA2_MMU_CTRL1_SW_DST_MMU_PREFETCH_DIR(x) ((x & 0x1) << 11) +#define s_RGA2_MMU_CTRL1_SW_ELS_MMU_EN(x) ((x & 0x1) << 12) +#define s_RGA2_MMU_CTRL1_SW_ELS_MMU_FLUSH(x) ((x & 0x1) << 13) + +#define RGA2_VSP_BICUBIC_LIMIT 1996 + +union rga2_color_ctrl { + uint32_t value; + struct { + uint32_t dst_color_mode:1; + uint32_t src_color_mode:1; + + uint32_t dst_factor_mode:3; + uint32_t src_factor_mode:3; + + uint32_t dst_alpha_cal_mode:1; + uint32_t src_alpha_cal_mode:1; + + uint32_t dst_blend_mode:2; + uint32_t src_blend_mode:2; + + uint32_t dst_alpha_mode:1; + uint32_t src_alpha_mode:1; + } bits; +}; + +union rga2_alpha_ctrl { + uint32_t value; + struct { + uint32_t dst_factor_mode:3; + uint32_t src_factor_mode:3; + + uint32_t dst_alpha_cal_mode:1; + uint32_t src_alpha_cal_mode:1; + + uint32_t dst_blend_mode:2; + uint32_t src_blend_mode:2; + + uint32_t dst_alpha_mode:1; + uint32_t src_alpha_mode:1; + } bits; +}; + +extern const struct rga_backend_ops rga2_ops; + +#endif + diff --git a/drivers/video/rockchip/rga3/include/rga3_reg_info.h b/drivers/video/rockchip/rga3/include/rga3_reg_info.h new file mode 100644 index 000000000..4db80cfb0 --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga3_reg_info.h @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __REG3_INFO_H__ +#define __REG3_INFO_H__ + +#include "rga_drv.h" + +/* sys reg */ +#define RGA3_SYS_CTRL 0x000 +#define RGA3_CMD_CTRL 0x004 +#define RGA3_CMD_ADDR 0x008 +#define RGA3_MI_GROUP_CTRL 0x00c +#define RGA3_ARQOS_CTRL 0x010 +#define RGA3_VERSION_NUM 0x018 +#define RGA3_VERSION_TIM 0x01c +#define RGA3_INT_EN 0x020 +#define RGA3_INT_RAW 0x024 +#define RGA3_INT_MSK 0x028 +#define RGA3_INT_CLR 0x02c +#define RGA3_RO_SRST 0x030 +#define RGA3_STATUS0 0x034 +#define RGA3_SCAN_CNT 0x038 +#define RGA3_CMD_STATE 0x040 + +/* cmd reg */ +#define RGA3_WIN0_RD_CTRL_OFFSET 0x000 +#define RGA3_WIN0_Y_BASE_OFFSET 0x010 +#define RGA3_WIN0_U_BASE_OFFSET 0x014 +#define RGA3_WIN0_V_BASE_OFFSET 0x018 +#define RGA3_WIN0_VIR_STRIDE_OFFSET 0x01c +#define RGA3_WIN0_FBC_OFF_OFFSET 0x020 +#define RGA3_WIN0_SRC_SIZE_OFFSET 0x024 +#define RGA3_WIN0_ACT_OFF_OFFSET 0x028 +#define RGA3_WIN0_ACT_SIZE_OFFSET 0x02c +#define RGA3_WIN0_DST_SIZE_OFFSET 0x030 +#define RGA3_WIN0_SCL_FAC_OFFSET 0x034 +#define RGA3_WIN0_UV_VIR_STRIDE_OFFSET 0x038 +#define RGA3_WIN1_RD_CTRL_OFFSET 0x040 +#define RGA3_WIN1_Y_BASE_OFFSET 0x050 +#define RGA3_WIN1_U_BASE_OFFSET 0x054 +#define RGA3_WIN1_V_BASE_OFFSET 0x058 +#define RGA3_WIN1_VIR_STRIDE_OFFSET 0x05c +#define RGA3_WIN1_FBC_OFF_OFFSET 0x060 +#define RGA3_WIN1_SRC_SIZE_OFFSET 0x064 +#define RGA3_WIN1_ACT_OFF_OFFSET 0x068 +#define RGA3_WIN1_ACT_SIZE_OFFSET 0x06c +#define RGA3_WIN1_DST_SIZE_OFFSET 0x070 +#define RGA3_WIN1_SCL_FAC_OFFSET 0x074 +#define RGA3_WIN1_UV_VIR_STRIDE_OFFSET 0x078 +#define RGA3_OVLP_CTRL_OFFSET 0x080 +#define RGA3_OVLP_OFF_OFFSET 0x084 +#define RGA3_OVLP_TOP_KEY_MIN_OFFSET 0x088 +#define RGA3_OVLP_TOP_KEY_MAX_OFFSET 0x08c +#define RGA3_OVLP_TOP_CTRL_OFFSET 0x090 +#define RGA3_OVLP_BOT_CTRL_OFFSET 0x094 +#define RGA3_OVLP_TOP_ALPHA_OFFSET 0x098 +#define RGA3_OVLP_BOT_ALPHA_OFFSET 0x09c +#define RGA3_WR_CTRL_OFFSET 0x0a0 +#define RGA3_WR_FBCE_CTRL_OFFSET 0x0a4 +#define RGA3_WR_VIR_STRIDE_OFFSET 0x0a8 +#define RGA3_WR_PL_VIR_STRIDE_OFFSET 0x0ac +#define RGA3_WR_Y_BASE_OFFSET 0x0b0 +#define RGA3_WR_U_BASE_OFFSET 0x0b4 +#define RGA3_WR_V_BASE_OFFSET 0x0b8 + +/* RGA3_SYS_CTRL */ +#define m_RGA3_SYS_CTRL_FRMEND_AUTO_RSTN_EN (0x1 << 11) +#define m_RGA3_SYS_CTRL_RGA_BIC_MODE (0x3 << 9) +#define m_RGA3_SYS_CTRL_RGA_RAM_CLK_ON (0x1 << 8) +#define m_RGA3_SYS_CTRL_CCLK_SRESET (0x1 << 4) +#define m_RGA3_SYS_CTRL_ACLK_SRESET (0x1 << 3) +#define m_RGA3_SYS_CTRL_RGA_LGC_CLK_ON (0x1 << 2) +#define m_RGA3_SYS_CTRL_CMD_MODE (0x1 << 1) +#define m_RGA3_SYS_CTRL_RGA_SART (0x1 << 0) + +#define s_RGA3_SYS_CTRL_RGA_BIC_MODE(x) ((x & 0x3) << 9) +#define s_RGA3_SYS_CTRL_CCLK_SRESET(x) ((x & 0x1) << 4) +#define s_RGA3_SYS_CTRL_ACLK_SRESET(x) ((x & 0x1) << 3) +#define s_RGA3_SYS_CTRL_CMD_MODE(x) ((x & 0x1) << 1) + +/* TODO: RGA3_INT_EN/RGA3_INT_RAW/RGA3_INT_MSK/RGA3_INT_CLR */ +#define m_RGA3_INT_WIN1_VOR_FIFO_REN_ERR (0x1 << 29) +#define m_RGA3_INT_WIN1_VOR_FIFO_WEN_ERR (0x1 << 28) +#define m_RGA3_INT_WIN1_HOR_FIFO_REN_ERR (0x1 << 27) +#define m_RGA3_INT_WIN1_HOR_FIFO_WEN_ERR (0x1 << 26) +#define m_RGA3_INT_WIN1_IN_FIFO_REB_ERR (0x1 << 25) +#define m_RGA3_INT_WIN1_IN_FIFO_WEN_ERR (0x1 << 24) +#define m_RGA3_INT_WIN0_VOR_FIFO_REN_ERR (0x1 << 21) +#define m_RGA3_INT_WIN0_VOR_FIFO_WEN_ERR (0x1 << 20) +#define m_RGA3_INT_WIN0_HOR_FIFO_REN_ERR (0x1 << 19) +#define m_RGA3_INT_WIN0_HOR_FIFO_WEN_ERR (0x1 << 18) +#define m_RGA3_INT_WIN0_IN_FIFO_REB_ERR (0x1 << 17) +#define m_RGA3_INT_WIN0_IN_FIFO_WEN_ERR (0x1 << 16) +#define m_RGA3_INT_RGA_MI_WR_BUS_ERR (0x1 << 15) +#define m_RGA3_INT_RGA_MI_WR_IN_HERR (0x1 << 14) +//The signal is invalid, it will be pulled up every time, no need to care. +// #define m_RGA3_INT_RGA_MI_WR_IN_VERR (0x1 << 13) +#define m_RGA3_INT_WIN1_V_ERR (0x1 << 11) +#define m_RGA3_INT_WIN1_H_ERR (0x1 << 10) +#define m_RGA3_INT_WIN1_FBCD_DEC_ERR (0x1 << 9) +#define m_RGA3_INT_WIN1_RD_FRM_END (0x1 << 8) //not error +#define m_RGA3_INT_WIN0_V_ERR (0x1 << 7) +#define m_RGA3_INT_WIN0_H_ERR (0x1 << 6) +#define m_RGA3_INT_WIN0_FBCD_DEC_ERR (0x1 << 5) +#define m_RGA3_INT_WIN0_RD_FRM_END (0x1 << 4) //not error +#define m_RGA3_INT_CMD_LINE_FINISH (0x1 << 3) //not error +#define m_RGA3_INT_RAG_MI_RD_BUS_ERR (0x1 << 2) +#define m_RGA3_INT_RGA_MMU_INTR (0x1 << 1) +#define m_RGA3_INT_FRM_DONE (0x1 << 0) //not error + +#define m_RGA3_INT_ERROR_MASK \ + ( \ + m_RGA3_INT_RGA_MMU_INTR | \ + m_RGA3_INT_RAG_MI_RD_BUS_ERR | \ + m_RGA3_INT_WIN0_FBCD_DEC_ERR | \ + m_RGA3_INT_WIN0_H_ERR | \ + m_RGA3_INT_WIN0_V_ERR | \ + m_RGA3_INT_WIN1_FBCD_DEC_ERR | \ + m_RGA3_INT_WIN1_H_ERR | \ + m_RGA3_INT_WIN1_V_ERR | \ + m_RGA3_INT_RGA_MI_WR_IN_HERR | \ + m_RGA3_INT_RGA_MI_WR_BUS_ERR | \ + m_RGA3_INT_WIN0_IN_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN0_IN_FIFO_REB_ERR | \ + m_RGA3_INT_WIN0_HOR_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN0_HOR_FIFO_REN_ERR| \ + m_RGA3_INT_WIN0_VOR_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN0_VOR_FIFO_REN_ERR | \ + m_RGA3_INT_WIN1_IN_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN1_IN_FIFO_REB_ERR | \ + m_RGA3_INT_WIN1_HOR_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN1_HOR_FIFO_REN_ERR| \ + m_RGA3_INT_WIN1_VOR_FIFO_WEN_ERR | \ + m_RGA3_INT_WIN1_VOR_FIFO_REN_ERR \ + ) + +/* RGA3_CMD_CTRL */ +#define m_RGA3_CMD_CTRL_CMD_INCR_NUM (0x3ff << 3) +#define m_RGA3_CMD_CTRL_CMD_STOP_MODE (0x1 << 2) +#define m_RGA3_CMD_CTRL_CMD_INCR_VALID_P (0x1 << 1) +#define m_RGA3_CMD_CTRL_CMD_LINE_ST_P (0x1 << 0) + +/* RGA3_RO_SRST */ +#define m_RGA3_RO_SRST_RO_RST_DONE (0x3f << 0) + +/* RGA3_CMD_STATE */ +#define m_RGA3_CMD_STATE_CMD_CNT_CUR (0xfff << 16) +#define m_RGA3_CMD_STATE_CMD_WORKING (0x1 << 0) + +/* RGA3_WIN0_RD_CTRL */ +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_ENABLE (0x1 << 0) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_MODE (0x3 << 1) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_PIC_FORMAT (0xf << 4) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_FORMAT (0x3 << 8) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT (0x1 << 10) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_ENDIAN_MODE (0x1 << 11) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_PIX_SWAP (0x1 << 12) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_YC_SWAP (0x1 << 13) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_ROT (0x1 << 16) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_XMIRROR (0x1 << 17) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_YMIRROR (0x1 << 18) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_BY (0x1 << 20) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_UP (0x1 << 21) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_BY (0x1 << 22) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_UP (0x1 << 23) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_Y2R_EN (0x1 << 24) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_R2Y_EN (0x1 << 25) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE (0x3 << 26) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_PERF_OPT_DIS (0x1 << 29) +#define m_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_ALIGN_DIS (0x1 << 30) + +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_ENABLE(x) ((x & 0x1) << 0) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_MODE(x) ((x & 0x3) << 1) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_PIC_FORMAT(x) ((x & 0xf) << 4) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_FORMAT(x) ((x & 0x3) << 8) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT(x) ((x & 0x1) << 10) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_ENDIAN_MODE(x) ((x & 0x1) << 11) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_PIX_SWAP(x) ((x & 0x1) << 12) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_YC_SWAP(x) ((x & 0x1) << 13) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_ROT(x) ((x & 0x1) << 16) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_XMIRROR(x) ((x & 0x1) << 17) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_YMIRROR(x) ((x & 0x1) << 18) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_BY(x) ((x & 0x1) << 20) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_UP(x) ((x & 0x1) << 21) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_BY(x) ((x & 0x1) << 22) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_UP(x) ((x & 0x1) << 23) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_Y2R_EN(x) ((x & 0x1) << 24) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_R2Y_EN(x) ((x & 0x1) << 25) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE(x) ((x & 0x3) << 26) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_PERF_OPT_DIS(x) ((x & 0x1) << 29) +#define s_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_ALIGN_DIS(x) ((x & 0x1) << 30) + +/* RGA3_WIN0_FBC_OFF */ +#define m_RGA3_WIN0_FBC_OFF_SW_WIN0_FBC_XOFF (0x1fff << 0) +#define m_RGA3_WIN0_FBC_OFF_SW_WIN0_FBC_YOFF (0x1fff << 16) + +#define s_RGA3_WIN0_FBC_OFF_SW_WIN0_FBC_XOFF(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN0_FBC_OFF_SW_WIN0_FBC_YOFF(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN0_SRC_SIZE */ +#define m_RGA3_WIN0_SRC_SIZE_SW_WIN0_SRC_WIDTH (0x1fff << 0) +#define m_RGA3_WIN0_SRC_SIZE_SW_WIN0_SRC_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN0_SRC_OFF_SW_WIN0_SRC_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN0_SRC_OFF_SW_WIN0_SRC_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN0_ACT_OFF */ +#define m_RGA3_WIN0_ACT_OFF_SW_WIN0_ACT_XOFF (0x1fff << 0) +#define m_RGA3_WIN0_ACT_OFF_SW_WIN0_ACT_YOFF (0x1fff << 16) + +#define s_RGA3_WIN0_ACT_OFF_SW_WIN0_ACT_XOFF(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN0_ACT_OFF_SW_WIN0_ACT_YOFF(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN0_ACT_SIZE */ +#define m_RGA3_WIN0_ACT_SIZE_SW_WIN0_ACT_WIDTH (0x1fff << 0) +#define m_RGA3_WIN0_ACT_SIZE_SW_WIN0_ACT_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN0_ACT_SIZE_SW_WIN0_ACT_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN0_ACT_SIZE_SW_WIN0_ACT_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN0_DST_SIZE */ +#define m_RGA3_WIN0_DST_SIZE_SW_WIN0_DST_WIDTH (0x1fff << 0) +#define m_RGA3_WIN0_DST_SIZE_SW_WIN0_DST_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN0_DST_SIZE_SW_WIN0_DST_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN0_DST_SIZE_SW_WIN0_DST_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN0_SCL_FAC */ +#define m_RGA3_WIN0_SCL_FAC_SW_WIN0_VER_FAC (0xffff << 0) +#define m_RGA3_WIN0_SCL_FAC_SW_WIN0_HOR_FAC (0xffff << 16) + +#define s_RGA3_WIN0_SCL_FAC_SW_WIN0_VER_FAC(x) ((x & 0xffff) << 0) +#define s_RGA3_WIN0_SCL_FAC_SW_WIN0_HOR_FAC(x) ((x & 0xffff) << 16) + +/* RGA3_WIN1_RD_CTRL */ +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_ENABLE (0x1 << 0) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_MODE (0x3 << 1) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_PIC_FORMAT (0xf << 4) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_FORMAT (0x3 << 8) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT (0x1 << 10) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_ENDIAN_MODE (0x1 << 11) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_PIX_SWAP (0x1 << 12) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_YC_SWAP (0x1 << 13) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_ROT (0x1 << 16) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_XMIRROR (0x1 << 17) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_YMIRROR (0x1 << 18) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_BY (0x1 << 20) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_UP (0x1 << 21) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_BY (0x1 << 22) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_UP (0x1 << 23) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_Y2R_EN (0x1 << 24) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_R2Y_EN (0x1 << 25) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_CSC_MODE (0x3 << 26) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_PERF_OPT_DIS (0x1 << 29) +#define m_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_ALIGN_DIS (0x1 << 30) + +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_ENABLE(x) ((x & 0x1) << 0) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_MODE(x) ((x & 0x3) << 1) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_PIC_FORMAT(x) ((x & 0xf) << 4) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_FORMAT(x) ((x & 0x3) << 8) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT(x) ((x & 0x1) << 10) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_ENDIAN_MODE(x) ((x & 0x1) << 11) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_PIX_SWAP(x) ((x & 0x1) << 12) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_YC_SWAP(x) ((x & 0x1) << 13) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_ROT(x) ((x & 0x1) << 16) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_XMIRROR(x) ((x & 0x1) << 17) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_YMIRROR(x) ((x & 0x1) << 18) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_BY(x) ((x & 0x1) << 20) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_UP(x) ((x & 0x1) << 21) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_BY(x) ((x & 0x1) << 22) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_UP(x) ((x & 0x1) << 23) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_Y2R_EN(x) ((x & 0x1) << 24) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_R2Y_EN(x) ((x & 0x1) << 25) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_CSC_MODE(x) ((x & 0x3) << 26) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_PERF_OPT_DIS(x) ((x & 0x1) << 29) +#define s_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_ALIGN_DIS(x) ((x & 0x1) << 30) + +/* RGA3_WIN1_FBC_OFF */ +#define m_RGA3_WIN1_FBC_OFF_SW_WIN1_FBC_XOFF (0x1fff << 0) +#define m_RGA3_WIN1_FBC_OFF_SW_WIN1_FBC_YOFF (0x1fff << 16) + +#define s_RGA3_WIN1_FBC_OFF_SW_WIN1_FBC_XOFF(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN1_FBC_OFF_SW_WIN1_FBC_YOFF(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN1_SRC_SIZE */ +#define m_RGA3_WIN1_SRC_SIZE_SW_WIN1_SRC_WIDTH (0x1fff << 0) +#define m_RGA3_WIN1_SRC_SIZE_SW_WIN1_SRC_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN1_SRC_OFF_SW_WIN1_SRC_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN1_SRC_OFF_SW_WIN1_SRC_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN1_ACT_OFF */ +#define m_RGA3_WIN1_ACT_OFF_SW_WIN1_ACT_XOFF (0x1fff << 0) +#define m_RGA3_WIN1_ACT_OFF_SW_WIN1_ACT_YOFF (0x1fff << 16) + +#define s_RGA3_WIN1_ACT_OFF_SW_WIN1_ACT_XOFF(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN1_ACT_OFF_SW_WIN1_ACT_YOFF(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN1_ACT_SIZE */ +#define m_RGA3_WIN1_ACT_SIZE_SW_WIN1_ACT_WIDTH (0x1fff << 0) +#define m_RGA3_WIN1_ACT_SIZE_SW_WIN1_ACT_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN1_ACT_SIZE_SW_WIN1_ACT_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN1_ACT_SIZE_SW_WIN1_ACT_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN1_DST_SIZE */ +#define m_RGA3_WIN1_DST_SIZE_SW_WIN1_DST_WIDTH (0x1fff << 0) +#define m_RGA3_WIN1_DST_SIZE_SW_WIN1_DST_HEIGHT (0x1fff << 16) + +#define s_RGA3_WIN1_DST_SIZE_SW_WIN1_DST_WIDTH(x) ((x & 0x1fff) << 0) +#define s_RGA3_WIN1_DST_SIZE_SW_WIN1_DST_HEIGHT(x) ((x & 0x1fff) << 16) + +/* RGA3_WIN1_SCL_FAC */ +#define m_RGA3_WIN1_SCL_FAC_SW_WIN1_VER_FAC (0xffff << 0) +#define m_RGA3_WIN1_SCL_FAC_SW_WIN1_HOR_FAC (0xffff << 16) + +#define s_RGA3_WIN1_SCL_FAC_SW_WIN1_VER_FAC(x) ((x & 0xffff) << 0) +#define s_RGA3_WIN1_SCL_FAC_SW_WIN1_HOR_FAC(x) ((x & 0xffff) << 16) + +/* RGA3_OVLP_CTRL */ +#define m_RGA3_OVLP_CTRL_SW_OVLP_MODE (0x3 << 0) +#define m_RGA3_OVLP_CTRL_SW_OVLP_FIELD (0x1 << 2) +#define m_RGA3_OVLP_CTRL_SW_TOP_SWAP (0x1 << 3) +#define m_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN (0x1 << 4) +#define m_RGA3_OVLP_CTRL_SW_TOP_KEY_EN (0x7FFF << 5) +#define m_RGA3_OVLP_CTRL_SW_OVLP_Y2R_EN (0x1 << 20) +#define m_RGA3_OVLP_CTRL_SW_OVLP_R2Y_EN (0x1 << 21) +#define m_RGA3_OVLP_CTRL_SW_OVLP_CSC_MODE (0x3 << 22) + +#define s_RGA3_OVLP_CTRL_SW_OVLP_MODE(x) ((x & 0x3) << 0) +#define s_RGA3_OVLP_CTRL_SW_OVLP_FIELD(x) ((x & 0x1) << 2) +#define s_RGA3_OVLP_CTRL_SW_TOP_SWAP(x) ((x & 0x1) << 3) +#define s_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN(x) ((x & 0x1) << 4) +#define s_RGA3_OVLP_CTRL_SW_TOP_KEY_EN(x) ((x & 0x7FFF) << 5) +#define s_RGA3_OVLP_CTRL_SW_OVLP_Y2R_EN(x) ((x & 0x1) << 20) +#define s_RGA3_OVLP_CTRL_SW_OVLP_R2Y_EN(x) ((x & 0x1) << 21) +#define s_RGA3_OVLP_CTRL_SW_OVLP_CSC_MODE(x) ((x & 0x3) << 22) + +/* RGA3_OVLP_OFF */ +#define m_RGA3_OVLP_OFF_SW_OVLP_XOFF (0x1fff << 0) +#define m_RGA3_OVLP_OFF_SW_OVLP_YOFF (0x1fff << 16) + +#define s_RGA3_OVLP_OFF_SW_OVLP_XOFF(x) ((x & 0x1fff) << 0) +#define s_RGA3_OVLP_OFF_SW_OVLP_YOFF(x) ((x & 0x1fff) << 16) + +/* RGA3_OVLP_TOP_KEY_MIN */ +#define m_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_YG_MIN (0x3ff << 0) +#define m_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_UB_MIN (0x3ff << 10) +#define m_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_VR_MIN (0x3ff << 20) + +#define s_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_YG_MIN(x) ((x & 0x3f)f << 0) +#define s_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_UB_MIN(x) ((x & 0x3ff) << 10) +#define s_RGA3_OVLP_TOP_KEY_MIN_SW_TOP_KEY_VR_MIN(x) ((x & 0x3ff) << 20) + +/* RGA3_OVLP_TOP_KEY_MAX */ +#define m_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_YG_MAX (0x3ff << 0) +#define m_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_UB_MAX (0x3ff << 10) +#define m_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_VR_MAX (0x3ff << 20) + +#define s_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_YG_MAX(x) ((x & 0x3ff) << 0) +#define s_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_UB_MAX(x) ((x & 0x3ff) << 10) +#define s_RGA3_OVLP_TOP_KEY_MAX_SW_TOP_KEY_VR_MAX(x) ((x & 0x3ff) << 20) + +/* RGA3_OVLP_TOP_CTRL */ +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_COLOR_M0 (0x1 << 0) +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_M0 (0x1 << 1) +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_BLEND_M0 (0x3 << 2) +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_CAL_M0 (0x1 << 4) +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_FACTOR_M0 (0x7 << 5) +#define m_RGA3_OVLP_TOP_CTRL_SW_TOP_GLOBAL_ALPHA (0xff << 16) + +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_COLOR_M0(x) ((x & 0x1) << 0) +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_M0(x) ((x & 0x1) << 1) +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_BLEND_M0(x) ((x & 0x3) << 2) +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_CAL_M0(x) ((x & 0x1) << 4) +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_FACTOR_M0(x) ((x & 0x7) << 5) +#define s_RGA3_OVLP_TOP_CTRL_SW_TOP_GLOBAL_ALPHA(x) ((x & 0xff) << 16) + +/* RGA3_OVLP_BOT_CTRL */ +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_COLOR_M0 (0x1 << 0) +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_M0 (0x1 << 1) +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_BLEND_M0 (0x3 << 2) +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_CAL_M0 (0x1 << 4) +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_FACTOR_M0 (0x7 << 5) +#define m_RGA3_OVLP_BOT_CTRL_SW_BOT_GLOBAL_ALPHA (0xff << 16) + +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_COLOR_M0(x) ((x & 0x1) << 0) +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_M0(x) ((x & 0x1) << 1) +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_BLEND_M0(x) ((x & 0x3) << 2) +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_CAL_M0(x) ((x & 0x1) << 4) +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_FACTOR_M0(x) ((x & 0x7) << 5) +#define s_RGA3_OVLP_BOT_CTRL_SW_BOT_GLOBAL_ALPHA(x) ((x & 0xff) << 16) + +/* RGA3_OVLP_TOP_ALPHA */ +#define m_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_M1 (0x1 << 1) +#define m_RGA3_OVLP_TOP_ALPHA_SW_TOP_BLEND_M1 (0x3 << 2) +#define m_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_CAL_M1 (0x1 << 4) +#define m_RGA3_OVLP_TOP_ALPHA_SW_TOP_FACTOR_M1 (0x7 << 5) + +#define s_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_M1(x) ((x & 0x1) << 1) +#define s_RGA3_OVLP_TOP_ALPHA_SW_TOP_BLEND_M1(x) ((x & 0x3) << 2) +#define s_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_CAL_M1(x) ((x & 0x1) << 4) +#define s_RGA3_OVLP_TOP_ALPHA_SW_TOP_FACTOR_M1(x) ((x & 0x7) << 5) + +/* RGA3_OVLP_BOT_ALPHA */ +#define m_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_M1 (0x1 << 1) +#define m_RGA3_OVLP_BOT_ALPHA_SW_BOT_BLEND_M1 (0x3 << 2) +#define m_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_CAL_M1 (0x1 << 4) +#define m_RGA3_OVLP_BOT_ALPHA_SW_BOT_FACTOR_M1 (0x7 << 5) + +#define s_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_M1(x) ((x & 0x1) << 1) +#define s_RGA3_OVLP_BOT_ALPHA_SW_BOT_BLEND_M1(x) ((x & 0x3) << 2) +#define s_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_CAL_M1(x) ((x & 0x1) << 4) +#define s_RGA3_OVLP_BOT_ALPHA_SW_BOT_FACTOR_M1(x) ((x & 0x7) << 5) + +/* RGA3_WR_CTRL */ +#define m_RGA3_WR_CTRL_SW_WR_MODE (0x3 << 0) +#define m_RGA3_WR_CTRL_SW_WR_FBCE_SPARSE_EN (0x1 << 2) +#define m_RGA3_WR_CTRL_SW_WR_PIC_FORMAT (0xf << 4) +#define m_RGA3_WR_CTRL_SW_WR_FORMAT (0x3 << 8) +#define m_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT (0x1 << 10) +#define m_RGA3_WR_CTRL_SW_WR_ENDIAN_MODE (0x1 << 11) +#define m_RGA3_WR_CTRL_SW_WR_PIX_SWAP (0x1 << 12) +#define m_RGA3_WR_CTRL_SW_OUTSTANDING_MAX (0x3f << 13) +#define m_RGA3_WR_CTRL_SW_WR_YC_SWAP (0x1 << 20) + +#define s_RGA3_WR_CTRL_SW_WR_MODE(x) ((x & 0x3) << 0) +#define s_RGA3_WR_CTRL_SW_WR_FBCE_SPARSE_EN(x) ((x & 0x1) << 2) +#define s_RGA3_WR_CTRL_SW_WR_PIC_FORMAT(x) ((x & 0xf) << 4) +#define s_RGA3_WR_CTRL_SW_WR_FORMAT(x) ((x & 0x3) << 8) +#define s_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT(x) ((x & 0x1) << 10) +#define s_RGA3_WR_CTRL_SW_WR_ENDIAN_MODE(x) ((x & 0x1) << 11) +#define s_RGA3_WR_CTRL_SW_WR_PIX_SWAP(x) ((x & 0x1) << 12) +#define s_RGA3_WR_CTRL_SW_OUTSTANDING_MAX(x) ((x & 0x3f) << 13) +#define s_RGA3_WR_CTRL_SW_WR_YC_SWAP(x) ((x & 0x1) << 20) + +/* RGA3_WR_FBCE_CTRL */ +#define m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_BLKBD_OPT_DIS (0x1 << 0) +#define m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_HOFF_DISS (0x1 << 1) +#define m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_PL_FIFO0_WATERMARK (0x3f << 2) +#define m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_PL_FIFO1_WATERMARK (0x3f << 8) +#define m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_SIZE_ALIGN_DIS (0x1 << 31) + +#define s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_BLKBD_OPT_DIS(x) ((x & 0x1) << 0) +#define s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_HOFF_DISS(x) ((x & 0x1) << 1) +#define s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_PL_FIFO0_WATERMARK(x) ((x & 0x3f) << 2) +#define s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_PL_FIFO1_WATERMARK(x) ((x & 0x3f) << 8) +#define s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_SIZE_ALIGN_DIS(x) ((x & 0x1) << 31) + +/* RGA3_MMU_STATUS read_only */ +#define m_RGA3_MMU_STATUS_PAGING_ENABLED (0x1 << 0) +#define m_RGA3_MMU_STATUS_PAGE_FAULT_ACTIVE (0x1 << 1) +#define m_RGA3_MMU_STATUS_STAIL_ACTIVE (0x1 << 2) +#define m_RGA3_MMU_STATUS_MMU_IDLE (0x1 << 3) +#define m_RGA3_MMU_STATUS_REPLAY_BUFFER_EMPTY (0x1 << 4) +#define m_RGA3_MMU_STATUS_PAGE_FAULT_IS_WRITE (0x1 << 5) +#define m_RGA3_MMU_STATUS_PAGE_FAULT_BUS_ID (0x1f << 6) + +/* RGA3_MMU_INT_RAWSTAT read_only */ +#define m_RGA3_MMU_INT_RAWSTAT_READ_BUS_ERROR (0x1 << 0) +#define m_RGA3_MMU_INT_RAWSTAT_PAGE_FAULT (0x1 << 1) + +/* RGA3_MMU_INT_CLEAR write_only */ +#define m_RGA3_MMU_INT_CLEAR_READ_BUS_ERROR (0x1 << 0) +#define m_RGA3_MMU_INT_CLEAR_PAGE_FAULT (0x1 << 1) + +#define s_RGA3_MMU_INT_CLEAR_READ_BUS_ERROR(x) ((x & 0x1) << 0) +#define s_RGA3_MMU_INT_CLEAR_PAGE_FAULT(x) ((x & 0x1) << 1) + +/* RGA3_MMU_INT_MASK */ +#define m_RGA3_MMU_INT_MASK_READ_BUS_ERROR (0x1 << 0) +#define m_RGA3_MMU_INT_MASK_PAGE_FAULT (0x1 << 1) + +#define s_RGA3_MMU_INT_MASK_READ_BUS_ERROR(x) ((x & 0x1) << 0) +#define s_RGA3_MMU_INT_MASK_PAGE_FAULT(x) ((x & 0x1) << 1) + +/* RGA3_MMU_INT_STATUS read_only */ +#define m_RGA3_MMU_INT_STATUS_READ_BUS_ERROR (0x1 << 0) +#define m_RGA3_MMU_INT_STATUS_PAGE_FAULT (0x1 << 1) + +/* RGA3_MMU_AUTO_GATING */ +#define m_RGA3_MMU_AUTO_GATING_MMU_AUTO_GATING (0x1 << 1) +#define m_RGA3_MMU_AUTO_GATING_MMU_CFG_MODE (0x1 << 1) +#define m_RGA3_MMU_AUTO_GATING_MMU_BUG_FIXED_DISABLE (0x1 << 31) + +#define s_RGA3_MMU_AUTO_GATING_MMU_AUTO_GATING(x) ((x & 0x1) << 1) +#define s_RGA3_MMU_AUTO_GATING_MMU_BUG_FIXED_DISABLE(x) ((x & 0x1) << 31) + +#define RGA3_ROT_BIT_ROT_90 BIT(0) +#define RGA3_ROT_BIT_X_MIRROR BIT(1) +#define RGA3_ROT_BIT_Y_MIRROR BIT(2) + +union rga3_color_ctrl { + uint32_t value; + struct { + uint32_t color_mode:1; + uint32_t alpha_mode:1; + uint32_t blend_mode:2; + uint32_t alpha_cal_mode:1; + uint32_t factor_mode:3; + + uint32_t reserved:8; + + uint32_t global_alpha:8; + } bits; +}; + +union rga3_alpha_ctrl { + uint32_t value; + struct { + uint32_t reserved:1; + uint32_t alpha_mode:1; + uint32_t blend_mode:2; + uint32_t alpha_cal_mode:1; + uint32_t factor_mode:3; + } bits; +}; + +extern const struct rga_backend_ops rga3_ops; + +#endif + diff --git a/drivers/video/rockchip/rga3/include/rga_common.h b/drivers/video/rockchip/rga3/include/rga_common.h new file mode 100644 index 000000000..32e83cc9c --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_common.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + */ + +#ifndef __LINUX_RKRGA_COMMON_H_ +#define __LINUX_RKRGA_COMMON_H_ + +#include "rga_drv.h" +#include "rga_hw_config.h" + +#define RGA_GET_PAGE_COUNT(size) (((size) >> PAGE_SHIFT) + (((size) & (~PAGE_MASK)) ? 1 : 0)) + +bool rga_is_rgb_format(uint32_t format); +bool rga_is_yuv_format(uint32_t format); +bool rga_is_alpha_format(uint32_t format); +bool rga_is_yuv420_packed_format(uint32_t format); +bool rga_is_yuv420_planar_format(uint32_t format); +bool rga_is_yuv420_semi_planar_format(uint32_t format); +bool rga_is_yuv422_packed_format(uint32_t format); +bool rga_is_yuv422_planar_format(uint32_t format); +bool rga_is_yuv422_semi_planar_format(uint32_t format); +bool rga_is_yuv8bit_format(uint32_t format); +bool rga_is_yuv10bit_format(uint32_t format); +bool rga_is_yuv422p_format(uint32_t format); +bool rga_is_only_y_format(uint32_t format); + +int rga_get_format_bits(uint32_t format); +int rga_get_pixel_stride_from_format(uint32_t format); + +const char *rga_get_format_name(uint32_t format); +const char *rga_get_render_mode_str(uint8_t mode); +const char *rga_get_rotate_mode_str(uint8_t mode); +const char *rga_get_blend_mode_str(enum rga_alpha_blend_mode mode); +const char *rga_get_memory_type_str(uint8_t type); +const char *rga_get_mmu_type_str(enum rga_mmu mmu_type); +const char *rga_get_core_name(enum RGA_SCHEDULER_CORE core); + +void rga_convert_addr(struct rga_img_info_t *img, bool before_vir_get_channel); +void rga_swap_pd_mode(struct rga_req *req_rga); +int rga_image_size_cal(int w, int h, int format, + int *yrgb_size, int *uv_size, int *v_size); +void rga_dump_memory_parm(struct rga_memory_parm *parm); +void rga_dump_external_buffer(struct rga_external_buffer *buffer); + +#endif diff --git a/drivers/video/rockchip/rga3/include/rga_debugger.h b/drivers/video/rockchip/rga3/include/rga_debugger.h new file mode 100644 index 000000000..c274fe87f --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_debugger.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + * Huang Lee + */ + +#ifndef _RGA_DEBUGGER_H_ +#define _RGA_DEBUGGER_H_ + +#include "rga_drv.h" + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER + +extern int RGA_DEBUG_REG; +extern int RGA_DEBUG_MSG; +extern int RGA_DEBUG_TIME; +extern int RGA_DEBUG_INT_FLAG; +extern int RGA_DEBUG_MM; +extern int RGA_DEBUG_CHECK_MODE; +extern int RGA_DEBUG_NONUSE; +extern int RGA_DEBUG_DUMP_IMAGE; + +#define DEBUGGER_EN(name) (unlikely(RGA_DEBUG_##name ? true : false)) + +/* + * struct rga_debugger - RGA debugger information + * + * This structure represents a debugger to be created by the rga driver + * or core. + */ +struct rga_debugger { +#ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS + /* Directory of debugfs file */ + struct dentry *debugfs_dir; + struct list_head debugfs_entry_list; + struct mutex debugfs_lock; +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_PROC_FS + /* Directory of procfs file */ + struct proc_dir_entry *procfs_dir; + struct list_head procfs_entry_list; + struct mutex procfs_lock; +#endif +}; + +/* + * struct rga_debugger_list - debugfs/procfs info list entry + * + * This structure represents a debugfs/procfs file to be created by the rga + * driver or core. + */ +struct rga_debugger_list { + /* File name */ + const char *name; + /* + * Show callback. &seq_file->private will be set to the &struct + * rga_debugger_node corresponding to the instance of this info + * on a given &struct rga_debugger. + */ + int (*show)(struct seq_file *seq, void *data); + /* + * Write callback. &seq_file->private will be set to the &struct + * rga_debugger_node corresponding to the instance of this info + * on a given &struct rga_debugger. + */ + ssize_t (*write)(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp); + /* Procfs/Debugfs private data. */ + void *data; +}; + +/* + * struct rga_debugger_node - Nodes for debugfs/procfs + * + * This structure represents each instance of procfs/debugfs created from the + * template. + */ +struct rga_debugger_node { + struct rga_debugger *debugger; + + /* template for this node. */ + const struct rga_debugger_list *info_ent; + + /* Each Procfs/Debugfs file. */ +#ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS + struct dentry *dent; +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_PROC_FS + struct proc_dir_entry *pent; +#endif + + struct list_head list; +}; + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS +int rga_debugfs_init(void); +int rga_debugfs_remove(void); +#else +static inline int rga_debugfs_remove(void) +{ + return 0; +} +static inline int rga_debugfs_init(void) +{ + return 0; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS */ + +#ifdef CONFIG_ROCKCHIP_RGA_PROC_FS +int rga_procfs_remove(void); +int rga_procfs_init(void); +#else +static inline int rga_procfs_remove(void) +{ + return 0; +} +static inline int rga_procfs_init(void) +{ + return 0; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RGA_PROC_FS */ + +#else + +#define DEBUGGER_EN(name) (unlikely(false)) + +#endif /* #ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER */ + +void rga_cmd_print_debug_info(struct rga_req *req); +void rga_request_task_debug_info(struct seq_file *m, struct rga_req *req); +#ifdef CONFIG_NO_GKI +void rga_dump_job_image(struct rga_job *dump_job); +#else +static inline void rga_dump_job_image(struct rga_job *dump_job) +{ +} +#endif /* #ifdef CONFIG_NO_GKI */ + +#endif /* #ifndef _RGA_DEBUGGER_H_ */ + diff --git a/drivers/video/rockchip/rga3/include/rga_dma_buf.h b/drivers/video/rockchip/rga3/include/rga_dma_buf.h new file mode 100644 index 000000000..2ff0857a6 --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_dma_buf.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + * Huang Lee + */ +#ifndef __RGA3_DMA_BUF_H__ +#define __RGA3_DMA_BUF_H__ + +#include "rga_drv.h" + +#ifndef for_each_sgtable_sg +/* + * Loop over each sg element in the given sg_table object. + */ +#define for_each_sgtable_sg(sgt, sg, i) \ + for_each_sg((sgt)->sgl, sg, (sgt)->orig_nents, i) +#endif + +int rga_buf_size_cal(unsigned long yrgb_addr, unsigned long uv_addr, + unsigned long v_addr, int format, uint32_t w, + uint32_t h, unsigned long *StartAddr, unsigned long *size); + +int rga_virtual_memory_check(void *vaddr, u32 w, u32 h, u32 format, int fd); +int rga_dma_memory_check(struct rga_dma_buffer *rga_dma_buffer, struct rga_img_info_t *img); + +int rga_iommu_map_sgt(struct sg_table *sgt, size_t size, + struct rga_dma_buffer *buffer, + struct device *rga_dev); +int rga_iommu_map(phys_addr_t paddr, size_t size, + struct rga_dma_buffer *buffer, + struct device *rga_dev); +void rga_iommu_unmap(struct rga_dma_buffer *buffer); + +int rga_dma_map_buf(struct dma_buf *dma_buf, struct rga_dma_buffer *rga_dma_buffer, + enum dma_data_direction dir, struct device *rga_dev); +int rga_dma_map_fd(int fd, struct rga_dma_buffer *rga_dma_buffer, + enum dma_data_direction dir, struct device *rga_dev); +void rga_dma_unmap_buf(struct rga_dma_buffer *rga_dma_buffer); + +void rga_dma_sync_flush_range(void *pstart, void *pend, struct rga_scheduler_t *scheduler); + +#endif /* #ifndef __RGA3_DMA_BUF_H__ */ + diff --git a/drivers/video/rockchip/rga3/include/rga_drv.h b/drivers/video/rockchip/rga3/include/rga_drv.h new file mode 100644 index 000000000..c9d28387a --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_drv.h @@ -0,0 +1,468 @@ +/* 6.18 renamed MAX_ORDER (exclusive) to MAX_PAGE_ORDER (inclusive) */ +#include +#ifndef MAX_ORDER +#define MAX_ORDER (MAX_PAGE_ORDER + 1) +#endif +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RGA_DRV_H_ +#define __LINUX_RGA_DRV_H_ + +#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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifdef CONFIG_DMABUF_CACHE +#include +#else +#include +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#include +#endif + +#include + +#include "rga.h" + +#define RGA_CORE_REG_OFFSET 0x10000 + +/* load interval: 1000ms */ +#define RGA_LOAD_INTERVAL_US 1000000 + +/* timer interval: 1000ms */ +#define RGA_TIMER_INTERVAL_NS 1000000000 + +#if ((defined(CONFIG_RK_IOMMU) || defined(CONFIG_ROCKCHIP_IOMMU)) \ + && defined(CONFIG_ION_ROCKCHIP)) +#define CONFIG_RGA_IOMMU +#endif + +/* Driver information */ +#define DRIVER_DESC "RGA multicore Device Driver" +#define DRIVER_NAME "rga_multicore" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#define DRIVER_MAJOR_VERISON 1 +#define DRIVER_MINOR_VERSION 3 +#define DRIVER_REVISION_VERSION 1 +#define DRIVER_PATCH_VERSION + +#define DRIVER_VERSION (STR(DRIVER_MAJOR_VERISON) "." STR(DRIVER_MINOR_VERSION) \ + "." STR(DRIVER_REVISION_VERSION) STR(DRIVER_PATCH_VERSION)) + +/* time limit */ +#define RGA_JOB_TIMEOUT_DELAY HZ +#define RGA_RESET_TIMEOUT 1000 + +#define RGA_MAX_SCHEDULER RGA_HW_SIZE +#define RGA_MAX_BUS_CLK 10 + +#define RGA_BUFFER_POOL_MAX_SIZE 64 + +#ifndef ABS +#define ABS(X) (((X) < 0) ? (-(X)) : (X)) +#endif + +#ifndef CLIP +#define CLIP(x, a, b) (((x) < (a)) \ + ? (a) : (((x) > (b)) ? (b) : (x))) +#endif + +extern struct rga_drvdata_t *rga_drvdata; + +enum { + RGA3_SCHEDULER_CORE0 = 1 << 0, + RGA3_SCHEDULER_CORE1 = 1 << 1, + RGA2_SCHEDULER_CORE0 = 1 << 2, + RGA_CORE_MASK = 0x7, + RGA_NONE_CORE = 0x0, +}; + +enum { + RGA_CMD_SLAVE = 1, + RGA_CMD_MASTER = 2, +}; + +enum iommu_dma_cookie_type { + IOMMU_DMA_IOVA_COOKIE, + IOMMU_DMA_MSI_COOKIE, +}; + +enum rga_scheduler_status { + RGA_SCHEDULER_IDLE = 0, + RGA_SCHEDULER_WORKING, + RGA_SCHEDULER_ABORT, +}; + +enum rga_job_state { + RGA_JOB_STATE_PENDING = 0, + RGA_JOB_STATE_PREPARE, + RGA_JOB_STATE_RUNNING, + RGA_JOB_STATE_FINISH, + RGA_JOB_STATE_DONE, + RGA_JOB_STATE_INTR_ERR, + RGA_JOB_STATE_HW_TIMEOUT, + RGA_JOB_STATE_ABORT, +}; + +struct rga_iommu_dma_cookie { + enum iommu_dma_cookie_type type; + + /* Full allocator for IOMMU_DMA_IOVA_COOKIE */ + struct iova_domain iovad; +}; + +struct rga_iommu_info { + struct device *dev; + struct device *default_dev; /* for dma-buf_api */ + struct iommu_domain *domain; + struct iommu_group *group; +}; + +struct rga_dma_buffer { + /* DMABUF information */ + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + void *vmap_ptr; + + struct iommu_domain *domain; + + enum dma_data_direction dir; + + dma_addr_t iova; + unsigned long size; + /* + * The offset of the first page of the sgt. + * Since alloc iova must be page aligned, the offset of the first page is + * identified separately. + */ + size_t offset; + + /* The scheduler of the mapping */ + struct rga_scheduler_t *scheduler; +}; + +struct rga_virt_addr { + uint64_t addr; + + struct page **pages; + int pages_order; + int page_count; + unsigned long size; + + /* The offset of the first page of the virtual address */ + size_t offset; + + int result; +}; + +struct rga_internal_buffer { + /* DMA buffer */ + struct rga_dma_buffer *dma_buffer; + + /* virtual address */ + struct rga_virt_addr *virt_addr; + + /* physical address */ + uint64_t phys_addr; + + /* buffer size */ + unsigned long size; + + struct rga_memory_parm memory_parm; + + + struct mm_struct *current_mm; + + /* memory type. */ + uint32_t type; + + uint32_t handle; + + uint32_t mm_flag; + + struct kref refcount; + struct rga_session *session; +}; + +struct rga_scheduler_t; + +struct rga_session { + int id; + + pid_t tgid; + + char *pname; +}; + +struct rga_job_buffer { + union { + struct { + struct rga_external_buffer *ex_y_addr; + struct rga_external_buffer *ex_uv_addr; + struct rga_external_buffer *ex_v_addr; + }; + struct rga_external_buffer *ex_addr; + }; + + union { + struct { + struct rga_internal_buffer *y_addr; + struct rga_internal_buffer *uv_addr; + struct rga_internal_buffer *v_addr; + }; + struct rga_internal_buffer *addr; + }; + + uint32_t *page_table; + int order; + int page_count; +}; + +struct rga_job { + struct list_head head; + + struct rga_scheduler_t *scheduler; + struct rga_session *session; + + struct rga_req rga_command_base; + uint32_t cmd_reg[32 * 8]; + struct rga_full_csc full_csc; + struct rga_csc_clip full_csc_clip; + struct rga_pre_intr_info pre_intr_info; + + struct rga_job_buffer src_buffer; + struct rga_job_buffer src1_buffer; + struct rga_job_buffer dst_buffer; + /* used by rga2 */ + struct rga_job_buffer els_buffer; + + /* for rga2 virtual_address */ + struct mm_struct *mm; + + /* job time stamp */ + ktime_t timestamp; + /* The time when the job is actually executed on the hardware */ + ktime_t hw_running_time; + /* The time only for hrtimer to calculate the load */ + ktime_t hw_recoder_time; + unsigned int flags; + int request_id; + int priority; + int core; + int ret; + pid_t pid; + bool use_batch_mode; + + struct kref refcount; + unsigned long state; + uint32_t intr_status; + uint32_t hw_status; + uint32_t cmd_status; +}; + +struct rga_backend_ops { + int (*get_version)(struct rga_scheduler_t *scheduler); + int (*set_reg)(struct rga_job *job, struct rga_scheduler_t *scheduler); + int (*init_reg)(struct rga_job *job); + void (*soft_reset)(struct rga_scheduler_t *scheduler); + int (*read_back_reg)(struct rga_job *job, struct rga_scheduler_t *scheduler); + int (*irq)(struct rga_scheduler_t *scheduler); + int (*isr_thread)(struct rga_job *job, struct rga_scheduler_t *scheduler); +}; + +struct rga_timer { + u32 busy_time; + u32 busy_time_record; +}; + +struct rga_scheduler_t { + struct device *dev; + void __iomem *rga_base; + struct rga_iommu_info *iommu_info; + + struct clk *clks[RGA_MAX_BUS_CLK]; + int num_clks; + + enum rga_scheduler_status status; + int pd_refcount; + + struct rga_job *running_job; + struct list_head todo_list; + spinlock_t irq_lock; + wait_queue_head_t job_done_wq; + const struct rga_backend_ops *ops; + const struct rga_hw_data *data; + int job_count; + int irq; + struct rga_version_t version; + int core; + + struct rga_timer timer; +}; + +struct rga_request { + struct rga_req *task_list; + int task_count; + uint32_t finished_task_count; + uint32_t failed_task_count; + + bool use_batch_mode; + bool is_running; + bool is_done; + int ret; + uint32_t sync_mode; + + int32_t acquire_fence_fd; + int32_t release_fence_fd; + struct dma_fence *release_fence; + spinlock_t fence_lock; + + wait_queue_head_t finished_wq; + + int flags; + uint8_t mpi_config_flags; + int id; + struct rga_session *session; + + spinlock_t lock; + struct kref refcount; + + pid_t pid; + + /* + * The mapping of virtual addresses to obtain physical addresses requires + * the memory mapping information of the current process. + */ + struct mm_struct *current_mm; + + struct rga_feature feature; + /* TODO: add some common work */ +}; + +struct rga_pending_request_manager { + struct mutex lock; + + /* + * @request_idr: + * + * Mapping of request id to object pointers. Used by the GEM + * subsystem. Protected by @lock. + */ + struct idr request_idr; + + int request_count; +}; + +struct rga_session_manager { + struct mutex lock; + + struct idr ctx_id_idr; + + int session_cnt; +}; + +struct rga_drvdata_t { + /* used by rga2's mmu lock */ + struct mutex lock; + + struct rga_scheduler_t *scheduler[RGA_MAX_SCHEDULER]; + int num_of_scheduler; + /* The scheduler_index used by default for memory mapping. */ + int map_scheduler_index; + struct rga_mmu_base *mmu_base; + + struct delayed_work power_off_work; + + struct rga_mm *mm; + + /* rga_job pending manager, import by RGA_START_CONFIG */ + struct rga_pending_request_manager *pend_request_manager; + + struct rga_session_manager *session_manager; + +#ifdef CONFIG_ROCKCHIP_RGA_ASYNC + struct rga_fence_context *fence_ctx; +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER + struct rga_debugger *debugger; +#endif +}; + +struct rga_irqs_data_t { + const char *name; + irqreturn_t (*irq_hdl)(int irq, void *ctx); + irqreturn_t (*irq_thread)(int irq, void *ctx); +}; + +struct rga_match_data_t { + const char * const *clks; + int num_clks; +}; + +static inline int rga_read(int offset, struct rga_scheduler_t *scheduler) +{ + return readl(scheduler->rga_base + offset); +} + +static inline void rga_write(int value, int offset, struct rga_scheduler_t *scheduler) +{ + writel(value, scheduler->rga_base + offset); +} + +int rga_power_enable(struct rga_scheduler_t *scheduler); +int rga_power_disable(struct rga_scheduler_t *scheduler); + +int rga_kernel_commit(struct rga_req *cmd); + +#endif /* __LINUX_RGA_FENCE_H_ */ diff --git a/drivers/video/rockchip/rga3/include/rga_fence.h b/drivers/video/rockchip/rga3/include/rga_fence.h new file mode 100644 index 000000000..7e3bbeebb --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_fence.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RGA_FENCE_H_ +#define __LINUX_RGA_FENCE_H_ + +struct rga_fence_context { + unsigned int context; + unsigned int seqno; + spinlock_t spinlock; +}; + +struct rga_fence_waiter { + /* Base sync driver waiter structure */ + struct dma_fence_cb waiter; + + void *private; +}; + +#ifdef CONFIG_ROCKCHIP_RGA_ASYNC +int rga_fence_context_init(struct rga_fence_context **ctx); +void rga_fence_context_remove(struct rga_fence_context **ctx); + +struct dma_fence *rga_dma_fence_alloc(void); +int rga_dma_fence_get_fd(struct dma_fence *fence); +struct dma_fence *rga_get_dma_fence_from_fd(int fence_fd); +int rga_dma_fence_wait(struct dma_fence *fence); +int rga_dma_fence_add_callback(struct dma_fence *fence, dma_fence_func_t func, void *private); + + +static inline void rga_dma_fence_put(struct dma_fence *fence) +{ + if (fence) + dma_fence_put(fence); +} + +static inline void rga_dma_fence_signal(struct dma_fence *fence, int error) +{ + if (fence) { + if (error != 0) + dma_fence_set_error(fence, error); + dma_fence_signal(fence); + } +} + +static inline int rga_dma_fence_get_status(struct dma_fence *fence) +{ + if (fence) + return dma_fence_get_status(fence); + else + return 1; +} + +#else +static inline struct dma_fence *rga_dma_fence_alloc(void) +{ + return NULL; +} + +static inline int rga_dma_fence_get_fd(struct dma_fence *fence) +{ + return 0; +} + +static inline struct dma_fence *rga_get_dma_fence_from_fd(int fence_fd) +{ + return NULL; +} + +static inline int rga_dma_fence_wait(struct dma_fence *fence) +{ + return 0; +} + +static inline int rga_dma_fence_add_callback(struct dma_fence *fence, + dma_fence_func_t func, + void *private) +{ + return 0; +} + +static inline void rga_dma_fence_put(struct dma_fence *fence) +{ +} + +static inline void rga_dma_fence_signal(struct dma_fence *fence, int error) +{ +} + +static inline int rga_dma_fence_get_status(struct dma_fence *fence) +{ + return 0; +} + +#endif /* #ifdef CONFIG_SYNC_FILE */ + +#endif /* __LINUX_RGA_FENCE_H_ */ diff --git a/drivers/video/rockchip/rga3/include/rga_hw_config.h b/drivers/video/rockchip/rga3/include/rga_hw_config.h new file mode 100644 index 000000000..46f7531aa --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_hw_config.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RGA_HW_CONFIG_H_ +#define __LINUX_RGA_HW_CONFIG_H_ + +#include "rga_drv.h" + +enum rga_mmu { + RGA_NONE_MMU = 0, + RGA_MMU = 1, + RGA_IOMMU = 2, +}; + +enum rga_hw_support_format_index { + RGA_RASTER_INDEX, + RGA_AFBC16x16_INDEX, + RGA_TILE8x8_INDEX, + RGA_FORMAT_INDEX_BUTT, +}; + +struct rga_win_data { + const char *name; + const uint32_t *formats[RGA_FORMAT_INDEX_BUTT]; + uint32_t formats_count[RGA_FORMAT_INDEX_BUTT]; + + uint32_t supported_rotations; + uint32_t scale_up_mode; + uint32_t scale_down_mode; + uint32_t rd_mode; +}; + +struct rga_rect { + int width; + int height; +}; + +struct rga_rect_range { + struct rga_rect min; + struct rga_rect max; +}; + +struct rga_hw_data { + uint32_t version; + uint32_t feature; + + uint32_t csc_r2y_mode; + uint32_t csc_y2r_mode; + + struct rga_rect_range input_range; + struct rga_rect_range output_range; + + unsigned int max_upscale_factor; + unsigned int max_downscale_factor; + + uint32_t byte_stride_align; + uint32_t max_byte_stride; + + const struct rga_win_data *win; + unsigned int win_size; + + enum rga_mmu mmu; +}; + +extern const struct rga_hw_data rga3_data; +extern const struct rga_hw_data rga2e_data; +extern const struct rga_hw_data rga2e_1106_data; +extern const struct rga_hw_data rga2e_iommu_data; + +/* Returns false if in range, true otherwise */ +static inline bool rga_hw_out_of_range(const struct rga_rect_range *range, int width, int height) +{ + return (width > range->max.width || height > range->max.height || + width < range->min.width || height < range->min.height); +} + +#endif /* __LINUX_RGA_HW_CONFIG_H_ */ diff --git a/drivers/video/rockchip/rga3/include/rga_iommu.h b/drivers/video/rockchip/rga3/include/rga_iommu.h new file mode 100644 index 000000000..b80a1f48b --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_iommu.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RGA_MMU_INFO_H__ +#define __RGA_MMU_INFO_H__ + +#include "rga_drv.h" + +/* RGA_IOMMU register offsets */ +#define RGA_IOMMU_BASE 0xf00 +#define RGA_IOMMU_DTE_ADDR (RGA_IOMMU_BASE + 0x00) /* Directory table address */ +#define RGA_IOMMU_STATUS (RGA_IOMMU_BASE + 0x04) +#define RGA_IOMMU_COMMAND (RGA_IOMMU_BASE + 0x08) +#define RGA_IOMMU_PAGE_FAULT_ADDR (RGA_IOMMU_BASE + 0x0C) /* IOVA of last page fault */ +#define RGA_IOMMU_ZAP_ONE_LINE (RGA_IOMMU_BASE + 0x10) /* Shootdown one IOTLB entry */ +#define RGA_IOMMU_INT_RAWSTAT (RGA_IOMMU_BASE + 0x14) /* IRQ status ignoring mask */ +#define RGA_IOMMU_INT_CLEAR (RGA_IOMMU_BASE + 0x18) /* Acknowledge and re-arm irq */ +#define RGA_IOMMU_INT_MASK (RGA_IOMMU_BASE + 0x1C) /* IRQ enable */ +#define RGA_IOMMU_INT_STATUS (RGA_IOMMU_BASE + 0x20) /* IRQ status after masking */ +#define RGA_IOMMU_AUTO_GATING (RGA_IOMMU_BASE + 0x24) + +/* RGA_IOMMU_STATUS fields */ +#define RGA_IOMMU_STATUS_PAGING_ENABLED BIT(0) +#define RGA_IOMMU_STATUS_PAGE_FAULT_ACTIVE BIT(1) +#define RGA_IOMMU_STATUS_STALL_ACTIVE BIT(2) +#define RGA_IOMMU_STATUS_IDLE BIT(3) +#define RGA_IOMMU_STATUS_REPLAY_BUFFER_EMPTY BIT(4) +#define RGA_IOMMU_STATUS_PAGE_FAULT_IS_WRITE BIT(5) +#define RGA_IOMMU_STATUS_STALL_NOT_ACTIVE BIT(31) + +/* RGA_IOMMU_COMMAND command values */ +#define RGA_IOMMU_CMD_ENABLE_PAGING 0 /* Enable memory translation */ +#define RGA_IOMMU_CMD_DISABLE_PAGING 1 /* Disable memory translation */ +#define RGA_IOMMU_CMD_ENABLE_STALL 2 /* Stall paging to allow other cmds */ +#define RGA_IOMMU_CMD_DISABLE_STALL 3 /* Stop stall re-enables paging */ +#define RGA_IOMMU_CMD_ZAP_CACHE 4 /* Shoot down entire IOTLB */ +#define RGA_IOMMU_CMD_PAGE_FAULT_DONE 5 /* Clear page fault */ +#define RGA_IOMMU_CMD_FORCE_RESET 6 /* Reset all registers */ + +/* RGA_IOMMU_INT_* register fields */ +#define RGA_IOMMU_IRQ_PAGE_FAULT 0x01 /* page fault */ +#define RGA_IOMMU_IRQ_BUS_ERROR 0x02 /* bus read error */ +#define RGA_IOMMU_IRQ_MASK (RGA_IOMMU_IRQ_PAGE_FAULT | RGA_IOMMU_IRQ_BUS_ERROR) + +/* + * The maximum input is 8192*8192, the maximum output is 4096*4096 + * The size of physical pages requested is: + * (( maximum_input_value * + * maximum_input_value * format_bpp ) / 4K_page_size) + 1 + */ +#define RGA2_PHY_PAGE_SIZE (((8192 * 8192 * 4) / 4096) + 1) + +struct rga_mmu_base { + unsigned int *buf_virtual; + struct page **pages; + u8 buf_order; + u8 pages_order; + + int32_t front; + int32_t back; + int32_t size; + int32_t curr; +}; + +int rga_user_memory_check(struct page **pages, u32 w, u32 h, u32 format, int flag); +int rga_set_mmu_base(struct rga_job *job, struct rga2_req *req); +unsigned int *rga_mmu_buf_get(struct rga_mmu_base *mmu_base, uint32_t size); + +struct rga_mmu_base *rga_mmu_base_init(size_t size); +void rga_mmu_base_free(struct rga_mmu_base **mmu_base); + +int rga_iommu_detach(struct rga_iommu_info *info); +int rga_iommu_attach(struct rga_iommu_info *info); +struct rga_iommu_info *rga_iommu_probe(struct device *dev); +int rga_iommu_remove(struct rga_iommu_info *info); + +int rga_iommu_bind(void); +void rga_iommu_unbind(void); + +#endif + diff --git a/drivers/video/rockchip/rga3/include/rga_job.h b/drivers/video/rockchip/rga3/include/rga_job.h new file mode 100644 index 000000000..0fc776173 --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_job.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RKRGA_JOB_H_ +#define __LINUX_RKRGA_JOB_H_ + +#include +#include + +#include "rga_drv.h" + +enum job_flags { + RGA_JOB_DONE = 1 << 0, + RGA_JOB_ASYNC = 1 << 1, + RGA_JOB_SYNC = 1 << 2, + RGA_JOB_USE_HANDLE = 1 << 3, + RGA_JOB_UNSUPPORT_RGA_MMU = 1 << 4, +}; + +void rga_job_scheduler_dump_info(struct rga_scheduler_t *scheduler); +void rga_job_next(struct rga_scheduler_t *scheduler); +struct rga_job *rga_job_done(struct rga_scheduler_t *scheduler); +struct rga_job *rga_job_commit(struct rga_req *rga_command_base, struct rga_request *request); +int rga_job_mpi_commit(struct rga_req *rga_command_base, struct rga_request *request); + +int rga_job_assign(struct rga_job *job); + + +int rga_request_check(struct rga_user_request *req); +struct rga_request *rga_request_lookup(struct rga_pending_request_manager *request_manager, + uint32_t id); + +int rga_request_commit(struct rga_request *user_request); +void rga_request_session_destroy_abort(struct rga_session *session); +int rga_request_put(struct rga_request *request); +void rga_request_get(struct rga_request *request); +int rga_request_free(struct rga_request *request); +int rga_request_alloc(uint32_t flags, struct rga_session *session); + +struct rga_request *rga_request_config(struct rga_user_request *user_request); +struct rga_request *rga_request_kernel_config(struct rga_user_request *user_request); +int rga_request_submit(struct rga_request *request); +int rga_request_mpi_submit(struct rga_req *req, struct rga_request *request); +int rga_request_release_signal(struct rga_scheduler_t *scheduler, struct rga_job *job); + +int rga_request_manager_init(struct rga_pending_request_manager **request_manager_session); +int rga_request_manager_remove(struct rga_pending_request_manager **request_manager_session); + +#endif /* __LINUX_RKRGA_JOB_H_ */ diff --git a/drivers/video/rockchip/rga3/include/rga_mm.h b/drivers/video/rockchip/rga3/include/rga_mm.h new file mode 100644 index 000000000..d68fd75dc --- /dev/null +++ b/drivers/video/rockchip/rga3/include/rga_mm.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + */ + +#ifndef __LINUX_RKRGA_MM_H_ +#define __LINUX_RKRGA_MM_H_ + +#include "rga_drv.h" + +enum rga_mm_flag { + /* It will identify whether the buffer is within 0 ~ 4G. */ + RGA_MEM_UNDER_4G = 1 << 0, + /* Logo enable IOMMU */ + RGA_MEM_NEED_USE_IOMMU = 1 << 1, + /* Flag this is a physical contiguous memory. */ + RGA_MEM_PHYSICAL_CONTIGUOUS = 1 << 2, + /* need force flush cache */ + RGA_MEM_FORCE_FLUSH_CACHE = 1 << 3, +}; + +struct rga_mm { + struct mutex lock; + + /* + * @memory_idr: + * + * Mapping of memory object handles to object pointers. Used by the GEM + * subsystem. Protected by @memory_lock. + */ + struct idr memory_idr; + + /* the count of buffer in the cached_list */ + int buffer_count; +}; + +static inline bool rga_mm_is_invalid_dma_buffer(struct rga_dma_buffer *buffer) +{ + if (buffer == NULL) + return true; + + return buffer->scheduler == NULL ? true : false; +} + +struct rga_internal_buffer *rga_mm_lookup_handle(struct rga_mm *mm_session, uint32_t handle); +int rga_mm_lookup_flag(struct rga_mm *mm_session, uint64_t handle); +dma_addr_t rga_mm_lookup_iova(struct rga_internal_buffer *buffer); +struct sg_table *rga_mm_lookup_sgt(struct rga_internal_buffer *buffer); + +void rga_mm_dump_buffer(struct rga_internal_buffer *dump_buffer); +void rga_mm_dump_info(struct rga_mm *session); + +int rga_mm_map_job_info(struct rga_job *job); +void rga_mm_unmap_job_info(struct rga_job *job); + +int rga_mm_import_buffer(struct rga_external_buffer *external_buffer, + struct rga_session *session); +int rga_mm_release_buffer(uint32_t handle); +int rga_mm_session_release_buffer(struct rga_session *session); + +int rga_mm_init(struct rga_mm **session); +int rga_mm_remove(struct rga_mm **session); + +#endif diff --git a/drivers/video/rockchip/rga3/rga2_reg_info.c b/drivers/video/rockchip/rga3/rga2_reg_info.c new file mode 100644 index 000000000..8792e8d64 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga2_reg_info.c @@ -0,0 +1,2742 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga2_reg: " fmt + +#include "rga2_reg_info.h" +#include "rga_dma_buf.h" +#include "rga_iommu.h" +#include "rga_common.h" +#include "rga_hw_config.h" +#include "rga_debugger.h" + +unsigned int rga2_rop_code[256] = { + 0x00000007, 0x00000451, 0x00006051, 0x00800051, + 0x00007041, 0x00800041, 0x00804830, 0x000004f0,//0 + 0x00800765, 0x000004b0, 0x00000065, 0x000004f4, + 0x00000075, 0x000004e6, 0x00804850, 0x00800005, + + 0x00006850, 0x00800050, 0x00805028, 0x00000568, + 0x00804031, 0x00000471, 0x002b6071, 0x018037aa,//1 + 0x008007aa, 0x00036071, 0x00002c6a, 0x00803631, + 0x00002d68, 0x00802721, 0x008002d0, 0x000006d0, + + 0x0080066e, 0x00000528, 0x00000066, 0x0000056c, + 0x018007aa, 0x0002e06a, 0x00003471, 0x00834031,//2 + 0x00800631, 0x0002b471, 0x00006071, 0x008037aa, + 0x000036d0, 0x008002d4, 0x00002d28, 0x000006d4, + + 0x0000006e, 0x00000565, 0x00003451, 0x00800006, + 0x000034f0, 0x00834830, 0x00800348, 0x00000748,//3 + 0x00002f48, 0x0080034c, 0x000034b0, 0x0000074c, + 0x00000031, 0x00834850, 0x000034e6, 0x00800071, + + 0x008006f4, 0x00000431, 0x018007a1, 0x00b6e870, + 0x00000074, 0x0000046e, 0x00002561, 0x00802f28,//4 + 0x00800728, 0x0002a561, 0x000026c2, 0x008002c6, + 0x00007068, 0x018035aa, 0x00002c2a, 0x000006c6, + + 0x0000006c, 0x00000475, 0x000024e2, 0x008036b0, + 0x00804051, 0x00800004, 0x00800251, 0x00000651, + 0x00002e4a, 0x0080024e, 0x00000028, 0x00824842, + 0x000024a2, 0x0000064e, 0x000024f4, 0x00800068,//5 + + 0x008006b0, 0x000234f0, 0x00002741, 0x00800345, + 0x00003651, 0x00800255, 0x00000030, 0x00834051, + 0x00a34842, 0x000002b0, 0x00800271, 0x0002b651, + 0x00800368, 0x0002a741, 0x0000364e, 0x00806830,//6 + + 0x00006870, 0x008037a2, 0x00003431, 0x00000745, + 0x00002521, 0x00000655, 0x0000346e, 0x00800062, + 0x008002f0, 0x000236d0, 0x000026d4, 0x00807028, + 0x000036c6, 0x00806031, 0x008005aa, 0x00000671,//7 + + 0x00800671, 0x000005aa, 0x00006031, 0x008036c6, + 0x00007028, 0x00802e55, 0x008236d0, 0x000002f0, + 0x00000070, 0x0080346e, 0x00800655, 0x00802521, + 0x00800745, 0x00803431, 0x000037a2, 0x00806870,//8 + + 0x00006830, 0x0080364e, 0x00822f48, 0x00000361, + 0x0082b651, 0x00000271, 0x00800231, 0x002b4051, + 0x00034051, 0x00800030, 0x0080026e, 0x00803651, + 0x0080036c, 0x00802741, 0x008234f0, 0x000006b0,//9 + + 0x00000068, 0x00802c75, 0x0080064e, 0x008024a2, + 0x0002c04a, 0x00800021, 0x00800275, 0x00802e51, + 0x00800651, 0x00000251, 0x00800000, 0x00004051, + 0x000036b0, 0x008024e2, 0x00800475, 0x00000045,//a + + 0x008006c6, 0x00802c2a, 0x000035aa, 0x00807068, + 0x008002f4, 0x008026c2, 0x00822d68, 0x00000728, + 0x00002f28, 0x00802561, 0x0080046e, 0x00000046, + 0x00836870, 0x000007a2, 0x00800431, 0x00004071,//b + + 0x00000071, 0x008034e6, 0x00034850, 0x00800031, + 0x0080074c, 0x008034b0, 0x00800365, 0x00802f48, + 0x00800748, 0x00000341, 0x000026a2, 0x008034f0, + 0x00800002, 0x00005048, 0x00800565, 0x00000055,//c + + 0x008006d4, 0x00802d28, 0x008002e6, 0x008036d0, + 0x000037aa, 0x00806071, 0x0082b471, 0x00000631, + 0x00002e2a, 0x00803471, 0x00826862, 0x010007aa, + 0x0080056c, 0x00000054, 0x00800528, 0x00005068,//d + + 0x008006d0, 0x000002d0, 0x00002721, 0x00802d68, + 0x00003631, 0x00802c6a, 0x00836071, 0x000007aa, + 0x010037aa, 0x00a36870, 0x00800471, 0x00004031, + 0x00800568, 0x00005028, 0x00000050, 0x00800545,//e + + 0x00800001, 0x00004850, 0x008004e6, 0x0000004e, + 0x008004f4, 0x0000004c, 0x008004b0, 0x00004870, + 0x008004f0, 0x00004830, 0x00000048, 0x0080044e, + 0x00000051, 0x008004d4, 0x00800451, 0x00800007,//f +}; + +static void RGA2_reg_get_param(unsigned char *base, struct rga2_req *msg) +{ + u32 *bRGA_SRC_X_FACTOR; + u32 *bRGA_SRC_Y_FACTOR; + u32 sw, sh; + u32 dw, dh; + u32 param_x, param_y; + + bRGA_SRC_X_FACTOR = (u32 *) (base + RGA2_SRC_X_FACTOR_OFFSET); + bRGA_SRC_Y_FACTOR = (u32 *) (base + RGA2_SRC_Y_FACTOR_OFFSET); + + if (((msg->rotate_mode & 0x3) == 1) || + ((msg->rotate_mode & 0x3) == 3)) { + dw = msg->dst.act_h; + dh = msg->dst.act_w; + } else { + dw = msg->dst.act_w; + dh = msg->dst.act_h; + } + + sw = msg->src.act_w; + sh = msg->src.act_h; + + if (sw > dw) { +#if SCALE_DOWN_LARGE + param_x = ((dw) << 16) / (sw) + 1; +#else + param_x = ((dw) << 16) / (sw); +#endif + *bRGA_SRC_X_FACTOR |= ((param_x & 0xffff) << 0); + } else if (sw < dw) { +#if SCALE_UP_LARGE + param_x = ((sw - 1) << 16) / (dw - 1); +#else + param_x = ((sw) << 16) / (dw); +#endif + *bRGA_SRC_X_FACTOR |= ((param_x & 0xffff) << 16); + } else { + *bRGA_SRC_X_FACTOR = 0; //((1 << 14) << 16) | (1 << 14); + } + + if (sh > dh) { +#if SCALE_DOWN_LARGE + param_y = ((dh) << 16) / (sh) + 1; +#else + param_y = ((dh) << 16) / (sh); +#endif + *bRGA_SRC_Y_FACTOR |= ((param_y & 0xffff) << 0); + } else if (sh < dh) { +#if SCALE_UP_LARGE + param_y = ((sh - 1) << 16) / (dh - 1); +#else + param_y = ((sh) << 16) / (dh); +#endif + *bRGA_SRC_Y_FACTOR |= ((param_y & 0xffff) << 16); + } else { + *bRGA_SRC_Y_FACTOR = 0; //((1 << 14) << 16) | (1 << 14); + } +} + +static void RGA2_set_mode_ctrl(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_MODE_CTL; + u32 reg = 0; + u32 render_mode = msg->render_mode; + + bRGA_MODE_CTL = (u32 *) (base + RGA2_MODE_CTRL_OFFSET); + + if (msg->render_mode == UPDATE_PALETTE_TABLE_MODE) + render_mode = 0x3; + + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_RENDER_MODE)) | + (s_RGA2_MODE_CTRL_SW_RENDER_MODE(render_mode))); + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_BITBLT_MODE)) | + (s_RGA2_MODE_CTRL_SW_BITBLT_MODE(msg->bitblt_mode))); + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_CF_ROP4_PAT)) | + (s_RGA2_MODE_CTRL_SW_CF_ROP4_PAT(msg->color_fill_mode))); + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_ALPHA_ZERO_KET)) | + (s_RGA2_MODE_CTRL_SW_ALPHA_ZERO_KET(msg->alpha_zero_key))); + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_GRADIENT_SAT)) | + (s_RGA2_MODE_CTRL_SW_GRADIENT_SAT(msg->alpha_rop_flag >> 7))); + reg = + ((reg & (~m_RGA2_MODE_CTRL_SW_INTR_CF_E)) | + (s_RGA2_MODE_CTRL_SW_INTR_CF_E(msg->CMD_fin_int_enable))); + + reg = ((reg & (~m_RGA2_MODE_CTRL_SW_MOSAIC_EN)) | + (s_RGA2_MODE_CTRL_SW_MOSAIC_EN(msg->mosaic_info.enable))); + + reg = ((reg & (~m_RGA2_MODE_CTRL_SW_YIN_YOUT_EN)) | + (s_RGA2_MODE_CTRL_SW_YIN_YOUT_EN(msg->yin_yout_en))); + + reg = ((reg & (~m_RGA2_MODE_CTRL_SW_OSD_E)) | + (s_RGA2_MODE_CTRL_SW_OSD_E(msg->osd_info.enable))); + + *bRGA_MODE_CTL = reg; +} + +static void RGA2_set_reg_src_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_SRC_INFO; + u32 *bRGA_SRC_BASE0, *bRGA_SRC_BASE1, *bRGA_SRC_BASE2; + u32 *bRGA_SRC_VIR_INFO; + u32 *bRGA_SRC_ACT_INFO; + u32 *bRGA_MASK_ADDR; + u32 *bRGA_SRC_TR_COLOR0, *bRGA_SRC_TR_COLOR1; + + u8 disable_uv_channel_en = 0; + + u32 reg = 0; + u8 src0_format = 0; + + u8 src0_rb_swp = 0; + u8 src0_alpha_swp = 0; + + u8 src0_cbcr_swp = 0; + u8 pixel_width = 1; + u32 stride = 0; + u32 uv_stride = 0; + u32 mask_stride = 0; + u32 ydiv = 1, xdiv = 2; + u8 yuv10 = 0; + + u32 sw, sh; + u32 dw, dh; + u8 rotate_mode; + u8 vsp_scale_mode = 0; + u8 scale_w_flag, scale_h_flag; + + bRGA_SRC_INFO = (u32 *) (base + RGA2_SRC_INFO_OFFSET); + + bRGA_SRC_BASE0 = (u32 *) (base + RGA2_SRC_BASE0_OFFSET); + bRGA_SRC_BASE1 = (u32 *) (base + RGA2_SRC_BASE1_OFFSET); + bRGA_SRC_BASE2 = (u32 *) (base + RGA2_SRC_BASE2_OFFSET); + + bRGA_SRC_VIR_INFO = (u32 *) (base + RGA2_SRC_VIR_INFO_OFFSET); + bRGA_SRC_ACT_INFO = (u32 *) (base + RGA2_SRC_ACT_INFO_OFFSET); + + bRGA_MASK_ADDR = (u32 *) (base + RGA2_MASK_BASE_OFFSET); + + bRGA_SRC_TR_COLOR0 = (u32 *) (base + RGA2_SRC_TR_COLOR0_OFFSET); + bRGA_SRC_TR_COLOR1 = (u32 *) (base + RGA2_SRC_TR_COLOR1_OFFSET); + + if (msg->src.format == RGA_FORMAT_YCbCr_420_SP_10B || + msg->src.format == RGA_FORMAT_YCrCb_420_SP_10B) { + if ((msg->src.act_w == msg->dst.act_w) && + (msg->src.act_h == msg->dst.act_h) && + (msg->rotate_mode == 0)) + msg->rotate_mode = 1 << 6; + } + + { + rotate_mode = msg->rotate_mode & 0x3; + + sw = msg->src.act_w; + sh = msg->src.act_h; + + if ((rotate_mode == 1) | (rotate_mode == 3)) { + dw = msg->dst.act_h; + dh = msg->dst.act_w; + } else { + dw = msg->dst.act_w; + dh = msg->dst.act_h; + } + + if (sw > dw) + scale_w_flag = 1; + else if (sw < dw) + scale_w_flag = 2; + else { + scale_w_flag = 0; + if (msg->rotate_mode >> 6) + scale_w_flag = 3; + } + + if (sh > dh) + scale_h_flag = 1; + else if (sh < dh) + scale_h_flag = 2; + else { + scale_h_flag = 0; + if (msg->rotate_mode >> 6) + scale_h_flag = 3; + } + + /* uvvds need to force tile mode. */ + if (msg->uvvds_mode && scale_w_flag == 0) + scale_w_flag = 3; + } + + /* VSP scale mode select, HSD > VSD > VSP > HSP */ + if (scale_h_flag == 0x2) { + /* After HSD, VSP needs to check dst_width */ + if ((scale_w_flag == 0x1) && (dw < RGA2_VSP_BICUBIC_LIMIT)) + vsp_scale_mode = 0x0; + else if (sw < RGA2_VSP_BICUBIC_LIMIT) + vsp_scale_mode = 0x0; + else + /* default select bilinear */ + vsp_scale_mode = 0x1; + } + + switch (msg->src.format) { + case RGA_FORMAT_RGBA_8888: + src0_format = 0x0; + pixel_width = 4; + break; + case RGA_FORMAT_BGRA_8888: + src0_format = 0x0; + src0_rb_swp = 0x1; + pixel_width = 4; + break; + case RGA_FORMAT_RGBX_8888: + src0_format = 0x1; + pixel_width = 4; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_BGRX_8888: + src0_format = 0x1; + src0_rb_swp = 0x1; + pixel_width = 4; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_RGB_888: + src0_format = 0x2; + pixel_width = 3; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_BGR_888: + src0_format = 0x2; + src0_rb_swp = 1; + pixel_width = 3; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_RGB_565: + src0_format = 0x4; + pixel_width = 2; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_RGBA_5551: + src0_format = 0x5; + pixel_width = 2; + break; + case RGA_FORMAT_RGBA_4444: + src0_format = 0x6; + pixel_width = 2; + break; + case RGA_FORMAT_BGR_565: + src0_format = 0x4; + pixel_width = 2; + msg->src_trans_mode &= 0x07; + src0_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_5551: + src0_format = 0x5; + pixel_width = 2; + src0_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_4444: + src0_format = 0x6; + pixel_width = 2; + src0_rb_swp = 0x1; + break; + + /* ARGB */ + /* + * In colorkey mode, xrgb/xbgr does not + * need to enable the alpha channel + */ + case RGA_FORMAT_ARGB_8888: + src0_format = 0x0; + pixel_width = 4; + src0_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_8888: + src0_format = 0x0; + pixel_width = 4; + src0_alpha_swp = 1; + src0_rb_swp = 0x1; + break; + case RGA_FORMAT_XRGB_8888: + src0_format = 0x1; + pixel_width = 4; + src0_alpha_swp = 1; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_XBGR_8888: + src0_format = 0x1; + pixel_width = 4; + src0_alpha_swp = 1; + src0_rb_swp = 0x1; + msg->src_trans_mode &= 0x07; + break; + case RGA_FORMAT_ARGB_5551: + src0_format = 0x5; + pixel_width = 2; + src0_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_5551: + src0_format = 0x5; + pixel_width = 2; + src0_alpha_swp = 1; + src0_rb_swp = 0x1; + break; + case RGA_FORMAT_ARGB_4444: + src0_format = 0x6; + pixel_width = 2; + src0_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_4444: + src0_format = 0x6; + pixel_width = 2; + src0_alpha_swp = 1; + src0_rb_swp = 0x1; + break; + + case RGA_FORMAT_YVYU_422: + src0_format = 0x7; + pixel_width = 2; + src0_cbcr_swp = 1; + src0_rb_swp = 0x1; + break; //rbswap=ycswap + case RGA_FORMAT_VYUY_422: + src0_format = 0x7; + pixel_width = 2; + src0_cbcr_swp = 1; + src0_rb_swp = 0x0; + break; + case RGA_FORMAT_YUYV_422: + src0_format = 0x7; + pixel_width = 2; + src0_cbcr_swp = 0; + src0_rb_swp = 0x1; + break; + case RGA_FORMAT_UYVY_422: + src0_format = 0x7; + pixel_width = 2; + src0_cbcr_swp = 0; + src0_rb_swp = 0x0; + break; + + case RGA_FORMAT_YCbCr_422_SP: + src0_format = 0x8; + xdiv = 1; + ydiv = 1; + break; + case RGA_FORMAT_YCbCr_422_P: + src0_format = 0x9; + xdiv = 2; + ydiv = 1; + break; + case RGA_FORMAT_YCbCr_420_SP: + src0_format = 0xa; + xdiv = 1; + ydiv = 2; + break; + case RGA_FORMAT_YCbCr_420_P: + src0_format = 0xb; + xdiv = 2; + ydiv = 2; + break; + case RGA_FORMAT_YCrCb_422_SP: + src0_format = 0x8; + xdiv = 1; + ydiv = 1; + src0_cbcr_swp = 1; + break; + case RGA_FORMAT_YCrCb_422_P: + src0_format = 0x9; + xdiv = 2; + ydiv = 1; + src0_cbcr_swp = 1; + break; + case RGA_FORMAT_YCrCb_420_SP: + src0_format = 0xa; + xdiv = 1; + ydiv = 2; + src0_cbcr_swp = 1; + break; + case RGA_FORMAT_YCrCb_420_P: + src0_format = 0xb; + xdiv = 2; + ydiv = 2; + src0_cbcr_swp = 1; + break; + + case RGA_FORMAT_YCbCr_420_SP_10B: + src0_format = 0xa; + xdiv = 1; + ydiv = 2; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_420_SP_10B: + src0_format = 0xa; + xdiv = 1; + ydiv = 2; + src0_cbcr_swp = 1; + yuv10 = 1; + break; + case RGA_FORMAT_YCbCr_422_SP_10B: + src0_format = 0x8; + xdiv = 1; + ydiv = 1; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_422_SP_10B: + src0_format = 0x8; + xdiv = 1; + ydiv = 1; + src0_cbcr_swp = 1; + yuv10 = 1; + break; + + case RGA_FORMAT_YCbCr_400: + src0_format = 0x8; + /* When Yin_Yout is enabled, no need to go through the software. */ + disable_uv_channel_en = msg->yin_yout_en ? false : true; + xdiv = 1; + ydiv = 1; + break; + }; + + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SRC_FMT)) | + (s_RGA2_SRC_INFO_SW_SRC_FMT(src0_format))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_RB_SWAP)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_RB_SWAP(src0_rb_swp))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_ALPHA_SWAP)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_ALPHA_SWAP(src0_alpha_swp))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_UV_SWAP)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_UV_SWAP(src0_cbcr_swp))); + + if (msg->src1.format == RGA_FORMAT_RGBA_2BPP) + reg = ((reg & (~m_RGA2_SRC_INFO_SW_SW_CP_ENDIAN)) | + (s_RGA2_SRC_INFO_SW_SW_CP_ENDAIN(msg->osd_info.bpp2_info.endian_swap & 1))); + + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_CSC_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_CSC_MODE(msg->yuv2rgb_mode))); + + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_ROT_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_ROT_MODE(msg->rotate_mode & 0x3))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_MIR_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_MIR_MODE + ((msg->rotate_mode >> 4) & 0x3))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_HSCL_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_HSCL_MODE((scale_w_flag)))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_VSCL_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_VSCL_MODE((scale_h_flag)))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_SCL_FILTER)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_SCL_FILTER(( + msg->scale_bicu_mode)))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_TRANS_MODE)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_TRANS_MODE(msg->src_trans_mode))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_TRANS_E)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_TRANS_E(msg->src_trans_mode >> 1))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_SRC_DITHER_UP_E)) | + (s_RGA2_SRC_INFO_SW_SW_SRC_DITHER_UP_E + ((msg->alpha_rop_flag >> 4) & 0x1))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_VSP_MODE_SEL)) | + (s_RGA2_SRC_INFO_SW_SW_VSP_MODE_SEL((vsp_scale_mode)))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_YUV10_E)) | + (s_RGA2_SRC_INFO_SW_SW_YUV10_E((yuv10)))); + + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_YUV10_ROUND_E)) | + (s_RGA2_SRC_INFO_SW_SW_YUV10_ROUND_E((yuv10)))); + + RGA2_reg_get_param(base, msg); + + stride = (((msg->src.vir_w * pixel_width) + 3) & ~3) >> 2; + uv_stride = ((msg->src.vir_w / xdiv + 3) & ~3); + + if (disable_uv_channel_en == 1) { + /* + * When Y400 as the input format, because the current + * RGA does not support closing + * the access of the UV channel, the address of the UV + * channel access is equal to + * the address of the Y channel access to ensure that + * the UV channel can access, + * preventing the RGA hardware from reporting errors. + */ + *bRGA_SRC_BASE0 = + (u32) (msg->src.yrgb_addr + + msg->src.y_offset * (stride << 2) + + msg->src.x_offset * pixel_width); + *bRGA_SRC_BASE1 = *bRGA_SRC_BASE0; + *bRGA_SRC_BASE2 = *bRGA_SRC_BASE0; + } else { + *bRGA_SRC_BASE0 = + (u32) (msg->src.yrgb_addr + + msg->src.y_offset * (stride << 2) + + msg->src.x_offset * pixel_width); + *bRGA_SRC_BASE1 = + (u32) (msg->src.uv_addr + + (msg->src.y_offset / ydiv) * uv_stride + + (msg->src.x_offset / xdiv)); + *bRGA_SRC_BASE2 = + (u32) (msg->src.v_addr + + (msg->src.y_offset / ydiv) * uv_stride + + (msg->src.x_offset / xdiv)); + } + + //mask_stride = ((msg->src0_act.width + 31) & ~31) >> 5; + mask_stride = msg->rop_mask_stride; + + *bRGA_SRC_VIR_INFO = stride | (mask_stride << 16); + + *bRGA_SRC_ACT_INFO = + (msg->src.act_w - 1) | ((msg->src.act_h - 1) << 16); + + *bRGA_MASK_ADDR = (u32) msg->rop_mask_addr; + + *bRGA_SRC_INFO = reg; + + *bRGA_SRC_TR_COLOR0 = msg->color_key_min; + *bRGA_SRC_TR_COLOR1 = msg->color_key_max; +} + +static void RGA2_set_reg_dst_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_DST_INFO; + u32 *bRGA_DST_BASE0, *bRGA_DST_BASE1, *bRGA_DST_BASE2, + *bRGA_SRC_BASE3; + u32 *bRGA_DST_VIR_INFO; + u32 *bRGA_DST_ACT_INFO; + + u32 *RGA_DST_Y4MAP_LUT0; //Y4 LUT0 + u32 *RGA_DST_Y4MAP_LUT1; //Y4 LUT1 + u32 *RGA_DST_NN_QUANTIZE_SCALE; + u32 *RGA_DST_NN_QUANTIZE_OFFSET; + + u32 line_width_real; + + u8 ydither_en = 0; + + u8 src1_format = 0; + u8 src1_rb_swp = 0; + u8 src1_alpha_swp = 0; + + u8 dst_format = 0; + u8 dst_rb_swp = 0; + u8 dst_cbcr_swp = 0; + u8 dst_alpha_swp = 0; + + u8 dst_fmt_yuv400_en = 0; + u8 dst_fmt_y4_en = 0; + u8 dst_nn_quantize_en = 0; + + u32 reg = 0; + u8 spw, dpw; + u8 bbp_shift = 0; + u32 s_stride, d_stride; + u32 x_mirr, y_mirr, rot_90_flag; + u32 yrgb_addr, u_addr, v_addr, s_yrgb_addr; + u32 d_uv_stride, x_div, y_div; + u32 y_lt_addr, y_ld_addr, y_rt_addr, y_rd_addr; + u32 u_lt_addr, u_ld_addr, u_rt_addr, u_rd_addr; + u32 v_lt_addr, v_ld_addr, v_rt_addr, v_rd_addr; + + dpw = 1; + x_div = y_div = 1; + + dst_nn_quantize_en = (msg->alpha_rop_flag >> 8) & 0x1; + + bRGA_DST_INFO = (u32 *) (base + RGA2_DST_INFO_OFFSET); + bRGA_DST_BASE0 = (u32 *) (base + RGA2_DST_BASE0_OFFSET); + bRGA_DST_BASE1 = (u32 *) (base + RGA2_DST_BASE1_OFFSET); + bRGA_DST_BASE2 = (u32 *) (base + RGA2_DST_BASE2_OFFSET); + + bRGA_SRC_BASE3 = (u32 *) (base + RGA2_SRC_BASE3_OFFSET); + + bRGA_DST_VIR_INFO = (u32 *) (base + RGA2_DST_VIR_INFO_OFFSET); + bRGA_DST_ACT_INFO = (u32 *) (base + RGA2_DST_ACT_INFO_OFFSET); + + RGA_DST_Y4MAP_LUT0 = (u32 *) (base + RGA2_DST_Y4MAP_LUT0_OFFSET); + RGA_DST_Y4MAP_LUT1 = (u32 *) (base + RGA2_DST_Y4MAP_LUT1_OFFSET); + RGA_DST_NN_QUANTIZE_SCALE = + (u32 *) (base + RGA2_DST_QUANTIZE_SCALE_OFFSET); + RGA_DST_NN_QUANTIZE_OFFSET = + (u32 *) (base + RGA2_DST_QUANTIZE_OFFSET_OFFSET); + + switch (msg->src1.format) { + case RGA_FORMAT_RGBA_8888: + src1_format = 0x0; + spw = 4; + break; + case RGA_FORMAT_BGRA_8888: + src1_format = 0x0; + src1_rb_swp = 0x1; + spw = 4; + break; + case RGA_FORMAT_RGBX_8888: + src1_format = 0x1; + spw = 4; + break; + case RGA_FORMAT_BGRX_8888: + src1_format = 0x1; + src1_rb_swp = 0x1; + spw = 4; + break; + case RGA_FORMAT_RGB_888: + src1_format = 0x2; + spw = 3; + break; + case RGA_FORMAT_BGR_888: + src1_format = 0x2; + src1_rb_swp = 1; + spw = 3; + break; + case RGA_FORMAT_RGB_565: + src1_format = 0x4; + spw = 2; + break; + case RGA_FORMAT_RGBA_5551: + src1_format = 0x5; + spw = 2; + break; + case RGA_FORMAT_RGBA_4444: + src1_format = 0x6; + spw = 2; + break; + case RGA_FORMAT_BGR_565: + src1_format = 0x4; + spw = 2; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_5551: + src1_format = 0x5; + spw = 2; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_4444: + src1_format = 0x6; + spw = 2; + src1_rb_swp = 0x1; + break; + + /* ARGB */ + case RGA_FORMAT_ARGB_8888: + src1_format = 0x0; + spw = 4; + src1_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_8888: + src1_format = 0x0; + spw = 4; + src1_alpha_swp = 1; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_XRGB_8888: + src1_format = 0x1; + spw = 4; + src1_alpha_swp = 1; + break; + case RGA_FORMAT_XBGR_8888: + src1_format = 0x1; + spw = 4; + src1_alpha_swp = 1; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_ARGB_5551: + src1_format = 0x5; + spw = 2; + src1_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_5551: + src1_format = 0x5; + spw = 2; + src1_alpha_swp = 1; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_ARGB_4444: + src1_format = 0x6; + spw = 2; + src1_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_4444: + src1_format = 0x6; + spw = 2; + src1_alpha_swp = 1; + src1_rb_swp = 0x1; + break; + case RGA_FORMAT_RGBA_2BPP: + src1_format = 0x0; + spw = 1; + /* 2BPP = 8 >> 2 = 2bit */ + bbp_shift = 2; + src1_alpha_swp = msg->osd_info.bpp2_info.ac_swap; + break; + default: + spw = 4; + break; + }; + + reg = + ((reg & (~m_RGA2_DST_INFO_SW_SRC1_FMT)) | + (s_RGA2_DST_INFO_SW_SRC1_FMT(src1_format))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_SRC1_RB_SWP)) | + (s_RGA2_DST_INFO_SW_SRC1_RB_SWP(src1_rb_swp))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_SRC1_ALPHA_SWP)) | + (s_RGA2_DST_INFO_SW_SRC1_ALPHA_SWP(src1_alpha_swp))); + + switch (msg->dst.format) { + case RGA_FORMAT_RGBA_8888: + dst_format = 0x0; + dpw = 4; + break; + case RGA_FORMAT_BGRA_8888: + dst_format = 0x0; + dst_rb_swp = 0x1; + dpw = 4; + break; + case RGA_FORMAT_RGBX_8888: + dst_format = 0x1; + dpw = 4; + break; + case RGA_FORMAT_BGRX_8888: + dst_format = 0x1; + dst_rb_swp = 0x1; + dpw = 4; + break; + case RGA_FORMAT_RGB_888: + dst_format = 0x2; + dpw = 3; + break; + case RGA_FORMAT_BGR_888: + dst_format = 0x2; + dst_rb_swp = 1; + dpw = 3; + break; + case RGA_FORMAT_RGB_565: + dst_format = 0x4; + dpw = 2; + break; + case RGA_FORMAT_RGBA_5551: + dst_format = 0x5; + dpw = 2; + break; + case RGA_FORMAT_RGBA_4444: + dst_format = 0x6; + dpw = 2; + break; + case RGA_FORMAT_BGR_565: + dst_format = 0x4; + dpw = 2; + dst_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_5551: + dst_format = 0x5; + dpw = 2; + dst_rb_swp = 0x1; + break; + case RGA_FORMAT_BGRA_4444: + dst_format = 0x6; + dpw = 2; + dst_rb_swp = 0x1; + break; + + /* ARGB */ + case RGA_FORMAT_ARGB_8888: + dst_format = 0x0; + dpw = 4; + dst_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_8888: + dst_format = 0x0; + dpw = 4; + dst_alpha_swp = 1; + dst_rb_swp = 0x1; + break; + case RGA_FORMAT_XRGB_8888: + dst_format = 0x1; + dpw = 4; + dst_alpha_swp = 1; + break; + case RGA_FORMAT_XBGR_8888: + dst_format = 0x1; + dpw = 4; + dst_alpha_swp = 1; + dst_rb_swp = 0x1; + break; + case RGA_FORMAT_ARGB_5551: + dst_format = 0x5; + dpw = 2; + dst_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_5551: + dst_format = 0x5; + dpw = 2; + dst_alpha_swp = 1; + dst_rb_swp = 0x1; + break; + case RGA_FORMAT_ARGB_4444: + dst_format = 0x6; + dpw = 2; + dst_alpha_swp = 1; + break; + case RGA_FORMAT_ABGR_4444: + dst_format = 0x6; + dpw = 2; + dst_alpha_swp = 1; + dst_rb_swp = 0x1; + break; + + case RGA_FORMAT_YCbCr_422_SP: + dst_format = 0x8; + x_div = 1; + y_div = 1; + break; + case RGA_FORMAT_YCbCr_422_P: + dst_format = 0x9; + x_div = 2; + y_div = 1; + break; + case RGA_FORMAT_YCbCr_420_SP: + dst_format = 0xa; + x_div = 1; + y_div = 2; + break; + case RGA_FORMAT_YCbCr_420_P: + dst_format = 0xb; + dst_cbcr_swp = 1; + x_div = 2; + y_div = 2; + break; + case RGA_FORMAT_YCrCb_422_SP: + dst_format = 0x8; + dst_cbcr_swp = 1; + x_div = 1; + y_div = 1; + break; + case RGA_FORMAT_YCrCb_422_P: + dst_format = 0x9; + dst_cbcr_swp = 1; + x_div = 2; + y_div = 1; + break; + case RGA_FORMAT_YCrCb_420_SP: + dst_format = 0xa; + dst_cbcr_swp = 1; + x_div = 1; + y_div = 2; + break; + case RGA_FORMAT_YCrCb_420_P: + dst_format = 0xb; + x_div = 2; + y_div = 2; + break; + + case RGA_FORMAT_YCbCr_400: + dst_format = 0x8; + dst_fmt_yuv400_en = 1; + x_div = 1; + y_div = 1; + break; + case RGA_FORMAT_Y4: + dst_format = 0x8; + dst_fmt_y4_en = 1; + dst_fmt_yuv400_en = 1; + x_div = 1; + y_div = 1; + break; + + case RGA_FORMAT_YUYV_422: + dst_format = 0xe; + dpw = 2; + dst_cbcr_swp = 1; + break; + case RGA_FORMAT_YVYU_422: + dst_format = 0xe; + dpw = 2; + break; + case RGA_FORMAT_YUYV_420: + dst_format = 0xf; + dpw = 2; + dst_cbcr_swp = 1; + break; + case RGA_FORMAT_YVYU_420: + dst_format = 0xf; + dpw = 2; + break; + case RGA_FORMAT_UYVY_422: + dst_format = 0xc; + dpw = 2; + dst_cbcr_swp = 1; + break; + case RGA_FORMAT_VYUY_422: + dst_format = 0xc; + dpw = 2; + break; + case RGA_FORMAT_UYVY_420: + dst_format = 0xd; + dpw = 2; + dst_cbcr_swp = 1; + break; + case RGA_FORMAT_VYUY_420: + dst_format = 0xd; + dpw = 2; + break; + }; + + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_FMT)) | + (s_RGA2_DST_INFO_SW_DST_FMT(dst_format))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_RB_SWAP)) | + (s_RGA2_DST_INFO_SW_DST_RB_SWAP(dst_rb_swp))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_ALPHA_SWAP)) | + (s_RGA2_DST_INFO_SW_ALPHA_SWAP(dst_alpha_swp))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_UV_SWAP)) | + (s_RGA2_DST_INFO_SW_DST_UV_SWAP(dst_cbcr_swp))); + + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_FMT_YUV400_EN)) | + (s_RGA2_DST_INFO_SW_DST_FMT_YUV400_EN(dst_fmt_yuv400_en))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_FMT_Y4_EN)) | + (s_RGA2_DST_INFO_SW_DST_FMT_Y4_EN(dst_fmt_y4_en))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_NN_QUANTIZE_EN)) | + (s_RGA2_DST_INFO_SW_DST_NN_QUANTIZE_EN(dst_nn_quantize_en))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DITHER_UP_E)) | + (s_RGA2_DST_INFO_SW_DITHER_UP_E(msg->alpha_rop_flag >> 5))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DITHER_DOWN_E)) | + (s_RGA2_DST_INFO_SW_DITHER_DOWN_E(msg->alpha_rop_flag >> 6))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DITHER_MODE)) | + (s_RGA2_DST_INFO_SW_DITHER_MODE(msg->dither_mode))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_CSC_MODE)) | + (s_RGA2_DST_INFO_SW_DST_CSC_MODE(msg->yuv2rgb_mode >> 2))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_CSC_CLIP_MODE)) | + (s_RGA2_DST_INFO_SW_CSC_CLIP_MODE(msg->yuv2rgb_mode >> 4))); + /* full csc enable */ + reg = + ((reg & (~m_RGA2_DST_INFO_SW_DST_CSC_MODE_2)) | + (s_RGA2_DST_INFO_SW_DST_CSC_MODE_2(msg->full_csc_en))); + /* + * Some older chips do not support src1 csc mode, + * they do not have these two registers. + */ + reg = + ((reg & (~m_RGA2_DST_INFO_SW_SRC1_CSC_MODE)) | + (s_RGA2_DST_INFO_SW_SRC1_CSC_MODE(msg->yuv2rgb_mode >> 5))); + reg = + ((reg & (~m_RGA2_DST_INFO_SW_SRC1_CSC_CLIP_MODE)) | + (s_RGA2_DST_INFO_SW_SRC1_CSC_CLIP_MODE( + msg->yuv2rgb_mode >> 7))); + + reg = ((reg & (~m_RGA2_DST_INFO_SW_DST_UVHDS_MODE)) | + (s_RGA2_DST_INFO_SW_DST_UVHDS_MODE(msg->uvhds_mode))); + reg = ((reg & (~m_RGA2_DST_INFO_SW_DST_UVVDS_MODE)) | + (s_RGA2_DST_INFO_SW_DST_UVVDS_MODE(msg->uvvds_mode))); + + ydither_en = (msg->dst.format == RGA_FORMAT_Y4) + && ((msg->alpha_rop_flag >> 6) & 0x1); + + *bRGA_DST_INFO = reg; + + s_stride = (((msg->src1.vir_w * spw >> bbp_shift) + 3) & ~3) >> 2; + d_stride = ((msg->dst.vir_w * dpw + 3) & ~3) >> 2; + + if (dst_fmt_y4_en) { + /* Y4 output will HALF */ + d_stride = ((d_stride + 1) & ~1) >> 1; + } + + d_uv_stride = (d_stride << 2) / x_div; + + *bRGA_DST_VIR_INFO = d_stride | (s_stride << 16); + if ((msg->dst.vir_w % 2 != 0) && + (msg->dst.act_w == msg->src.act_w) + && (msg->dst.act_h == msg->src.act_h) + && (msg->dst.format == RGA_FORMAT_BGR_888 + || msg->dst.format == RGA_FORMAT_RGB_888)) + *bRGA_DST_ACT_INFO = + (msg->dst.act_w) | ((msg->dst.act_h - 1) << 16); + else + *bRGA_DST_ACT_INFO = + (msg->dst.act_w - 1) | ((msg->dst.act_h - 1) << 16); + s_stride <<= 2; + d_stride <<= 2; + + if (((msg->rotate_mode & 0xf) == 0) || + ((msg->rotate_mode & 0xf) == 1)) { + x_mirr = 0; + y_mirr = 0; + } else { + x_mirr = 1; + y_mirr = 1; + } + + rot_90_flag = msg->rotate_mode & 1; + x_mirr = (x_mirr + ((msg->rotate_mode >> 4) & 1)) & 1; + y_mirr = (y_mirr + ((msg->rotate_mode >> 5) & 1)) & 1; + + if (ydither_en) { + if (x_mirr && y_mirr) { + pr_err("ydither mode do not support rotate x_mirr=%d,y_mirr=%d\n", + x_mirr, y_mirr); + } + + if (msg->dst.act_w != msg->src.act_w) + pr_err("ydither mode do not support x dir scale\n"); + + if (msg->dst.act_h != msg->src.act_h) + pr_err("ydither mode do not support y dir scale\n"); + } + + if (dst_fmt_y4_en) { + *RGA_DST_Y4MAP_LUT0 = (msg->gr_color.gr_x_r & 0xffff) | + (msg->gr_color.gr_x_g << 16); + *RGA_DST_Y4MAP_LUT1 = (msg->gr_color.gr_y_r & 0xffff) | + (msg->gr_color.gr_y_g << 16); + } + + if (dst_nn_quantize_en) { + *RGA_DST_NN_QUANTIZE_SCALE = (msg->gr_color.gr_x_r & 0xffff) | + (msg->gr_color.gr_x_g << 10) | + (msg->gr_color.gr_x_b << 20); + *RGA_DST_NN_QUANTIZE_OFFSET = (msg->gr_color.gr_y_r & 0xffff) | + (msg->gr_color.gr_y_g << 10) | + (msg->gr_color.gr_y_b << 20); + } + + s_yrgb_addr = + (u32) msg->src1.yrgb_addr + (msg->src1.y_offset * s_stride) + + (msg->src1.x_offset * spw >> bbp_shift); + + *bRGA_SRC_BASE3 = s_yrgb_addr; + + if (dst_fmt_y4_en) { + yrgb_addr = (u32) msg->dst.yrgb_addr + + (msg->dst.y_offset * d_stride) + + ((msg->dst.x_offset * dpw) >> 1); + } else { + yrgb_addr = (u32) msg->dst.yrgb_addr + + (msg->dst.y_offset * d_stride) + + (msg->dst.x_offset * dpw); + } + u_addr = (u32) msg->dst.uv_addr + + (msg->dst.y_offset / y_div) * d_uv_stride + + msg->dst.x_offset / x_div; + v_addr = (u32) msg->dst.v_addr + + (msg->dst.y_offset / y_div) * d_uv_stride + + msg->dst.x_offset / x_div; + + y_lt_addr = yrgb_addr; + u_lt_addr = u_addr; + v_lt_addr = v_addr; + + /* Warning */ + line_width_real = + dst_fmt_y4_en ? ((msg->dst.act_w) >> 1) : msg->dst.act_w; + + /* + * YUV packet mode is a new format, and the write behavior during + * rotation is different from the old format. + */ + if (rga_is_yuv422_packed_format(msg->dst.format)) { + y_ld_addr = yrgb_addr + (msg->dst.act_h - 1) * (d_stride); + y_rt_addr = yrgb_addr + (msg->dst.act_w * 2 - 1); + y_rd_addr = y_ld_addr + (msg->dst.act_w * 2 - 1); + } else if (rga_is_yuv420_packed_format(msg->dst.format)) { + y_ld_addr = (u32)msg->dst.yrgb_addr + + ((msg->dst.y_offset + (msg->dst.act_h - 1)) * d_stride) + + msg->dst.x_offset; + y_rt_addr = yrgb_addr + (msg->dst.act_w * 2 - 1); + y_rd_addr = y_ld_addr + (msg->dst.act_w - 1); + } else { + /* 270 degree & Mirror V */ + y_ld_addr = yrgb_addr + (msg->dst.act_h - 1) * (d_stride); + /* 90 degree & Mirror H */ + y_rt_addr = yrgb_addr + (line_width_real - 1) * dpw; + /* 180 degree */ + y_rd_addr = y_ld_addr + (line_width_real - 1) * dpw; + } + + u_ld_addr = u_addr + ((msg->dst.act_h / y_div) - 1) * (d_uv_stride); + v_ld_addr = v_addr + ((msg->dst.act_h / y_div) - 1) * (d_uv_stride); + + u_rt_addr = u_addr + (msg->dst.act_w / x_div) - 1; + v_rt_addr = v_addr + (msg->dst.act_w / x_div) - 1; + + u_rd_addr = u_ld_addr + (msg->dst.act_w / x_div) - 1; + v_rd_addr = v_ld_addr + (msg->dst.act_w / x_div) - 1; + + if (rot_90_flag == 0) { + if (y_mirr == 1) { + if (x_mirr == 1) { + yrgb_addr = y_rd_addr; + u_addr = u_rd_addr; + v_addr = v_rd_addr; + } else { + yrgb_addr = y_ld_addr; + u_addr = u_ld_addr; + v_addr = v_ld_addr; + } + } else { + if (x_mirr == 1) { + yrgb_addr = y_rt_addr; + u_addr = u_rt_addr; + v_addr = v_rt_addr; + } else { + yrgb_addr = y_lt_addr; + u_addr = u_lt_addr; + v_addr = v_lt_addr; + } + } + } else { + if (y_mirr == 1) { + if (x_mirr == 1) { + yrgb_addr = y_ld_addr; + u_addr = u_ld_addr; + v_addr = v_ld_addr; + } else { + yrgb_addr = y_rd_addr; + u_addr = u_rd_addr; + v_addr = v_rd_addr; + } + } else { + if (x_mirr == 1) { + yrgb_addr = y_lt_addr; + u_addr = u_lt_addr; + v_addr = v_lt_addr; + } else { + yrgb_addr = y_rt_addr; + u_addr = u_rt_addr; + v_addr = v_rt_addr; + } + } + } + + *bRGA_DST_BASE0 = (u32) yrgb_addr; + + if ((msg->dst.format == RGA_FORMAT_YCbCr_420_P) + || (msg->dst.format == RGA_FORMAT_YCrCb_420_P)) { + if (dst_cbcr_swp == 0) { + *bRGA_DST_BASE1 = (u32) v_addr; + *bRGA_DST_BASE2 = (u32) u_addr; + } else { + *bRGA_DST_BASE1 = (u32) u_addr; + *bRGA_DST_BASE2 = (u32) v_addr; + } + } else { + *bRGA_DST_BASE1 = (u32) u_addr; + *bRGA_DST_BASE2 = (u32) v_addr; + } +} + +static void RGA2_set_reg_alpha_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_ALPHA_CTRL0; + u32 *bRGA_ALPHA_CTRL1; + u32 *bRGA_FADING_CTRL; + u32 reg = 0; + union rga2_color_ctrl color_ctrl; + union rga2_alpha_ctrl alpha_ctrl; + struct rga_alpha_config *config; + + bRGA_ALPHA_CTRL0 = (u32 *) (base + RGA2_ALPHA_CTRL0_OFFSET); + bRGA_ALPHA_CTRL1 = (u32 *) (base + RGA2_ALPHA_CTRL1_OFFSET); + bRGA_FADING_CTRL = (u32 *) (base + RGA2_FADING_CTRL_OFFSET); + + color_ctrl.value = 0; + alpha_ctrl.value = 0; + config = &msg->alpha_config; + + color_ctrl.bits.src_color_mode = + config->fg_pre_multiplied ? RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED; + color_ctrl.bits.dst_color_mode = + config->bg_pre_multiplied ? RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED; + + if (config->fg_pixel_alpha_en) + color_ctrl.bits.src_blend_mode = + config->fg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL : + RGA_ALPHA_PER_PIXEL; + else + color_ctrl.bits.src_blend_mode = RGA_ALPHA_GLOBAL; + + if (config->bg_pixel_alpha_en) + color_ctrl.bits.dst_blend_mode = + config->bg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL : + RGA_ALPHA_PER_PIXEL; + else + color_ctrl.bits.dst_blend_mode = RGA_ALPHA_GLOBAL; + + /* + * Since the hardware uses 256 as 1, the original alpha value needs to + * be + (alpha >> 7). + */ + color_ctrl.bits.src_alpha_cal_mode = RGA_ALPHA_SATURATION; + color_ctrl.bits.dst_alpha_cal_mode = RGA_ALPHA_SATURATION; + + /* porter duff alpha enable */ + switch (config->mode) { + case RGA_ALPHA_BLEND_SRC: + /* + * SRC mode: + * Sf = 1, Df = 0; + * [Rc,Ra] = [Sc,Sa]; + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ONE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST: + /* + * SRC mode: + * Sf = 0, Df = 1; + * [Rc,Ra] = [Dc,Da]; + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ONE; + + break; + + case RGA_ALPHA_BLEND_SRC_OVER: + /* + * SRC-OVER mode: + * Sf = 1, Df = (1 - Sa) + * [Rc,Ra] = [ Sc + (1 - Sa) * Dc, Sa + (1 - Sa) * Da ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ONE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_DST_OVER: + /* + * DST-OVER mode: + * Sf = (1 - Da) , Df = 1 + * [Rc,Ra] = [ Sc * (1 - Da) + Dc, Sa * (1 - Da) + Da ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ONE; + + break; + + case RGA_ALPHA_BLEND_SRC_IN: + /* + * SRC-IN mode: + * Sf = Da , Df = 0 + * [Rc,Ra] = [ Sc * Da, Sa * Da ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST_IN: + /* + * DST-IN mode: + * Sf = 0 , Df = Sa + * [Rc,Ra] = [ Dc * Sa, Da * Sa ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE; + + break; + + case RGA_ALPHA_BLEND_SRC_OUT: + /* + * SRC-OUT mode: + * Sf = (1 - Da) , Df = 0 + * [Rc,Ra] = [ Sc * (1 - Da), Sa * (1 - Da) ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST_OUT: + /* + * DST-OUT mode: + * Sf = 0 , Df = (1 - Sa) + * [Rc,Ra] = [ Dc * (1 - Sa), Da * (1 - Sa) ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_SRC_ATOP: + /* + * SRC-ATOP mode: + * Sf = Da , Df = (1 - Sa) + * [Rc,Ra] = [ Sc * Da + Dc * (1 - Sa), Sa * Da + Da * (1 - Sa) ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_DST_ATOP: + /* + * DST-ATOP mode: + * Sf = (1 - Da) , Df = Sa + * [Rc,Ra] = [ Sc * (1 - Da) + Dc * Sa, Sa * (1 - Da) + Da * Sa ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE; + + break; + + case RGA_ALPHA_BLEND_XOR: + /* + * DST-XOR mode: + * Sf = (1 - Da) , Df = (1 - Sa) + * [Rc,Ra] = [ Sc * (1 - Da) + Dc * (1 - Sa), Sa * (1 - Da) + Da * (1 - Sa) ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_CLEAR: + /* + * DST-CLEAR mode: + * Sf = 0 , Df = 0 + * [Rc,Ra] = [ 0, 0 ] + */ + color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO; + + color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT; + color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO; + + break; + + default: + break; + } + + alpha_ctrl.bits.src_blend_mode = color_ctrl.bits.src_blend_mode; + alpha_ctrl.bits.dst_blend_mode = color_ctrl.bits.dst_blend_mode; + + alpha_ctrl.bits.src_alpha_cal_mode = color_ctrl.bits.src_alpha_cal_mode; + alpha_ctrl.bits.dst_alpha_cal_mode = color_ctrl.bits.dst_alpha_cal_mode; + + alpha_ctrl.bits.src_alpha_mode = color_ctrl.bits.src_alpha_mode; + alpha_ctrl.bits.src_factor_mode = color_ctrl.bits.src_factor_mode; + + alpha_ctrl.bits.dst_alpha_mode = color_ctrl.bits.dst_alpha_mode; + alpha_ctrl.bits.dst_factor_mode = color_ctrl.bits.dst_factor_mode; + + reg = + ((reg & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0)) | + (s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0(msg->alpha_rop_flag))); + reg = + ((reg & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL)) | + (s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL + (msg->alpha_rop_flag >> 1))); + reg = + ((reg & (~m_RGA2_ALPHA_CTRL0_SW_ROP_MODE)) | + (s_RGA2_ALPHA_CTRL0_SW_ROP_MODE(msg->rop_mode))); + reg = + ((reg & (~m_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA)) | + (s_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA + ((uint8_t)config->fg_global_alpha_value))); + reg = + ((reg & (~m_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA)) | + (s_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA + ((uint8_t)config->bg_global_alpha_value))); + + *bRGA_ALPHA_CTRL0 = reg; + *bRGA_ALPHA_CTRL1 = color_ctrl.value | (alpha_ctrl.value << 16); + + + if ((msg->alpha_rop_flag >> 2) & 1) { + *bRGA_FADING_CTRL = (1 << 24) | (msg->fading_b_value << 16) | + (msg->fading_g_value << 8) | (msg->fading_r_value); + } +} + +static void RGA2_set_reg_rop_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_ALPHA_CTRL0; + u32 *bRGA_ROP_CTRL0; + u32 *bRGA_ROP_CTRL1; + u32 *bRGA_MASK_ADDR; + u32 *bRGA_FG_COLOR; + u32 *bRGA_PAT_CON; + + u32 rop_code0 = 0; + u32 rop_code1 = 0; + + bRGA_ALPHA_CTRL0 = (u32 *) (base + RGA2_ALPHA_CTRL0_OFFSET); + bRGA_ROP_CTRL0 = (u32 *) (base + RGA2_ROP_CTRL0_OFFSET); + bRGA_ROP_CTRL1 = (u32 *) (base + RGA2_ROP_CTRL1_OFFSET); + bRGA_MASK_ADDR = (u32 *) (base + RGA2_MASK_BASE_OFFSET); + bRGA_FG_COLOR = (u32 *) (base + RGA2_SRC_FG_COLOR_OFFSET); + bRGA_PAT_CON = (u32 *) (base + RGA2_PAT_CON_OFFSET); + + if (msg->rop_mode == 0) { + rop_code0 = rga2_rop_code[(msg->rop_code & 0xff)]; + } else if (msg->rop_mode == 1) { + rop_code0 = rga2_rop_code[(msg->rop_code & 0xff)]; + } else if (msg->rop_mode == 2) { + rop_code0 = rga2_rop_code[(msg->rop_code & 0xff)]; + rop_code1 = rga2_rop_code[(msg->rop_code & 0xff00) >> 8]; + } + + *bRGA_ROP_CTRL0 = rop_code0; + *bRGA_ROP_CTRL1 = rop_code1; + *bRGA_FG_COLOR = msg->fg_color; + *bRGA_MASK_ADDR = (u32) msg->rop_mask_addr; + *bRGA_PAT_CON = (msg->pat.act_w - 1) | ((msg->pat.act_h - 1) << 8) + | (msg->pat.x_offset << 16) | (msg->pat.y_offset << 24); + *bRGA_ALPHA_CTRL0 = + *bRGA_ALPHA_CTRL0 | (((msg->endian_mode >> 1) & 1) << 20); + +} + +static void RGA_set_reg_mosaic(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_MOSAIC_MODE; + + bRGA_MOSAIC_MODE = (u32 *)(base + RGA2_MOSAIC_MODE_OFFSET); + + *bRGA_MOSAIC_MODE = (u32)(msg->mosaic_info.mode & 0x7); +} + +static void RGA2_set_reg_osd(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_OSD_CTRL0; + u32 *bRGA_OSD_CTRL1; + u32 *bRGA_OSD_INVERTSION_CAL0; + u32 *bRGA_OSD_INVERTSION_CAL1; + u32 *bRGA_OSD_COLOR0; + u32 *bRGA_OSD_COLOR1; + u32 *bRGA_OSD_LAST_FLAGS0; + u32 *bRGA_OSD_LAST_FLAGS1; + u32 reg; + u8 rgba2bpp_en = 0; + u8 block_num; + u16 fix_width; + + + bRGA_OSD_CTRL0 = (u32 *)(base + RGA2_OSD_CTRL0_OFFSET); + bRGA_OSD_CTRL1 = (u32 *)(base + RGA2_OSD_CTRL1_OFFSET); + bRGA_OSD_INVERTSION_CAL0 = (u32 *)(base + RGA2_OSD_INVERTSION_CAL0_OFFSET); + bRGA_OSD_INVERTSION_CAL1 = (u32 *)(base + RGA2_OSD_INVERTSION_CAL1_OFFSET); + bRGA_OSD_COLOR0 = (u32 *)(base + RGA2_OSD_COLOR0_OFFSET); + bRGA_OSD_COLOR1 = (u32 *)(base + RGA2_OSD_COLOR1_OFFSET); + bRGA_OSD_LAST_FLAGS0 = (u32 *)(base + RGA2_OSD_LAST_FLAGS0_OFFSET); + bRGA_OSD_LAST_FLAGS1 = (u32 *)(base + RGA2_OSD_LAST_FLAGS1_OFFSET); + + /* To save the number of register bits. */ + fix_width = msg->osd_info.mode_ctrl.block_fix_width / 2 - 1; + + /* The register is '0' as the first. */ + block_num = msg->osd_info.mode_ctrl.block_num - 1; + + if (msg->src1.format == RGA_FORMAT_RGBA_2BPP) + rgba2bpp_en = 1; + + reg = 0; + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_MODE)) | + (s_RGA2_OSD_CTRL0_SW_OSD_MODE(msg->osd_info.mode_ctrl.mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_VER_MODE)) | + (s_RGA2_OSD_CTRL0_SW_OSD_VER_MODE(msg->osd_info.mode_ctrl.direction_mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_WIDTH_MODE)) | + (s_RGA2_OSD_CTRL0_SW_OSD_WIDTH_MODE(msg->osd_info.mode_ctrl.width_mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_BLK_NUM)) | + (s_RGA2_OSD_CTRL0_SW_OSD_BLK_NUM(block_num))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_FLAGS_INDEX)) | + (s_RGA2_OSD_CTRL0_SW_OSD_FLAGS_INDEX(msg->osd_info.mode_ctrl.flags_index))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_FIX_WIDTH)) | + (s_RGA2_OSD_CTRL0_SW_OSD_FIX_WIDTH(fix_width))); + reg = ((reg & (~m_RGA2_OSD_CTRL0_SW_OSD_2BPP_MODE)) | + (s_RGA2_OSD_CTRL0_SW_OSD_2BPP_MODE(rgba2bpp_en))); + *bRGA_OSD_CTRL0 = reg; + + reg = 0; + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_COLOR_SEL)) | + (s_RGA2_OSD_CTRL1_SW_OSD_COLOR_SEL(msg->osd_info.mode_ctrl.color_mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_FLAG_SEL)) | + (s_RGA2_OSD_CTRL1_SW_OSD_FLAG_SEL(msg->osd_info.mode_ctrl.invert_flags_mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_DEFAULT_COLOR)) | + (s_RGA2_OSD_CTRL1_SW_OSD_DEFAULT_COLOR(msg->osd_info.mode_ctrl.default_color_sel))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_AUTO_INVERST_MODE)) | + (s_RGA2_OSD_CTRL1_SW_OSD_AUTO_INVERST_MODE(msg->osd_info.mode_ctrl.invert_mode))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_THRESH)) | + (s_RGA2_OSD_CTRL1_SW_OSD_THRESH(msg->osd_info.mode_ctrl.invert_thresh))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_INVERT_A_EN)) | + (s_RGA2_OSD_CTRL1_SW_OSD_INVERT_A_EN(msg->osd_info.mode_ctrl.invert_enable))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_INVERT_Y_DIS)) | + (s_RGA2_OSD_CTRL1_SW_OSD_INVERT_Y_DIS(msg->osd_info.mode_ctrl.invert_enable >> 1))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_INVERT_C_DIS)) | + (s_RGA2_OSD_CTRL1_SW_OSD_INVERT_C_DIS(msg->osd_info.mode_ctrl.invert_enable >> 2))); + reg = ((reg & (~m_RGA2_OSD_CTRL1_SW_OSD_UNFIX_INDEX)) | + (s_RGA2_OSD_CTRL1_SW_OSD_UNFIX_INDEX(msg->osd_info.mode_ctrl.unfix_index))); + *bRGA_OSD_CTRL1 = reg; + + *bRGA_OSD_INVERTSION_CAL0 = ((msg->osd_info.cal_factor.crb_max) << 24) | + ((msg->osd_info.cal_factor.crb_min) << 16) | + ((msg->osd_info.cal_factor.yg_max) << 8) | + ((msg->osd_info.cal_factor.yg_min) << 0); + *bRGA_OSD_INVERTSION_CAL1 = ((msg->osd_info.cal_factor.alpha_max) << 8) | + ((msg->osd_info.cal_factor.alpha_min) << 0); + + *bRGA_OSD_LAST_FLAGS0 = (msg->osd_info.last_flags0); + *bRGA_OSD_LAST_FLAGS1 = (msg->osd_info.last_flags1); + + if (msg->osd_info.mode_ctrl.color_mode == 1) { + *bRGA_OSD_COLOR0 = (msg->osd_info.bpp2_info.color0.value & 0xffffff); + *bRGA_OSD_COLOR1 = (msg->osd_info.bpp2_info.color1.value & 0xffffff); + } + + if (rgba2bpp_en) { + *bRGA_OSD_COLOR0 = msg->osd_info.bpp2_info.color0.value; + *bRGA_OSD_COLOR1 = msg->osd_info.bpp2_info.color1.value; + } +} + +static void RGA2_set_reg_color_palette(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_SRC_BASE0, *bRGA_SRC_INFO, *bRGA_SRC_VIR_INFO, + *bRGA_SRC_ACT_INFO, *bRGA_SRC_FG_COLOR, *bRGA_SRC_BG_COLOR; + u32 *p; + short x_off, y_off; + u16 src_stride; + u8 shift; + u32 sw; + u32 byte_num; + u32 reg; + + bRGA_SRC_BASE0 = (u32 *) (base + RGA2_SRC_BASE0_OFFSET); + bRGA_SRC_INFO = (u32 *) (base + RGA2_SRC_INFO_OFFSET); + bRGA_SRC_VIR_INFO = (u32 *) (base + RGA2_SRC_VIR_INFO_OFFSET); + bRGA_SRC_ACT_INFO = (u32 *) (base + RGA2_SRC_ACT_INFO_OFFSET); + bRGA_SRC_FG_COLOR = (u32 *) (base + RGA2_SRC_FG_COLOR_OFFSET); + bRGA_SRC_BG_COLOR = (u32 *) (base + RGA2_SRC_BG_COLOR_OFFSET); + + reg = 0; + + shift = 3 - msg->palette_mode; + + x_off = msg->src.x_offset; + y_off = msg->src.y_offset; + + sw = msg->src.vir_w; + byte_num = sw >> shift; + + src_stride = (byte_num + 3) & (~3); + + p = (u32 *) ((unsigned long)msg->src.yrgb_addr); + + p = p + (x_off >> shift) + y_off * src_stride; + + *bRGA_SRC_BASE0 = (unsigned long)p; + + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SRC_FMT)) | + (s_RGA2_SRC_INFO_SW_SRC_FMT((msg->palette_mode | 0xc)))); + reg = + ((reg & (~m_RGA2_SRC_INFO_SW_SW_CP_ENDIAN)) | + (s_RGA2_SRC_INFO_SW_SW_CP_ENDAIN(msg->endian_mode & 1))); + *bRGA_SRC_VIR_INFO = src_stride >> 2; + *bRGA_SRC_ACT_INFO = + (msg->src.act_w - 1) | ((msg->src.act_h - 1) << 16); + *bRGA_SRC_INFO = reg; + + *bRGA_SRC_FG_COLOR = msg->fg_color; + *bRGA_SRC_BG_COLOR = msg->bg_color; + +} + +static void RGA2_set_reg_color_fill(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_CF_GR_A; + u32 *bRGA_CF_GR_B; + u32 *bRGA_CF_GR_G; + u32 *bRGA_CF_GR_R; + u32 *bRGA_SRC_FG_COLOR; + u32 *bRGA_MASK_ADDR; + u32 *bRGA_PAT_CON; + + u32 mask_stride; + u32 *bRGA_SRC_VIR_INFO; + + bRGA_SRC_FG_COLOR = (u32 *) (base + RGA2_SRC_FG_COLOR_OFFSET); + + bRGA_CF_GR_A = (u32 *) (base + RGA2_CF_GR_A_OFFSET); + bRGA_CF_GR_B = (u32 *) (base + RGA2_CF_GR_B_OFFSET); + bRGA_CF_GR_G = (u32 *) (base + RGA2_CF_GR_G_OFFSET); + bRGA_CF_GR_R = (u32 *) (base + RGA2_CF_GR_R_OFFSET); + + bRGA_MASK_ADDR = (u32 *) (base + RGA2_MASK_BASE_OFFSET); + bRGA_PAT_CON = (u32 *) (base + RGA2_PAT_CON_OFFSET); + + bRGA_SRC_VIR_INFO = (u32 *) (base + RGA2_SRC_VIR_INFO_OFFSET); + + mask_stride = msg->rop_mask_stride; + + if (msg->color_fill_mode == 0) { + /* solid color */ + *bRGA_CF_GR_A = (msg->gr_color.gr_x_a & 0xffff) | + (msg->gr_color.gr_y_a << 16); + *bRGA_CF_GR_B = (msg->gr_color.gr_x_b & 0xffff) | + (msg->gr_color.gr_y_b << 16); + *bRGA_CF_GR_G = (msg->gr_color.gr_x_g & 0xffff) | + (msg->gr_color.gr_y_g << 16); + *bRGA_CF_GR_R = (msg->gr_color.gr_x_r & 0xffff) | + (msg->gr_color.gr_y_r << 16); + + *bRGA_SRC_FG_COLOR = msg->fg_color; + } else { + /* pattern color */ + *bRGA_MASK_ADDR = (u32) msg->pat.yrgb_addr; + *bRGA_PAT_CON = + (msg->pat.act_w - 1) | ((msg->pat.act_h - 1) << 8) + | (msg->pat.x_offset << 16) | (msg->pat.y_offset << 24); + } + *bRGA_SRC_VIR_INFO = mask_stride << 16; +} + +static void RGA2_set_reg_update_palette_table(u8 *base, + struct rga2_req *msg) +{ + u32 *bRGA_MASK_BASE; + u32 *bRGA_FADING_CTRL; + + bRGA_MASK_BASE = (u32 *) (base + RGA2_MASK_BASE_OFFSET); + bRGA_FADING_CTRL = (u32 *) (base + RGA2_FADING_CTRL_OFFSET); + + *bRGA_FADING_CTRL = msg->fading_g_value << 8; + *bRGA_MASK_BASE = (u32) msg->pat.yrgb_addr; +} + +static void RGA2_set_reg_update_patten_buff(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_PAT_MST; + u32 *bRGA_PAT_CON; + u32 *bRGA_PAT_START_POINT; + u32 *bRGA_FADING_CTRL; + u32 reg = 0; + struct rga_img_info_t *pat; + + u32 num, offset; + + pat = &msg->pat; + + num = (pat->act_w * pat->act_h) - 1; + + offset = pat->act_w * pat->y_offset + pat->x_offset; + + bRGA_PAT_START_POINT = (u32 *) (base + RGA2_FADING_CTRL_OFFSET); + bRGA_PAT_MST = (u32 *) (base + RGA2_MASK_BASE_OFFSET); + bRGA_PAT_CON = (u32 *) (base + RGA2_PAT_CON_OFFSET); + bRGA_FADING_CTRL = (u32 *) (base + RGA2_FADING_CTRL_OFFSET); + + *bRGA_PAT_MST = (u32) msg->pat.yrgb_addr; + *bRGA_PAT_START_POINT = (pat->act_w * pat->y_offset) + pat->x_offset; + + reg = (pat->act_w - 1) | ((pat->act_h - 1) << 8) | + (pat->x_offset << 16) | (pat->y_offset << 24); + *bRGA_PAT_CON = reg; + + *bRGA_FADING_CTRL = (num << 8) | offset; +} + +static void RGA2_set_pat_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_PAT_CON; + u32 *bRGA_FADING_CTRL; + u32 reg = 0; + struct rga_img_info_t *pat; + + u32 num, offset; + + pat = &msg->pat; + + num = ((pat->act_w * pat->act_h) - 1) & 0xff; + + offset = (pat->act_w * pat->y_offset) + pat->x_offset; + + bRGA_PAT_CON = (u32 *) (base + RGA2_PAT_CON_OFFSET); + bRGA_FADING_CTRL = (u32 *) (base + RGA2_FADING_CTRL_OFFSET); + + reg = (pat->act_w - 1) | ((pat->act_h - 1) << 8) | + (pat->x_offset << 16) | (pat->y_offset << 24); + *bRGA_PAT_CON = reg; + *bRGA_FADING_CTRL = (num << 8) | offset; +} + +static void RGA2_set_mmu_reg_info(u8 *base, struct rga2_req *msg) +{ + u32 *bRGA_MMU_CTRL1; + u32 *bRGA_MMU_SRC_BASE; + u32 *bRGA_MMU_SRC1_BASE; + u32 *bRGA_MMU_DST_BASE; + u32 *bRGA_MMU_ELS_BASE; + + u32 reg; + + bRGA_MMU_CTRL1 = (u32 *) (base + RGA2_MMU_CTRL1_OFFSET); + bRGA_MMU_SRC_BASE = (u32 *) (base + RGA2_MMU_SRC_BASE_OFFSET); + bRGA_MMU_SRC1_BASE = (u32 *) (base + RGA2_MMU_SRC1_BASE_OFFSET); + bRGA_MMU_DST_BASE = (u32 *) (base + RGA2_MMU_DST_BASE_OFFSET); + bRGA_MMU_ELS_BASE = (u32 *) (base + RGA2_MMU_ELS_BASE_OFFSET); + + reg = (msg->mmu_info.src0_mmu_flag & 0xf) | + ((msg->mmu_info.src1_mmu_flag & 0xf) << 4) | + ((msg->mmu_info.dst_mmu_flag & 0xf) << 8) | + ((msg->mmu_info.els_mmu_flag & 0x3) << 12); + + *bRGA_MMU_CTRL1 = reg; + *bRGA_MMU_SRC_BASE = (u32) (msg->mmu_info.src0_base_addr) >> 4; + *bRGA_MMU_SRC1_BASE = (u32) (msg->mmu_info.src1_base_addr) >> 4; + *bRGA_MMU_DST_BASE = (u32) (msg->mmu_info.dst_base_addr) >> 4; + *bRGA_MMU_ELS_BASE = (u32) (msg->mmu_info.els_base_addr) >> 4; +} + +static int rga2_gen_reg_info(u8 *base, struct rga2_req *msg) +{ + u8 dst_nn_quantize_en = 0; + + RGA2_set_mode_ctrl(base, msg); + + RGA2_set_pat_info(base, msg); + + switch (msg->render_mode) { + case BITBLT_MODE: + RGA2_set_reg_src_info(base, msg); + RGA2_set_reg_dst_info(base, msg); + dst_nn_quantize_en = (msg->alpha_rop_flag >> 8) & 0x1; + if (dst_nn_quantize_en != 1) { + if ((msg->dst.format != + RGA_FORMAT_Y4)) { + RGA2_set_reg_alpha_info(base, msg); + RGA2_set_reg_rop_info(base, msg); + } + } + if (msg->mosaic_info.enable) + RGA_set_reg_mosaic(base, msg); + if (msg->osd_info.enable) + RGA2_set_reg_osd(base, msg); + + break; + case COLOR_FILL_MODE: + RGA2_set_reg_color_fill(base, msg); + RGA2_set_reg_dst_info(base, msg); + RGA2_set_reg_alpha_info(base, msg); + break; + case COLOR_PALETTE_MODE: + RGA2_set_reg_color_palette(base, msg); + RGA2_set_reg_dst_info(base, msg); + break; + case UPDATE_PALETTE_TABLE_MODE: + RGA2_set_reg_update_palette_table(base, msg); + break; + case UPDATE_PATTEN_BUF_MODE: + RGA2_set_reg_update_patten_buff(base, msg); + break; + default: + pr_err("ERROR msg render mode %d\n", msg->render_mode); + break; + } + + RGA2_set_mmu_reg_info(base, msg); + + return 0; +} + +static void rga_cmd_to_rga2_cmd(struct rga_scheduler_t *scheduler, + struct rga_req *req_rga, struct rga2_req *req) +{ + if (req_rga->render_mode == 6) + req->render_mode = UPDATE_PALETTE_TABLE_MODE; + else if (req_rga->render_mode == 7) + req->render_mode = UPDATE_PATTEN_BUF_MODE; + else if (req_rga->render_mode == 5) + req->render_mode = BITBLT_MODE; + else + req->render_mode = req_rga->render_mode; + + memcpy(&req->src, &req_rga->src, sizeof(req_rga->src)); + memcpy(&req->dst, &req_rga->dst, sizeof(req_rga->dst)); + /* The application will only import pat or src1. */ + if (req->render_mode == UPDATE_PALETTE_TABLE_MODE) + memcpy(&req->pat, &req_rga->pat, sizeof(req_rga->pat)); + else + memcpy(&req->src1, &req_rga->pat, sizeof(req_rga->pat)); + + req->src.format = req_rga->src.format; + req->dst.format = req_rga->dst.format; + req->src1.format = req_rga->pat.format; + + switch (req_rga->rotate_mode & 0x0F) { + case 1: + if (req_rga->sina == 0 && req_rga->cosa == 65536) { + /* rotate 0 */ + req->rotate_mode = 0; + } else if (req_rga->sina == 65536 && req_rga->cosa == 0) { + /* rotate 90 */ + req->rotate_mode = 1; + req->dst.x_offset = req_rga->dst.x_offset; + req->dst.act_w = req_rga->dst.act_h; + req->dst.act_h = req_rga->dst.act_w; + } else if (req_rga->sina == 0 && req_rga->cosa == -65536) { + /* rotate 180 */ + req->rotate_mode = 2; + req->dst.x_offset = req_rga->dst.x_offset; + req->dst.y_offset = req_rga->dst.y_offset; + } else if (req_rga->sina == -65536 && req_rga->cosa == 0) { + /* totate 270 */ + req->rotate_mode = 3; + req->dst.y_offset = req_rga->dst.y_offset; + req->dst.act_w = req_rga->dst.act_h; + req->dst.act_h = req_rga->dst.act_w; + } + break; + case 2: + //x_mirror + req->rotate_mode |= (1 << 4); + break; + case 3: + //y_mirror + req->rotate_mode |= (2 << 4); + break; + case 4: + //x_mirror+y_mirror + req->rotate_mode |= (3 << 4); + break; + default: + req->rotate_mode = 0; + break; + } + + switch ((req_rga->rotate_mode & 0xF0) >> 4) { + case 2: + //x_mirror + req->rotate_mode |= (1 << 4); + break; + case 3: + //y_mirror + req->rotate_mode |= (2 << 4); + break; + case 4: + //x_mirror+y_mirror + req->rotate_mode |= (3 << 4); + break; + } + + req->LUT_addr = req_rga->LUT_addr; + req->rop_mask_addr = req_rga->rop_mask_addr; + + req->bitblt_mode = req_rga->bsfilter_flag; + + req->src_a_global_val = req_rga->alpha_global_value; + req->dst_a_global_val = req_rga->alpha_global_value; + req->rop_code = req_rga->rop_code; + req->rop_mode = req_rga->alpha_rop_mode; + + req->color_fill_mode = req_rga->color_fill_mode; + req->alpha_zero_key = req_rga->alpha_rop_mode >> 4; + req->src_trans_mode = req_rga->src_trans_mode; + req->color_key_min = req_rga->color_key_min; + req->color_key_max = req_rga->color_key_max; + + req->fg_color = req_rga->fg_color; + req->bg_color = req_rga->bg_color; + memcpy(&req->gr_color, &req_rga->gr_color, sizeof(req_rga->gr_color)); + + req->palette_mode = req_rga->palette_mode; + req->yuv2rgb_mode = req_rga->yuv2rgb_mode; + if (req_rga->full_csc.flag & 0x1) + req->full_csc_en = 1; + req->endian_mode = req_rga->endian_mode; + req->rgb2yuv_mode = 0; + + req->fading_alpha_value = 0; + req->fading_r_value = req_rga->fading.r; + req->fading_g_value = req_rga->fading.g; + req->fading_b_value = req_rga->fading.b; + + /* alpha mode set */ + req->alpha_rop_flag = 0; + /* alpha_rop_enable */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag & 1))); + /* rop_enable */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 1) & 1) << 1); + /* fading_enable */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 2) & 1) << 2); + /* alpha_cal_mode_sel */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 4) & 1) << 3); + /* dst_dither_down */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 5) & 1) << 6); + /* gradient fill mode sel */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 6) & 1) << 7); + /* RGA_NN_QUANTIZE */ + req->alpha_rop_flag |= (((req_rga->alpha_rop_flag >> 8) & 1) << 8); + req->dither_mode = req_rga->dither_mode; + + /* RGA2 1106 add */ + memcpy(&req->mosaic_info, &req_rga->mosaic_info, sizeof(req_rga->mosaic_info)); + + if ((scheduler->data->feature & RGA_YIN_YOUT) && + rga_is_only_y_format(req->src.format) && + rga_is_only_y_format(req->dst.format)) + req->yin_yout_en = true; + + req->uvhds_mode = req_rga->uvhds_mode; + req->uvvds_mode = req_rga->uvvds_mode; + + memcpy(&req->osd_info, &req_rga->osd_info, sizeof(req_rga->osd_info)); + + if (((req_rga->alpha_rop_flag) & 1)) { + if ((req_rga->alpha_rop_flag >> 3) & 1) { + req->alpha_config.enable = true; + + if ((req_rga->alpha_rop_flag >> 9) & 1) { + req->alpha_config.fg_pre_multiplied = false; + req->alpha_config.bg_pre_multiplied = false; + } else if (req->osd_info.enable) { + req->alpha_config.fg_pre_multiplied = true; + /* set dst(osd_block) real color mode */ + req->alpha_config.bg_pre_multiplied = false; + } else { + req->alpha_config.fg_pre_multiplied = true; + req->alpha_config.bg_pre_multiplied = true; + } + + req->alpha_config.fg_pixel_alpha_en = rga_is_alpha_format(req->src.format); + if (req->bitblt_mode) + req->alpha_config.bg_pixel_alpha_en = + rga_is_alpha_format(req->src1.format); + else + req->alpha_config.bg_pixel_alpha_en = + rga_is_alpha_format(req->dst.format); + + if (req_rga->feature.global_alpha_en) { + if (req_rga->fg_global_alpha < 0xff) { + req->alpha_config.fg_global_alpha_en = true; + req->alpha_config.fg_global_alpha_value = + req_rga->fg_global_alpha; + } else if (!req->alpha_config.fg_pixel_alpha_en) { + req->alpha_config.fg_global_alpha_en = true; + req->alpha_config.fg_global_alpha_value = 0xff; + } + + if (req_rga->bg_global_alpha < 0xff) { + req->alpha_config.bg_global_alpha_en = true; + req->alpha_config.bg_global_alpha_value = + req_rga->bg_global_alpha; + } else if (!req->alpha_config.bg_pixel_alpha_en) { + req->alpha_config.bg_global_alpha_en = true; + req->alpha_config.bg_global_alpha_value = 0xff; + } + } else { + req->alpha_config.bg_global_alpha_value = 0xff; + req->alpha_config.bg_global_alpha_value = 0xff; + } + + req->alpha_config.mode = req_rga->PD_mode; + } + } + + if (req_rga->mmu_info.mmu_en && (req_rga->mmu_info.mmu_flag & 1) == 1) { + req->mmu_info.src0_mmu_flag = 1; + req->mmu_info.dst_mmu_flag = 1; + + if (req_rga->mmu_info.mmu_flag >> 31) { + req->mmu_info.src0_mmu_flag = + ((req_rga->mmu_info.mmu_flag >> 8) & 1); + req->mmu_info.src1_mmu_flag = + ((req_rga->mmu_info.mmu_flag >> 9) & 1); + req->mmu_info.dst_mmu_flag = + ((req_rga->mmu_info.mmu_flag >> 10) & 1); + req->mmu_info.els_mmu_flag = + ((req_rga->mmu_info.mmu_flag >> 11) & 1); + } else { + if (req_rga->src.yrgb_addr >= 0xa0000000) { + req->mmu_info.src0_mmu_flag = 0; + req->src.yrgb_addr = + req_rga->src.yrgb_addr - 0x60000000; + req->src.uv_addr = + req_rga->src.uv_addr - 0x60000000; + req->src.v_addr = + req_rga->src.v_addr - 0x60000000; + } + + if (req_rga->dst.yrgb_addr >= 0xa0000000) { + req->mmu_info.dst_mmu_flag = 0; + req->dst.yrgb_addr = + req_rga->dst.yrgb_addr - 0x60000000; + } + + if (req_rga->pat.yrgb_addr >= 0xa0000000) { + req->mmu_info.src1_mmu_flag = 0; + req->src1.yrgb_addr = + req_rga->pat.yrgb_addr - 0x60000000; + } + } + } +} + +static void rga2_soft_reset(struct rga_scheduler_t *scheduler) +{ + u32 i; + u32 reg; + u32 iommu_dte_addr = 0; + + if (scheduler->data->mmu == RGA_IOMMU) + iommu_dte_addr = rga_read(RGA_IOMMU_DTE_ADDR, scheduler); + + rga_write(m_RGA2_SYS_CTRL_ACLK_SRESET_P | m_RGA2_SYS_CTRL_CCLK_SRESET_P | + m_RGA2_SYS_CTRL_RST_PROTECT_P, + RGA2_SYS_CTRL, scheduler); + + for (i = 0; i < RGA_RESET_TIMEOUT; i++) { + /* RGA_SYS_CTRL */ + reg = rga_read(RGA2_SYS_CTRL, scheduler) & 1; + + if (reg == 0) + break; + + udelay(1); + } + + if (scheduler->data->mmu == RGA_IOMMU) { + rga_write(iommu_dte_addr, RGA_IOMMU_DTE_ADDR, scheduler); + /* enable iommu */ + rga_write(RGA_IOMMU_CMD_ENABLE_PAGING, RGA_IOMMU_COMMAND, scheduler); + } + + if (i == RGA_RESET_TIMEOUT) + pr_err("RAG2 core[%d] soft reset timeout.\n", scheduler->core); + else + pr_info("RGA2 core[%d] soft reset complete.\n", scheduler->core); + +} + +static int rga2_check_param(const struct rga_hw_data *data, const struct rga2_req *req) +{ + if (!((req->render_mode == COLOR_FILL_MODE))) { + if (unlikely(rga_hw_out_of_range(&data->input_range, + req->src.act_w, req->src.act_h))) { + pr_err("invalid src resolution act_w = %d, act_h = %d\n", + req->src.act_w, req->src.act_h); + return -EINVAL; + } + + if (unlikely(req->src.vir_w * rga_get_pixel_stride_from_format(req->src.format) > + data->max_byte_stride * 8)) { + pr_err("invalid src stride, stride = %d, max_byte_stride = %d\n", + req->src.vir_w, data->max_byte_stride); + return -EINVAL; + } + + if (unlikely(req->src.vir_w < req->src.act_w)) { + pr_err("invalid src_vir_w act_w = %d, vir_w = %d\n", + req->src.act_w, req->src.vir_w); + return -EINVAL; + } + } + + if (unlikely(rga_hw_out_of_range(&data->output_range, req->dst.act_w, req->dst.act_h))) { + pr_err("invalid dst resolution act_w = %d, act_h = %d\n", + req->dst.act_w, req->dst.act_h); + return -EINVAL; + } + + if (unlikely(req->dst.vir_w * rga_get_pixel_stride_from_format(req->dst.format) > + data->max_byte_stride * 8)) { + pr_err("invalid dst stride, stride = %d, max_byte_stride = %d\n", + req->dst.vir_w, data->max_byte_stride); + return -EINVAL; + } + + if (unlikely(req->dst.vir_w < req->dst.act_w)) { + if (req->rotate_mode != 1) { + pr_err("invalid dst_vir_w act_h = %d, vir_h = %d\n", + req->dst.act_w, req->dst.vir_w); + return -EINVAL; + } + } + + return 0; +} + +static int rga2_align_check(struct rga2_req *req) +{ + if (rga_is_yuv10bit_format(req->src.format)) + if ((req->src.vir_w % 16) || (req->src.x_offset % 2) || + (req->src.act_w % 2) || (req->src.y_offset % 2) || + (req->src.act_h % 2) || (req->src.vir_h % 2)) + pr_info("err src wstride, 10bit yuv\n"); + if (rga_is_yuv10bit_format(req->dst.format)) + if ((req->dst.vir_w % 16) || (req->dst.x_offset % 2) || + (req->dst.act_w % 2) || (req->dst.y_offset % 2) || + (req->dst.act_h % 2) || (req->dst.vir_h % 2)) + pr_info("err dst wstride, 10bit yuv\n"); + if (rga_is_yuv8bit_format(req->src.format)) + if ((req->src.vir_w % 4) || (req->src.x_offset % 2) || + (req->src.act_w % 2) || (req->src.y_offset % 2) || + (req->src.act_h % 2) || (req->src.vir_h % 2)) + pr_info("err src wstride, 8bit yuv\n"); + if (rga_is_yuv8bit_format(req->dst.format)) + if ((req->dst.vir_w % 4) || (req->dst.x_offset % 2) || + (req->dst.act_w % 2) || (req->dst.y_offset % 2) || + (req->dst.act_h % 2) || (req->dst.vir_h % 2)) + pr_info("err dst wstride, 8bit yuv\n"); + + return 0; +} + +static void print_debug_info(struct rga2_req *req) +{ + pr_info("render_mode:%s,bitblit_mode=%d,rotate_mode:%s\n", + rga_get_render_mode_str(req->render_mode), req->bitblt_mode, + rga_get_rotate_mode_str(req->rotate_mode)); + + pr_info("src: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->src.yrgb_addr, + (unsigned long)req->src.uv_addr, + (unsigned long)req->src.v_addr, + req->src.act_w, req->src.act_h, + req->src.vir_w, req->src.vir_h); + pr_info("src: xoff=%d yoff=%d format=%s\n", + req->src.x_offset, req->src.y_offset, + rga_get_format_name(req->src.format)); + + if (req->src1.yrgb_addr != 0 || req->src1.uv_addr != 0 + || req->src1.v_addr != 0) { + pr_info("src1: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->src1.yrgb_addr, + (unsigned long)req->src1.uv_addr, + (unsigned long)req->src1.v_addr, + req->src1.act_w, req->src1.act_h, + req->src1.vir_w, req->src1.vir_h); + pr_info("src1: xoff=%d yoff=%d format=%s\n", + req->src1.x_offset, req->src1.y_offset, + rga_get_format_name(req->src1.format)); + } + + pr_info("dst: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->dst.yrgb_addr, + (unsigned long)req->dst.uv_addr, + (unsigned long)req->dst.v_addr, + req->dst.act_w, req->dst.act_h, + req->dst.vir_w, req->dst.vir_h); + pr_info("dst: xoff=%d yoff=%d format=%s\n", + req->dst.x_offset, req->dst.y_offset, + rga_get_format_name(req->dst.format)); + + pr_info("mmu: src=%.2x src1=%.2x dst=%.2x els=%.2x\n", + req->mmu_info.src0_mmu_flag, req->mmu_info.src1_mmu_flag, + req->mmu_info.dst_mmu_flag, req->mmu_info.els_mmu_flag); + pr_info("alpha: flag %x mode=%s\n", + req->alpha_rop_flag, rga_get_blend_mode_str(req->alpha_config.mode)); + pr_info("alpha: pre_multi=[%d,%d] pixl=[%d,%d] glb=[%d,%d]\n", + req->alpha_config.fg_pre_multiplied, req->alpha_config.bg_pre_multiplied, + req->alpha_config.fg_pixel_alpha_en, req->alpha_config.bg_pixel_alpha_en, + req->alpha_config.fg_global_alpha_en, req->alpha_config.bg_global_alpha_en); + pr_info("alpha: fg_global_alpha=%x bg_global_alpha=%x\n", + req->alpha_config.fg_global_alpha_value, req->alpha_config.bg_global_alpha_value); + pr_info("yuv2rgb mode is %x\n", req->yuv2rgb_mode); +} + +static int rga2_init_reg(struct rga_job *job) +{ + struct rga2_req req; + int ret = 0; + struct rga_scheduler_t *scheduler = NULL; + ktime_t timestamp = ktime_get(); + + scheduler = job->scheduler; + if (unlikely(scheduler == NULL)) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + + memset(&req, 0x0, sizeof(req)); + + rga_cmd_to_rga2_cmd(scheduler, &job->rga_command_base, &req); + if (req.full_csc_en) { + memcpy(&job->full_csc, &job->rga_command_base.full_csc, sizeof(job->full_csc)); + if (job->rga_command_base.feature.full_csc_clip_en) { + memcpy(&job->full_csc_clip, &job->rga_command_base.full_csc_clip, + sizeof(job->full_csc_clip)); + } else { + job->full_csc_clip.y.max = 0xff; + job->full_csc_clip.y.min = 0x0; + job->full_csc_clip.uv.max = 0xff; + job->full_csc_clip.uv.min = 0x0; + } + + } else { + job->full_csc_clip.y.max = 0xff; + job->full_csc_clip.y.min = 0x0; + job->full_csc_clip.uv.max = 0xff; + job->full_csc_clip.uv.min = 0x0; + } + memcpy(&job->pre_intr_info, &job->rga_command_base.pre_intr_info, + sizeof(job->pre_intr_info)); + + /* check value if legal */ + ret = rga2_check_param(scheduler->data, &req); + if (ret == -EINVAL) { + pr_err("req argument is inval\n"); + return ret; + } + + rga2_align_check(&req); + + /* for debug */ + if (DEBUGGER_EN(MSG)) + print_debug_info(&req); + + /* RGA2 mmu set */ + if ((req.mmu_info.src0_mmu_flag & 1) || (req.mmu_info.src1_mmu_flag & 1) || + (req.mmu_info.dst_mmu_flag & 1) || (req.mmu_info.els_mmu_flag & 1)) { + if (scheduler->data->mmu != RGA_MMU) { + pr_err("core[%d] has no MMU, please use physically contiguous memory.\n", + scheduler->core); + pr_err("mmu_flag[src, src1, dst, els] = [0x%x, 0x%x, 0x%x, 0x%x]\n", + req.mmu_info.src0_mmu_flag, req.mmu_info.src1_mmu_flag, + req.mmu_info.dst_mmu_flag, req.mmu_info.els_mmu_flag); + return -EINVAL; + } + + ret = rga_set_mmu_base(job, &req); + if (ret < 0) { + pr_err("%s, [%d] set mmu info error\n", __func__, + __LINE__); + return -EFAULT; + } + } + + /* In slave mode, the current frame completion interrupt must be enabled. */ + if (scheduler->data->mmu == RGA_IOMMU) + req.CMD_fin_int_enable = 1; + + if (rga2_gen_reg_info((uint8_t *)job->cmd_reg, &req) == -1) { + pr_err("gen reg info error\n"); + return -EINVAL; + } + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], generate register cost time %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + + return ret; +} + +static void rga2_dump_read_back_sys_reg(struct rga_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + uint32_t sys_reg[24] = {0}; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + for (i = 0; i < 24; i++) + sys_reg[i] = rga_read(RGA2_SYS_REG_BASE + i * 4, scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + pr_info("SYS_READ_BACK_REG\n"); + for (i = 0; i < 6; i++) + pr_info("0x%04x : %.8x %.8x %.8x %.8x\n", + RGA2_SYS_REG_BASE + i * 0x10, + sys_reg[0 + i * 4], sys_reg[1 + i * 4], + sys_reg[2 + i * 4], sys_reg[3 + i * 4]); +} + +static void rga2_dump_read_back_csc_reg(struct rga_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + uint32_t csc_reg[12] = {0}; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + for (i = 0; i < 12; i++) + csc_reg[i] = rga_read(RGA2_CSC_REG_BASE + i * 4, scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + pr_info("CSC_READ_BACK_REG\n"); + for (i = 0; i < 3; i++) + pr_info("0x%04x : %.8x %.8x %.8x %.8x\n", + RGA2_CSC_REG_BASE + i * 0x10, + csc_reg[0 + i * 4], csc_reg[1 + i * 4], + csc_reg[2 + i * 4], csc_reg[3 + i * 4]); +} + +static void rga2_dump_read_back_cmd_reg(struct rga_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + uint32_t cmd_reg[32] = {0}; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + for (i = 0; i < 32; i++) + cmd_reg[i] = rga_read(RGA2_CMD_REG_BASE + i * 4, scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + pr_info("CMD_READ_BACK_REG\n"); + for (i = 0; i < 8; i++) + pr_info("0x%04x : %.8x %.8x %.8x %.8x\n", + RGA2_CMD_REG_BASE + i * 0x10, + cmd_reg[0 + i * 4], cmd_reg[1 + i * 4], + cmd_reg[2 + i * 4], cmd_reg[3 + i * 4]); +} + +static void rga2_dump_read_back_reg(struct rga_scheduler_t *scheduler) +{ + rga2_dump_read_back_sys_reg(scheduler); + rga2_dump_read_back_csc_reg(scheduler); + rga2_dump_read_back_cmd_reg(scheduler); +} + +static void rga2_set_pre_intr_reg(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + uint32_t reg; + + if (job->pre_intr_info.read_intr_en) { + reg = s_RGA2_READ_LINE_SW_INTR_LINE_RD_TH(job->pre_intr_info.read_threshold); + rga_write(reg, RGA2_READ_LINE_CNT, scheduler); + } + + if (job->pre_intr_info.write_intr_en) { + reg = s_RGA2_WRITE_LINE_SW_INTR_LINE_WR_START(job->pre_intr_info.write_start); + reg = ((reg & (~m_RGA2_WRITE_LINE_SW_INTR_LINE_WR_STEP)) | + (s_RGA2_WRITE_LINE_SW_INTR_LINE_WR_STEP(job->pre_intr_info.write_step))); + rga_write(reg, RGA2_WRITE_LINE_CNT, scheduler); + } + + reg = rga_read(RGA2_SYS_CTRL, scheduler); + reg = ((reg & (~m_RGA2_SYS_CTRL_HOLD_MODE_EN)) | + (s_RGA2_SYS_CTRL_HOLD_MODE_EN(job->pre_intr_info.read_hold_en))); + rga_write(reg, RGA2_SYS_CTRL, scheduler); + + reg = rga_read(RGA2_INT, scheduler); + reg = (reg | s_RGA2_INT_LINE_RD_CLEAR(0x1) | s_RGA2_INT_LINE_WR_CLEAR(0x1)); + reg = ((reg & (~m_RGA2_INT_LINE_RD_EN)) | + (s_RGA2_INT_LINE_RD_EN(job->pre_intr_info.read_intr_en))); + reg = ((reg & (~m_RGA2_INT_LINE_WR_EN)) | + (s_RGA2_INT_LINE_WR_EN(job->pre_intr_info.write_intr_en))); + rga_write(reg, RGA2_INT, scheduler); +} + +static void rga2_set_reg_full_csc(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + /* full csc coefficient */ + /* Y coefficient */ + rga_write(job->full_csc.coe_y.r_v | + (job->full_csc_clip.y.max << 16) | (job->full_csc_clip.y.min << 24), + RGA2_DST_CSC_00, scheduler); + rga_write(job->full_csc.coe_y.g_y | + (job->full_csc_clip.uv.max << 16) | (job->full_csc_clip.uv.min << 24), + RGA2_DST_CSC_01, scheduler); + rga_write(job->full_csc.coe_y.b_u, RGA2_DST_CSC_02, scheduler); + rga_write(job->full_csc.coe_y.off, RGA2_DST_CSC_OFF0, scheduler); + + /* U coefficient */ + rga_write(job->full_csc.coe_u.r_v, RGA2_DST_CSC_10, scheduler); + rga_write(job->full_csc.coe_u.g_y, RGA2_DST_CSC_11, scheduler); + rga_write(job->full_csc.coe_u.b_u, RGA2_DST_CSC_12, scheduler); + rga_write(job->full_csc.coe_u.off, RGA2_DST_CSC_OFF1, scheduler); + + /* V coefficient */ + rga_write(job->full_csc.coe_v.r_v, RGA2_DST_CSC_20, scheduler); + rga_write(job->full_csc.coe_v.g_y, RGA2_DST_CSC_21, scheduler); + rga_write(job->full_csc.coe_v.b_u, RGA2_DST_CSC_22, scheduler); + rga_write(job->full_csc.coe_v.off, RGA2_DST_CSC_OFF2, scheduler); +} + +static int rga2_set_reg(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + int i; + bool master_mode_en; + uint32_t sys_ctrl; + ktime_t now = ktime_get(); + + /* + * Currently there is no iova allocated for storing cmd for the IOMMU device, + * so the iommu device needs to use the slave mode. + */ + if (scheduler->data->mmu != RGA_IOMMU) + master_mode_en = true; + else + master_mode_en = false; + + if (job->pre_intr_info.enable) + rga2_set_pre_intr_reg(job, scheduler); + + if (job->full_csc.flag) + rga2_set_reg_full_csc(job, scheduler); + + if (DEBUGGER_EN(REG)) { + uint32_t *p; + + rga2_dump_read_back_sys_reg(scheduler); + rga2_dump_read_back_csc_reg(scheduler); + + p = job->cmd_reg; + pr_info("CMD_REG\n"); + for (i = 0; i < 8; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", i, + p[0 + i * 4], p[1 + i * 4], + p[2 + i * 4], p[3 + i * 4]); + } + + /* All CMD finish int */ + rga_write(rga_read(RGA2_INT, scheduler) | + m_RGA2_INT_ERROR_ENABLE_MASK | m_RGA2_INT_ALL_CMD_DONE_INT_EN, + RGA2_INT, scheduler); + + /* sys_reg init */ + sys_ctrl = m_RGA2_SYS_CTRL_AUTO_CKG | m_RGA2_SYS_CTRL_AUTO_RST | + m_RGA2_SYS_CTRL_RST_PROTECT_P | m_RGA2_SYS_CTRL_DST_WR_OPT_DIS | + m_RGA2_SYS_CTRL_SRC0YUV420SP_RD_OPT_DIS; + + if (master_mode_en) { + /* master mode */ + sys_ctrl |= s_RGA2_SYS_CTRL_CMD_MODE(1); + + /* cmd buffer flush cache to ddr */ + rga_dma_sync_flush_range(&job->cmd_reg[0], &job->cmd_reg[32], scheduler); + + /* set cmd_addr */ + rga_write(virt_to_phys(job->cmd_reg), RGA2_CMD_BASE, scheduler); + rga_write(sys_ctrl, RGA2_SYS_CTRL, scheduler); + rga_write(m_RGA2_CMD_CTRL_CMD_LINE_ST_P, RGA2_CMD_CTRL, scheduler); + } else { + /* slave mode */ + sys_ctrl |= s_RGA2_SYS_CTRL_CMD_MODE(0) | m_RGA2_SYS_CTRL_CMD_OP_ST_P; + + /* set cmd_reg */ + for (i = 0; i <= 32; i++) + rga_write(job->cmd_reg[i], 0x100 + i * 4, scheduler); + + rga_write(sys_ctrl, RGA2_SYS_CTRL, scheduler); + } + + if (DEBUGGER_EN(REG)) + pr_info("sys_ctrl = %x, int = %x\n", + rga_read(RGA2_SYS_CTRL, scheduler), + rga_read(RGA2_INT, scheduler)); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], set register cost time %lld us\n", + job->request_id, ktime_us_delta(now, job->timestamp)); + + job->hw_running_time = now; + job->hw_recoder_time = now; + + if (DEBUGGER_EN(REG)) + rga2_dump_read_back_reg(scheduler); + + return 0; +} + +static int rga2_get_version(struct rga_scheduler_t *scheduler) +{ + u32 major_version, minor_version, svn_version; + u32 reg_version; + + if (!scheduler) { + pr_err("scheduler is null\n"); + return -EINVAL; + } + + reg_version = rga_read(RGA2_VERSION_NUM, scheduler); + + major_version = (reg_version & RGA2_MAJOR_VERSION_MASK) >> 24; + minor_version = (reg_version & RGA2_MINOR_VERSION_MASK) >> 20; + svn_version = (reg_version & RGA2_SVN_VERSION_MASK); + + /* + * some old rga ip has no rga version register, so force set to 2.00 + */ + if (!major_version && !minor_version) + major_version = 2; + + snprintf(scheduler->version.str, 10, "%x.%01x.%05x", major_version, + minor_version, svn_version); + + scheduler->version.major = major_version; + scheduler->version.minor = minor_version; + scheduler->version.revision = svn_version; + + return 0; +} + +static int rga2_read_back_reg(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + if (job->rga_command_base.osd_info.enable) { + job->rga_command_base.osd_info.cur_flags0 = rga_read(RGA2_OSD_CUR_FLAGS0, + scheduler); + job->rga_command_base.osd_info.cur_flags1 = rga_read(RGA2_OSD_CUR_FLAGS1, + scheduler); + } + + return 0; +} + +static int rga2_irq(struct rga_scheduler_t *scheduler) +{ + struct rga_job *job = scheduler->running_job; + + /* The hardware interrupt top-half don't need to lock the scheduler. */ + if (job == NULL) + return IRQ_HANDLED; + + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) + return IRQ_WAKE_THREAD; + + job->intr_status = rga_read(RGA2_INT, scheduler); + job->hw_status = rga_read(RGA2_STATUS2, scheduler); + job->cmd_status = rga_read(RGA2_STATUS1, scheduler); + + if (DEBUGGER_EN(INT_FLAG)) + pr_info("irq handler, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + job->intr_status, job->hw_status, job->cmd_status); + + if (job->intr_status & + (m_RGA2_INT_CUR_CMD_DONE_INT_FLAG | m_RGA2_INT_ALL_CMD_DONE_INT_FLAG)) { + set_bit(RGA_JOB_STATE_FINISH, &job->state); + } else if (job->intr_status & m_RGA2_INT_ERROR_FLAG_MASK) { + set_bit(RGA_JOB_STATE_INTR_ERR, &job->state); + + pr_err("irq handler err! INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + job->intr_status, job->hw_status, job->cmd_status); + scheduler->ops->soft_reset(scheduler); + } + + /*clear INTR */ + rga_write(rga_read(RGA2_INT, scheduler) | + (m_RGA2_INT_ERROR_CLEAR_MASK | + m_RGA2_INT_ALL_CMD_DONE_INT_CLEAR | m_RGA2_INT_NOW_CMD_DONE_INT_CLEAR | + m_RGA2_INT_LINE_RD_CLEAR | m_RGA2_INT_LINE_WR_CLEAR), + RGA2_INT, scheduler); + + return IRQ_WAKE_THREAD; +} + +static int rga2_isr_thread(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + if (DEBUGGER_EN(INT_FLAG)) + pr_info("isr thread, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + rga_read(RGA2_INT, scheduler), + rga_read(RGA2_STATUS2, scheduler), + rga_read(RGA2_STATUS1, scheduler)); + + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) { + if (job->hw_status & m_RGA2_STATUS2_RPP_ERROR) + pr_err("RGA current status: rpp error!\n"); + if (job->hw_status & m_RGA2_STATUS2_BUS_ERROR) + pr_err("RGA current status: bus error!\n"); + + if (job->intr_status & m_RGA2_INT_ERROR_INT_FLAG) { + pr_err("RGA bus error intr, please check your configuration and buffer.\n"); + job->ret = -EFAULT; + } else if (job->intr_status & m_RGA2_INT_MMU_INT_FLAG) { + pr_err("mmu failed, please check size of the buffer or whether the buffer has been freed.\n"); + job->ret = -EACCES; + } + + if (job->ret == 0) { + pr_err("rga intr error[0x%x]!\n", job->intr_status); + job->ret = -EFAULT; + } + } + + return IRQ_HANDLED; +} + +const struct rga_backend_ops rga2_ops = { + .get_version = rga2_get_version, + .set_reg = rga2_set_reg, + .init_reg = rga2_init_reg, + .soft_reset = rga2_soft_reset, + .read_back_reg = rga2_read_back_reg, + .irq = rga2_irq, + .isr_thread = rga2_isr_thread, +}; diff --git a/drivers/video/rockchip/rga3/rga3_reg_info.c b/drivers/video/rockchip/rga3/rga3_reg_info.c new file mode 100644 index 000000000..2b61833aa --- /dev/null +++ b/drivers/video/rockchip/rga3/rga3_reg_info.c @@ -0,0 +1,2191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga3_reg: " fmt + +#include "rga3_reg_info.h" +#include "rga_dma_buf.h" +#include "rga_iommu.h" +#include "rga_common.h" +#include "rga_debugger.h" +#include "rga_hw_config.h" + +#define FACTOR_MAX ((int)(2 << 15)) + +static void RGA3_set_reg_win0_info(u8 *base, struct rga3_req *msg) +{ + u32 *bRGA3_WIN0_RD_CTRL; + u32 *bRGA3_WIN0_Y_BASE, *bRGA3_WIN0_U_BASE, *bRGA3_WIN0_V_BASE; + u32 *bRGA3_WIN0_VIR_STRIDE; + u32 *bRGA3_WIN0_UV_VIR_STRIDE; + u32 *bRGA3_WIN0_SRC_SIZE; + u32 *bRGA3_WIN0_ACT_OFF; + u32 *bRGA3_WIN0_ACT_SIZE; + u32 *bRGA3_WIN0_DST_SIZE; + + u32 *bRGA3_WIN0_SCL_FAC; + /* Not used yet. */ + // u32 *bRGA3_WIN0_FBC_OFF; + + u32 sw = 0, sh = 0; + u32 dw = 0, dh = 0; + u32 param_x = 0, param_y = 0; + u8 x_up = 0, y_up = 0, x_by = 0, y_by = 0; + + u32 reg = 0; + + u8 win_format = 0; + u8 win_yc_swp = 0; + + /* rb swap on RGB, uv swap on YUV */ + u8 win_pix_swp = 0; + + /* + * 1: Semi planar, for yuv 4:2:x + * 2: Interleaved (yuyv), for yuv422 8bit only ,RGB + */ + u8 win_interleaved = 1; + + /* enable r2y or y2r */ + u8 win_r2y = 0; + u8 win_y2r = 0; + + u8 rotate_mode = 0; + u8 xmirror = 0; + u8 ymirror = 0; + + u8 pixel_width = 1; + u8 yuv10 = 0; + + u32 stride = 0; + u32 uv_stride = 0; + + bRGA3_WIN0_RD_CTRL = (u32 *) (base + RGA3_WIN0_RD_CTRL_OFFSET); + + bRGA3_WIN0_Y_BASE = (u32 *) (base + RGA3_WIN0_Y_BASE_OFFSET); + bRGA3_WIN0_U_BASE = (u32 *) (base + RGA3_WIN0_U_BASE_OFFSET); + bRGA3_WIN0_V_BASE = (u32 *) (base + RGA3_WIN0_V_BASE_OFFSET); + + bRGA3_WIN0_VIR_STRIDE = (u32 *) (base + RGA3_WIN0_VIR_STRIDE_OFFSET); + bRGA3_WIN0_UV_VIR_STRIDE = + (u32 *) (base + RGA3_WIN0_UV_VIR_STRIDE_OFFSET); + + /* Not used yet. */ + // bRGA3_WIN0_FBC_OFF = (u32 *) (base + RGA3_WIN0_FBC_OFF_OFFSET); + bRGA3_WIN0_ACT_OFF = (u32 *) (base + RGA3_WIN0_ACT_OFF_OFFSET); + bRGA3_WIN0_SRC_SIZE = (u32 *) (base + RGA3_WIN0_SRC_SIZE_OFFSET); + bRGA3_WIN0_ACT_SIZE = (u32 *) (base + RGA3_WIN0_ACT_SIZE_OFFSET); + bRGA3_WIN0_DST_SIZE = (u32 *) (base + RGA3_WIN0_DST_SIZE_OFFSET); + + bRGA3_WIN0_SCL_FAC = (u32 *) (base + RGA3_WIN0_SCL_FAC_OFFSET); + + if (msg->win0.rotate_mode != 0) { + rotate_mode = msg->rotate_mode & RGA3_ROT_BIT_ROT_90 ? 1 : 0; + xmirror = msg->rotate_mode & RGA3_ROT_BIT_X_MIRROR ? 1 : 0; + ymirror = msg->rotate_mode & RGA3_ROT_BIT_Y_MIRROR ? 1 : 0; + } + + /* scale */ + dw = msg->win0.dst_act_w; + dh = msg->win0.dst_act_h; + + if (rotate_mode) { + sh = msg->win0.src_act_w; + sw = msg->win0.src_act_h; + } else { + sw = msg->win0.src_act_w; + sh = msg->win0.src_act_h; + } + + if (sw > dw) { + x_up = 0; + x_by = 0; + } else if (sw < dw) { + x_up = 1; + x_by = 0; + } else { + x_up = 0; + x_by = 1; + } + + if (sh > dh) { + y_up = 0; + y_by = 0; + } else if (sh < dh) { + y_up = 1; + y_by = 0; + } else { + y_up = 0; + y_by = 1; + } + + if (x_by == 1 && x_up == 0) + param_x = 0; + else if (x_up == 1 && x_by == 0) { + param_x = FACTOR_MAX * (sw - 1) / (dw - 1); + /* even multiples of 128 require a scaling factor -1 */ + if ((FACTOR_MAX * (sw - 1)) % (dw - 1) == 0) + param_x = param_x - 1; + } else + param_x = FACTOR_MAX * (dw - 1) / (sw - 1) + 1; + + if (y_by == 1 && y_up == 0) + param_y = 0; + else if (y_up == 1 && y_by == 0) { + param_y = FACTOR_MAX * (sh - 1) / (dh - 1); + /* even multiples of 128 require a scaling factor -1 */ + if ((FACTOR_MAX * (sh - 1)) % (dh - 1) == 0) + param_y = param_y - 1; + } else + param_y = FACTOR_MAX * (dh - 1) / (sh - 1) + 1; + + switch (msg->win0.format) { + case RGA_FORMAT_RGBA_8888: + win_format = 0x8; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_BGRA_8888: + win_format = 0x6; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_ARGB_8888: + win_format = 0x9; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_ABGR_8888: + win_format = 0x7; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_RGB_888: + win_format = 0x5; + pixel_width = 3; + win_interleaved = 2; + win_pix_swp = 1; + break; + case RGA_FORMAT_BGR_888: + win_format = 0x5; + pixel_width = 3; + win_interleaved = 2; + break; + case RGA_FORMAT_RGB_565: + win_format = 0x4; + pixel_width = 2; + win_interleaved = 2; + win_pix_swp = 1; + break; + case RGA_FORMAT_BGR_565: + win_format = 0x4; + pixel_width = 2; + win_interleaved = 2; + break; + + case RGA_FORMAT_YVYU_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 1; + win_yc_swp = 1; + win_interleaved = 2; + break; + case RGA_FORMAT_VYUY_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 1; + win_yc_swp = 0; + win_interleaved = 2; + break; + case RGA_FORMAT_YUYV_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 0; + win_yc_swp = 1; + win_interleaved = 2; + break; + case RGA_FORMAT_UYVY_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 0; + win_yc_swp = 0; + win_interleaved = 2; + break; + + case RGA_FORMAT_YCbCr_422_SP: + win_format = 0x1; + break; + case RGA_FORMAT_YCbCr_420_SP: + win_format = 0x0; + break; + case RGA_FORMAT_YCrCb_422_SP: + win_format = 0x1; + win_pix_swp = 1; + break; + case RGA_FORMAT_YCrCb_420_SP: + win_format = 0x0; + win_pix_swp = 1; + break; + + case RGA_FORMAT_YCbCr_420_SP_10B: + win_format = 0x2; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_420_SP_10B: + win_format = 0x2; + yuv10 = 1; + win_pix_swp = 1; + break; + case RGA_FORMAT_YCbCr_422_SP_10B: + win_format = 0x3; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_422_SP_10B: + win_format = 0x3; + yuv10 = 1; + win_pix_swp = 1; + break; + }; + + if (rga_is_rgb_format(msg->win0.format) && + rga_is_yuv_format(msg->wr.format)) + win_r2y = 1; + if (rga_is_yuv_format(msg->win0.format) && + rga_is_rgb_format(msg->wr.format)) + win_y2r = 1; + + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_R2Y_EN)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_R2Y_EN(win_r2y))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_Y2R_EN)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_Y2R_EN(win_y2r))); + + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_PIC_FORMAT)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_PIC_FORMAT(win_format))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_PIX_SWAP)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_PIX_SWAP(win_pix_swp))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_YC_SWAP)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_YC_SWAP(win_yc_swp))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_FORMAT)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_FORMAT(win_interleaved))); + + if (win_r2y == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE(msg->win0.r2y_mode))); + } else if (win_y2r == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE(msg->win0.y2r_mode))); + } + + /* rotate & mirror */ + if (msg->win0.rotate_mode == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_ROT)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_ROT(rotate_mode))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_XMIRROR)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_XMIRROR(xmirror))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_YMIRROR)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_YMIRROR(ymirror))); + } + + /* scale */ + *bRGA3_WIN0_SCL_FAC = param_x | param_y << 16; + + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_BY)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_BY(x_by))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_UP)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_HOR_UP(x_up))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_BY)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_BY(y_by))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_UP)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_VER_UP(y_up))); + + /* rd_mode */ + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_RD_MODE(msg->win0.rd_mode))); + /* win0 enable */ + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_ENABLE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_ENABLE(msg->win0.enable))); + + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT(1))); + + /* Only on raster mode, yuv 10bit can change to compact or set endian */ + if (msg->win0.rd_mode == RGA_RASTER_MODE && yuv10 == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_YUV10B_COMPACT + (msg->win0.is_10b_compact))); + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_ENDIAN_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_ENDIAN_MODE + (msg->win0.is_10b_endian))); + } + + *bRGA3_WIN0_RD_CTRL = reg; + + switch (msg->win0.rd_mode) { + case 0: /* raster */ + stride = (((msg->win0.vir_w * pixel_width) + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win0.format)) + uv_stride = ((msg->win0.vir_w + 15) & ~15) >> 2; + else + uv_stride = stride; + break; + + case 1: /* fbc */ + stride = ((msg->win0.vir_w + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win0.format)) + uv_stride = ((msg->win0.vir_w + 15) & ~15) >> 2; + else + uv_stride = stride; + break; + + case 2: /* tile 8*8 */ + /* + * tile 8*8 mode 8 lines of data are read/written at one time, + * so stride needs * 8. YUV420 only has 4 lines of UV data, so + * it needs to >>1. + */ + stride = (((msg->win0.vir_w * pixel_width * 8) + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win0.format)) + uv_stride = ((((msg->win0.vir_w * 8) + 15) & ~15) >> 1) >> 2; + else + uv_stride = stride; + break; + } + + *bRGA3_WIN0_Y_BASE = (u32) msg->win0.yrgb_addr; + *bRGA3_WIN0_U_BASE = (u32) msg->win0.uv_addr; + *bRGA3_WIN0_V_BASE = (u32) msg->win0.v_addr; + + *bRGA3_WIN0_VIR_STRIDE = stride; + *bRGA3_WIN0_UV_VIR_STRIDE = uv_stride; + + *bRGA3_WIN0_ACT_OFF = msg->win0.x_offset | (msg->win0.y_offset << 16); + /* fbcd offset */ + /* + * *bRGA3_WIN0_FBC_OFF = msg->win0.fbc_x_offset | + * (msg->win0.fbc_y_offset << 16); + */ + + /* do not use win0 src size except fbcd */ + /* in FBCD, src_width needs to be aligned at 16 */ + *bRGA3_WIN0_SRC_SIZE = ALIGN(msg->win0.src_act_w + msg->win0.x_offset, 16) | + (ALIGN(msg->win0.y_offset + msg->win0.src_act_h, 16) << 16); + *bRGA3_WIN0_ACT_SIZE = + msg->win0.src_act_w | (msg->win0.src_act_h << 16); + *bRGA3_WIN0_DST_SIZE = + msg->win0.dst_act_w | (msg->win0.dst_act_h << 16); +} + +static void RGA3_set_reg_win1_info(u8 *base, struct rga3_req *msg) +{ + u32 *bRGA3_WIN1_RD_CTRL; + u32 *bRGA3_WIN1_Y_BASE, *bRGA3_WIN1_U_BASE, *bRGA3_WIN1_V_BASE; + u32 *bRGA3_WIN1_VIR_STRIDE; + u32 *bRGA3_WIN1_UV_VIR_STRIDE; + u32 *bRGA3_WIN1_SRC_SIZE; + u32 *bRGA3_WIN1_ACT_OFF; + u32 *bRGA3_WIN1_ACT_SIZE; + u32 *bRGA3_WIN1_DST_SIZE; + + u32 *bRGA3_WIN1_SCL_FAC; + /* Not used yet. */ + // u32 *bRGA3_WIN1_FBC_OFF; + + u32 sw = 0, sh = 0; + u32 dw = 0, dh = 0; + u32 param_x = 0, param_y = 0; + u8 x_up = 0, y_up = 0, x_by = 0, y_by = 0; + + u32 reg = 0; + + u8 win_format = 0; + u8 win_yc_swp = 0; + + /* rb swap on RGB, uv swap on YUV */ + u8 win_pix_swp = 0; + + /* + * 1: Semi planar, for yuv 4:2:x + * 2: Interleaved (yuyv), for yuv422 8bit only ,RGB + */ + u8 win_interleaved = 1; + + u8 pixel_width = 1; + u8 yuv10 = 0; + + /* enable r2y or y2r */ + u8 win_r2y = 0; + u8 win_y2r = 0; + + u8 rotate_mode = 0; + u8 xmirror = 0; + u8 ymirror = 0; + + u32 stride = 0; + u32 uv_stride = 0; + + bRGA3_WIN1_RD_CTRL = (u32 *) (base + RGA3_WIN1_RD_CTRL_OFFSET); + + bRGA3_WIN1_Y_BASE = (u32 *) (base + RGA3_WIN1_Y_BASE_OFFSET); + bRGA3_WIN1_U_BASE = (u32 *) (base + RGA3_WIN1_U_BASE_OFFSET); + bRGA3_WIN1_V_BASE = (u32 *) (base + RGA3_WIN1_V_BASE_OFFSET); + + bRGA3_WIN1_VIR_STRIDE = (u32 *) (base + RGA3_WIN1_VIR_STRIDE_OFFSET); + bRGA3_WIN1_UV_VIR_STRIDE = + (u32 *) (base + RGA3_WIN1_UV_VIR_STRIDE_OFFSET); + + /* Not used yet. */ + // bRGA3_WIN1_FBC_OFF = (u32 *) (base + RGA3_WIN1_FBC_OFF_OFFSET); + bRGA3_WIN1_ACT_OFF = (u32 *) (base + RGA3_WIN1_ACT_OFF_OFFSET); + bRGA3_WIN1_SRC_SIZE = (u32 *) (base + RGA3_WIN1_SRC_SIZE_OFFSET); + bRGA3_WIN1_ACT_SIZE = (u32 *) (base + RGA3_WIN1_ACT_SIZE_OFFSET); + bRGA3_WIN1_DST_SIZE = (u32 *) (base + RGA3_WIN1_DST_SIZE_OFFSET); + + bRGA3_WIN1_SCL_FAC = (u32 *) (base + RGA3_WIN1_SCL_FAC_OFFSET); + + if (msg->win1.rotate_mode != 0) { + rotate_mode = msg->rotate_mode & RGA3_ROT_BIT_ROT_90 ? 1 : 0; + xmirror = msg->rotate_mode & RGA3_ROT_BIT_X_MIRROR ? 1 : 0; + ymirror = msg->rotate_mode & RGA3_ROT_BIT_Y_MIRROR ? 1 : 0; + } + + /* scale */ + dw = msg->win1.dst_act_w; + dh = msg->win1.dst_act_h; + + if (rotate_mode) { + sh = msg->win1.src_act_w; + sw = msg->win1.src_act_h; + } else { + sw = msg->win1.src_act_w; + sh = msg->win1.src_act_h; + } + + if (sw > dw) { + x_up = 0; + x_by = 0; + } else if (sw < dw) { + x_up = 1; + x_by = 0; + } else { + x_up = 0; + x_by = 1; + } + + if (sh > dh) { + y_up = 0; + y_by = 0; + } else if (sh < dh) { + y_up = 1; + y_by = 0; + } else { + y_up = 0; + y_by = 1; + } + + if (x_by == 1) + param_x = 0; + else if (x_up == 1) { + param_x = (FACTOR_MAX * (sw - 1)) / (dw - 1); + /* even multiples of 128 require a scaling factor -1 */ + if ((FACTOR_MAX * (sw - 1)) % (dw - 1) == 0) + param_x = param_x - 1; + } else + param_x = (FACTOR_MAX * (dw - 1)) / (sw - 1) + 1; + + if (y_by == 1) + param_y = 0; + else if (y_up == 1) { + param_y = (FACTOR_MAX * (sh - 1)) / (dh - 1); + /* even multiples of 128 require a scaling factor -1 */ + if ((FACTOR_MAX * (sh - 1)) % (dh - 1) == 0) + param_y = param_y - 1; + } else + param_y = (FACTOR_MAX * (dh - 1)) / (sh - 1) + 1; + + switch (msg->win1.format) { + case RGA_FORMAT_RGBA_8888: + win_format = 0x8; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_BGRA_8888: + win_format = 0x6; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_ARGB_8888: + win_format = 0x9; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_ABGR_8888: + win_format = 0x7; + pixel_width = 4; + win_interleaved = 2; + break; + case RGA_FORMAT_RGB_888: + win_format = 0x5; + pixel_width = 3; + win_interleaved = 2; + win_pix_swp = 1; + break; + case RGA_FORMAT_BGR_888: + win_format = 0x5; + pixel_width = 3; + win_interleaved = 2; + break; + case RGA_FORMAT_RGB_565: + win_format = 0x4; + pixel_width = 2; + win_interleaved = 2; + win_pix_swp = 1; + break; + case RGA_FORMAT_BGR_565: + win_format = 0x4; + pixel_width = 2; + win_interleaved = 2; + break; + + case RGA_FORMAT_YVYU_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 1; + win_yc_swp = 1; + win_interleaved = 2; + break; + case RGA_FORMAT_VYUY_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 1; + win_yc_swp = 0; + win_interleaved = 2; + break; + case RGA_FORMAT_YUYV_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 0; + win_yc_swp = 1; + win_interleaved = 2; + break; + case RGA_FORMAT_UYVY_422: + win_format = 0x1; + pixel_width = 2; + win_pix_swp = 0; + win_yc_swp = 0; + win_interleaved = 2; + break; + + case RGA_FORMAT_YCbCr_422_SP: + win_format = 0x1; + break; + case RGA_FORMAT_YCbCr_420_SP: + win_format = 0x0; + break; + case RGA_FORMAT_YCrCb_422_SP: + win_format = 0x1; + win_pix_swp = 1; + break; + case RGA_FORMAT_YCrCb_420_SP: + win_format = 0x0; + win_pix_swp = 1; + break; + + case RGA_FORMAT_YCbCr_420_SP_10B: + win_format = 0x2; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_420_SP_10B: + win_format = 0x2; + win_pix_swp = 1; + yuv10 = 1; + break; + case RGA_FORMAT_YCbCr_422_SP_10B: + win_format = 0x3; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_422_SP_10B: + win_format = 0x3; + win_pix_swp = 1; + yuv10 = 1; + break; + }; + + if (rga_is_rgb_format(msg->win1.format) && + rga_is_yuv_format(msg->wr.format)) + win_r2y = 1; + if (rga_is_yuv_format(msg->win1.format) && + rga_is_rgb_format(msg->wr.format)) + win_y2r = 1; + + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_R2Y_EN)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_R2Y_EN(win_r2y))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_Y2R_EN)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_Y2R_EN(win_y2r))); + + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_PIC_FORMAT)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_PIC_FORMAT(win_format))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_PIX_SWAP)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_PIX_SWAP(win_pix_swp))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_YC_SWAP)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_YC_SWAP(win_yc_swp))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_FORMAT)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_FORMAT(win_interleaved))); + + if (win_r2y == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE(msg->win1.r2y_mode))); + } else if (win_y2r == 1) { + reg = + ((reg & (~m_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE)) | + (s_RGA3_WIN0_RD_CTRL_SW_WIN0_CSC_MODE(msg->win1.y2r_mode))); + } + + /* rotate & mirror */ + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_ROT)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_ROT(rotate_mode))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_XMIRROR)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_XMIRROR(xmirror))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_YMIRROR)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_YMIRROR(ymirror))); + //warning: TRM not complete + /* scale */ + *bRGA3_WIN1_SCL_FAC = param_x | param_y << 16; + + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_BY)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_BY(x_by))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_UP)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_HOR_UP(x_up))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_BY)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_BY(y_by))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_UP)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_VER_UP(y_up))); + + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT(1))); + + /* Only on roster mode, yuv 10bit can change to compact or set endian */ + if (msg->win1.rd_mode == RGA_RASTER_MODE && yuv10 == 1) { + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_YUV10B_COMPACT + (msg->win1.is_10b_compact))); + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_ENDIAN_MODE)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_ENDIAN_MODE + (msg->win1.is_10b_endian))); + } + + /* rd_mode */ + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_MODE)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_RD_MODE(msg->win1.rd_mode))); + /* win1 enable */ + reg = + ((reg & (~m_RGA3_WIN1_RD_CTRL_SW_WIN1_ENABLE)) | + (s_RGA3_WIN1_RD_CTRL_SW_WIN1_ENABLE(msg->win1.enable))); + + *bRGA3_WIN1_RD_CTRL = reg; + + switch (msg->win1.rd_mode) { + case 0: /* raster */ + stride = (((msg->win1.vir_w * pixel_width) + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win1.format)) + uv_stride = ((msg->win1.vir_w + 15) & ~15) >> 2; + else + uv_stride = stride; + break; + + case 1: /* fbc */ + stride = ((msg->win1.vir_w + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win1.format)) + uv_stride = ((msg->win1.vir_w + 15) & ~15) >> 2; + else + uv_stride = stride; + break; + + case 2: /* tile 8*8 */ + stride = (((msg->win1.vir_w * pixel_width * 8) + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win1.format)) + uv_stride = ((((msg->win1.vir_w * 8) + 15) & ~15) >> 1) >> 2; + else + uv_stride = stride; + break; + } + + *bRGA3_WIN1_Y_BASE = (u32) msg->win1.yrgb_addr; + *bRGA3_WIN1_U_BASE = (u32) msg->win1.uv_addr; + *bRGA3_WIN1_V_BASE = (u32) msg->win1.v_addr; + + *bRGA3_WIN1_VIR_STRIDE = stride; + *bRGA3_WIN1_UV_VIR_STRIDE = uv_stride; + + *bRGA3_WIN1_ACT_OFF = msg->win1.x_offset | (msg->win1.y_offset << 16); + /* fbcd offset */ + /* + * *bRGA3_WIN1_FBC_OFF = msg->win1.fbc_x_offset | + * (msg->win1.fbc_y_offset << 16); + */ + + /* do not use win1 src size except fbcd */ + *bRGA3_WIN1_SRC_SIZE = (msg->win1.src_act_w + + msg->win1.x_offset) | ((msg->win1.src_act_h + + msg->win1.y_offset) << 16); + *bRGA3_WIN1_ACT_SIZE = + msg->win1.src_act_w | (msg->win1.src_act_h << 16); + *bRGA3_WIN1_DST_SIZE = + msg->win1.dst_act_w | (msg->win1.dst_act_h << 16); +} + +static void RGA3_set_reg_wr_info(u8 *base, struct rga3_req *msg) +{ + u32 *bRGA3_WR_RD_CTRL; + u32 *bRGA3_WR_Y_BASE, *bRGA3_WR_U_BASE, *bRGA3_WR_V_BASE; + u32 *bRGA3_WR_VIR_STRIDE; + u32 *bRGA3_WR_PL_VIR_STRIDE; + u32 *bRGA3_WR_FBCD_CTRL; + + u32 reg = 0; + u32 fbcd_reg = 0; + + u8 wr_format = 0; + u8 wr_yc_swp = 0; + + /* rb swap on RGB, uv swap on YUV */ + u8 wr_pix_swp = 0; + + u8 pixel_width = 1; + u8 yuv10 = 0; + + /* + * 1: Semi planar, for yuv 4:2:x + * 2: Interleaved (yuyv), for yuv422 8bit only ,RGB + */ + u8 wr_interleaved = 1; + + u32 stride = 0; + u32 uv_stride = 0; + + u32 vir_h = 0; + + bRGA3_WR_RD_CTRL = (u32 *) (base + RGA3_WR_CTRL_OFFSET); + bRGA3_WR_FBCD_CTRL = (u32 *) (base + RGA3_WR_FBCE_CTRL_OFFSET); + + bRGA3_WR_Y_BASE = (u32 *) (base + RGA3_WR_Y_BASE_OFFSET); + bRGA3_WR_U_BASE = (u32 *) (base + RGA3_WR_U_BASE_OFFSET); + bRGA3_WR_V_BASE = (u32 *) (base + RGA3_WR_V_BASE_OFFSET); + + bRGA3_WR_VIR_STRIDE = (u32 *) (base + RGA3_WR_VIR_STRIDE_OFFSET); + bRGA3_WR_PL_VIR_STRIDE = + (u32 *) (base + RGA3_WR_PL_VIR_STRIDE_OFFSET); + + switch (msg->wr.format) { + case RGA_FORMAT_RGBA_8888: + wr_format = 0x6; + pixel_width = 4; + wr_interleaved = 2; + wr_pix_swp = 1; + break; + case RGA_FORMAT_BGRA_8888: + wr_format = 0x6; + pixel_width = 4; + wr_interleaved = 2; + break; + case RGA_FORMAT_RGB_888: + wr_format = 0x5; + pixel_width = 3; + wr_interleaved = 2; + wr_pix_swp = 1; + break; + case RGA_FORMAT_BGR_888: + wr_format = 0x5; + pixel_width = 3; + wr_interleaved = 2; + break; + case RGA_FORMAT_RGB_565: + wr_format = 0x4; + pixel_width = 2; + wr_interleaved = 2; + wr_pix_swp = 1; + break; + case RGA_FORMAT_BGR_565: + wr_format = 0x4; + pixel_width = 2; + wr_interleaved = 2; + break; + + case RGA_FORMAT_YVYU_422: + wr_format = 0x1; + pixel_width = 2; + wr_pix_swp = 1; + wr_yc_swp = 1; + wr_interleaved = 2; + break; + case RGA_FORMAT_VYUY_422: + wr_format = 0x1; + pixel_width = 2; + wr_pix_swp = 1; + wr_yc_swp = 0; + wr_interleaved = 2; + break; + case RGA_FORMAT_YUYV_422: + wr_format = 0x1; + pixel_width = 2; + wr_pix_swp = 0; + wr_yc_swp = 1; + wr_interleaved = 2; + break; + case RGA_FORMAT_UYVY_422: + wr_format = 0x1; + pixel_width = 2; + wr_pix_swp = 0; + wr_yc_swp = 0; + wr_interleaved = 2; + break; + + case RGA_FORMAT_YCbCr_422_SP: + wr_format = 0x1; + break; + case RGA_FORMAT_YCbCr_420_SP: + wr_format = 0x0; + break; + case RGA_FORMAT_YCrCb_422_SP: + wr_format = 0x1; + wr_pix_swp = 1; + break; + case RGA_FORMAT_YCrCb_420_SP: + wr_format = 0x0; + wr_pix_swp = 1; + break; + + case RGA_FORMAT_YCbCr_420_SP_10B: + wr_format = 0x2; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_420_SP_10B: + wr_format = 0x2; + wr_pix_swp = 1; + yuv10 = 1; + break; + case RGA_FORMAT_YCbCr_422_SP_10B: + wr_format = 0x3; + yuv10 = 1; + break; + case RGA_FORMAT_YCrCb_422_SP_10B: + wr_format = 0x3; + wr_pix_swp = 1; + yuv10 = 1; + break; + }; + + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_PIC_FORMAT)) | + (s_RGA3_WR_CTRL_SW_WR_PIC_FORMAT(wr_format))); + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_PIX_SWAP)) | + (s_RGA3_WR_CTRL_SW_WR_PIX_SWAP(wr_pix_swp))); + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_YC_SWAP)) | + (s_RGA3_WR_CTRL_SW_WR_YC_SWAP(wr_yc_swp))); + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_FORMAT)) | + (s_RGA3_WR_CTRL_SW_WR_FORMAT(wr_interleaved))); + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_FBCE_SPARSE_EN)) | + (s_RGA3_WR_CTRL_SW_WR_FBCE_SPARSE_EN(1))); + + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_OUTSTANDING_MAX)) | + (s_RGA3_WR_CTRL_SW_OUTSTANDING_MAX(0xf))); + + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT)) | + (s_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT(1))); + + /* Only on roster mode, yuv 10bit can change to compact or set endian */ + if (msg->wr.rd_mode == 0 && yuv10 == 1) { + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT)) | + (s_RGA3_WR_CTRL_SW_WR_YUV10B_COMPACT + (msg->wr.is_10b_compact))); + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_ENDIAN_MODE)) | + (s_RGA3_WR_CTRL_SW_WR_ENDIAN_MODE + (msg->wr.is_10b_endian))); + } + + /* rd_mode */ + reg = + ((reg & (~m_RGA3_WR_CTRL_SW_WR_MODE)) | + (s_RGA3_WR_CTRL_SW_WR_MODE(msg->wr.rd_mode))); + + fbcd_reg = ((fbcd_reg & (~m_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_HOFF_DISS)) | + (s_RGA3_WR_FBCE_CTRL_SW_WR_FBCE_HOFF_DISS(0))); + + *bRGA3_WR_RD_CTRL = reg; + *bRGA3_WR_FBCD_CTRL = fbcd_reg; + + switch (msg->wr.rd_mode) { + case 0: /* raster */ + stride = (((msg->wr.vir_w * pixel_width) + 15) & ~15) >> 2; + uv_stride = ((msg->wr.vir_w + 15) & ~15) >> 2; + + *bRGA3_WR_U_BASE = (u32) msg->wr.uv_addr; + + break; + + case 1: /* fbc */ + stride = ((msg->wr.vir_w + 15) & ~15) >> 2; + /* need to calculate fbcd header size */ + vir_h = ((msg->wr.vir_h + 15) & ~15); + + /* RGBA8888 */ + if (wr_format == 0x6) + uv_stride = ((msg->wr.vir_w + 15) & ~15); + /* RGB888 */ + else if (wr_format == 0x5) + uv_stride = (((msg->wr.vir_w + 15) & ~15) >> 2) * 3; + /* RGB565, yuv422 8bit, yuv420 10bit */ + else if (wr_format == 0x4 || wr_format == 0x1 || wr_format == 0x2) + uv_stride = ((msg->wr.vir_w + 15) & ~15) >> 1; + /* yuv420 8bit */ + else if (wr_format == 0x0) + uv_stride = (((msg->wr.vir_w + 15) & ~15) >> 3) * 3; + /* yuv422 10bit */ + else if (wr_format == 0x3) + uv_stride = (((msg->wr.vir_w + 15) & ~15) >> 3) * 5; + + *bRGA3_WR_U_BASE = (u32) (msg->wr.uv_addr + ((stride * vir_h)>>2)); + + break; + + case 2: /* tile 8*8 */ + stride = (((msg->wr.vir_w * pixel_width * 8) + 15) & ~15) >> 2; + if (rga_is_yuv420_semi_planar_format(msg->win0.format)) + uv_stride = ((((msg->wr.vir_w * 8) + 15) & ~15) >> 1) >> 2; + else + uv_stride = stride; + + *bRGA3_WR_U_BASE = (u32) msg->wr.uv_addr; + break; + } + + *bRGA3_WR_Y_BASE = (u32) msg->wr.yrgb_addr; + *bRGA3_WR_V_BASE = (u32) msg->wr.v_addr; + + *bRGA3_WR_VIR_STRIDE = stride; + *bRGA3_WR_PL_VIR_STRIDE = uv_stride; +} + +static void RGA3_set_reg_overlap_info(u8 *base, struct rga3_req *msg) +{ + u32 *bRGA_OVERLAP_TOP_CTRL; + u32 *bRGA_OVERLAP_BOT_CTRL; + u32 *bRGA_OVERLAP_TOP_ALPHA; + u32 *bRGA_OVERLAP_BOT_ALPHA; + u32 *bRGA_OVERLAP_TOP_KEY_MIN; + u32 *bRGA_OVERLAP_TOP_KEY_MAX; + + u32 *bRGA_OVERLAP_CTRL; + u32 *bRGA3_OVLP_OFF; + + u32 reg; + union rga3_color_ctrl top_color_ctrl, bottom_color_ctrl; + union rga3_alpha_ctrl top_alpha_ctrl, bottom_alpha_ctrl; + struct rga_alpha_config *config; + + bRGA_OVERLAP_TOP_CTRL = (u32 *) (base + RGA3_OVLP_TOP_CTRL_OFFSET); + bRGA_OVERLAP_BOT_CTRL = (u32 *) (base + RGA3_OVLP_BOT_CTRL_OFFSET); + bRGA_OVERLAP_TOP_ALPHA = (u32 *) (base + RGA3_OVLP_TOP_ALPHA_OFFSET); + bRGA_OVERLAP_BOT_ALPHA = (u32 *) (base + RGA3_OVLP_BOT_ALPHA_OFFSET); + + bRGA_OVERLAP_CTRL = (u32 *) (base + RGA3_OVLP_CTRL_OFFSET); + bRGA3_OVLP_OFF = (u32 *) (base + RGA3_OVLP_OFF_OFFSET); + + /* Alpha blend */ + /*bot -> win0(dst), top -> win1(src). */ + top_color_ctrl.value = 0; + bottom_color_ctrl.value = 0; + top_alpha_ctrl.value = 0; + bottom_alpha_ctrl.value = 0; + config = &msg->alpha_config; + + if (config->fg_pixel_alpha_en) + top_color_ctrl.bits.blend_mode = + config->fg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL : + RGA_ALPHA_PER_PIXEL; + else + top_color_ctrl.bits.blend_mode = RGA_ALPHA_GLOBAL; + + if (config->bg_pixel_alpha_en) + bottom_color_ctrl.bits.blend_mode = + config->bg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL : + RGA_ALPHA_PER_PIXEL; + else + bottom_color_ctrl.bits.blend_mode = RGA_ALPHA_GLOBAL; + + /* + * Since the hardware uses 256 as 1, the original alpha value needs to + * be + (alpha >> 7). + */ + top_color_ctrl.bits.alpha_cal_mode = RGA_ALPHA_SATURATION; + bottom_color_ctrl.bits.alpha_cal_mode = RGA_ALPHA_SATURATION; + + top_color_ctrl.bits.global_alpha = config->fg_global_alpha_value; + bottom_color_ctrl.bits.global_alpha = config->bg_global_alpha_value; + + /* porter duff alpha enable */ + switch (config->mode) { + case RGA_ALPHA_BLEND_SRC: + /* + * SRC mode: + * Sf = 1, Df = 0; + * [Rc,Ra] = [Sc,Sa]; + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST: + /* + * SRC mode: + * Sf = 0, Df = 1; + * [Rc,Ra] = [Dc,Da]; + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE; + + break; + + case RGA_ALPHA_BLEND_SRC_OVER: + /* + * SRC-OVER mode: + * Sf = 1, Df = (1 - Sa) + * [Rc,Ra] = [ Sc + (1 - Sa) * Dc, Sa + (1 - Sa) * Da ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_DST_OVER: + /* + * DST-OVER mode: + * Sf = (1 - Da) , Df = 1 + * [Rc,Ra] = [ Sc * (1 - Da) + Dc, Sa * (1 - Da) + Da ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE; + + break; + + case RGA_ALPHA_BLEND_SRC_IN: + /* + * SRC-IN mode: + * Sf = Da , Df = 0 + * [Rc,Ra] = [ Sc * Da, Sa * Da ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST_IN: + /* + * DST-IN mode: + * Sf = 0 , Df = Sa + * [Rc,Ra] = [ Dc * Sa, Da * Sa ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE; + + break; + + case RGA_ALPHA_BLEND_SRC_OUT: + /* + * SRC-OUT mode: + * Sf = (1 - Da) , Df = 0 + * [Rc,Ra] = [ Sc * (1 - Da), Sa * (1 - Da) ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + break; + + case RGA_ALPHA_BLEND_DST_OUT: + /* + * DST-OUT mode: + * Sf = 0 , Df = (1 - Sa) + * [Rc,Ra] = [ Dc * (1 - Sa), Da * (1 - Sa) ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_SRC_ATOP: + /* + * SRC-ATOP mode: + * Sf = Da , Df = (1 - Sa) + * [Rc,Ra] = [ Sc * Da + Dc * (1 - Sa), Sa * Da + Da * (1 - Sa) ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_DST_ATOP: + /* + * DST-ATOP mode: + * Sf = (1 - Da) , Df = Sa + * [Rc,Ra] = [ Sc * (1 - Da) + Dc * Sa, Sa * (1 - Da) + Da * Sa ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE; + + break; + + case RGA_ALPHA_BLEND_XOR: + /* + * DST-XOR mode: + * Sf = (1 - Da) , Df = (1 - Sa) + * [Rc,Ra] = [ Sc * (1 - Da) + Dc * (1 - Sa), Sa * (1 - Da) + Da * (1 - Sa) ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE; + + break; + + case RGA_ALPHA_BLEND_CLEAR: + /* + * DST-CLEAR mode: + * Sf = 0 , Df = 0 + * [Rc,Ra] = [ 0, 0 ] + */ + top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT; + bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO; + + break; + + default: + break; + } + + if (!config->enable && msg->abb_alpha_pass) { + /* + * enabled by default bot_blend_m1 && bot_alpha_cal_m1 for src channel(win0) + * In ABB mode, the number will be fetched according to 16*16, so it needs to + * be enabled top_blend_m1 && top_alpha_cal_m1 for dst channel(wr). + */ + top_color_ctrl.bits.color_mode = RGA_ALPHA_PRE_MULTIPLIED; + + top_alpha_ctrl.bits.blend_mode = RGA_ALPHA_PER_PIXEL; + top_alpha_ctrl.bits.alpha_cal_mode = RGA_ALPHA_NO_SATURATION; + + bottom_color_ctrl.bits.color_mode = RGA_ALPHA_PRE_MULTIPLIED; + + bottom_alpha_ctrl.bits.blend_mode = RGA_ALPHA_PER_PIXEL; + bottom_alpha_ctrl.bits.alpha_cal_mode = RGA_ALPHA_NO_SATURATION; + } else { + top_color_ctrl.bits.color_mode = + config->fg_pre_multiplied ? + RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED; + + top_alpha_ctrl.bits.blend_mode = top_color_ctrl.bits.blend_mode; + top_alpha_ctrl.bits.alpha_cal_mode = top_color_ctrl.bits.alpha_cal_mode; + top_alpha_ctrl.bits.alpha_mode = top_color_ctrl.bits.alpha_mode; + top_alpha_ctrl.bits.factor_mode = top_color_ctrl.bits.factor_mode; + + bottom_color_ctrl.bits.color_mode = + config->bg_pre_multiplied ? + RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED; + + bottom_alpha_ctrl.bits.blend_mode = bottom_color_ctrl.bits.blend_mode; + bottom_alpha_ctrl.bits.alpha_cal_mode = bottom_color_ctrl.bits.alpha_cal_mode; + bottom_alpha_ctrl.bits.alpha_mode = bottom_color_ctrl.bits.alpha_mode; + bottom_alpha_ctrl.bits.factor_mode = bottom_color_ctrl.bits.factor_mode; + } + + *bRGA_OVERLAP_TOP_CTRL = top_color_ctrl.value; + *bRGA_OVERLAP_BOT_CTRL = bottom_color_ctrl.value; + *bRGA_OVERLAP_TOP_ALPHA = top_alpha_ctrl.value; + *bRGA_OVERLAP_BOT_ALPHA = bottom_alpha_ctrl.value; + + /* set RGA_OVERLAP_CTRL */ + reg = 0; + /* color key */ + bRGA_OVERLAP_TOP_KEY_MIN = + (u32 *) (base + RGA3_OVLP_TOP_KEY_MIN_OFFSET); + bRGA_OVERLAP_TOP_KEY_MAX = + (u32 *) (base + RGA3_OVLP_TOP_KEY_MAX_OFFSET); + + /* + * YG : value (0:9) + * UB : value >> 10 (10:19) + * VG : value >> 20 (20:29) + */ + if (msg->color_key_min > 0 || msg->color_key_max > 0) { + *bRGA_OVERLAP_TOP_KEY_MIN = msg->color_key_min; + *bRGA_OVERLAP_TOP_KEY_MAX = msg->color_key_max; + reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_TOP_KEY_EN)) | + (s_RGA3_OVLP_CTRL_SW_TOP_KEY_EN(1))); + } + + /* 1: ABB mode, 0: ABC mode, ABB cannot support fbc in&out */ + if (msg->win0.yrgb_addr == msg->wr.yrgb_addr) + reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_OVLP_MODE)) | + (s_RGA3_OVLP_CTRL_SW_OVLP_MODE(1))); + + /* 1: yuv field, 0: rgb field */ + if (rga_is_yuv_format(msg->wr.format)) + reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_OVLP_FIELD)) | + (s_RGA3_OVLP_CTRL_SW_OVLP_FIELD(1))); + + /* + * warning: if m1 & m0 need config split,need to redesign + * this judge, which consider RGBA8888 format + */ + reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN)) | + (s_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN(config->enable))); + + *bRGA_OVERLAP_CTRL = reg; + + *bRGA3_OVLP_OFF = msg->wr.x_offset | (msg->wr.y_offset << 16); +} + +static int rga3_gen_reg_info(u8 *base, struct rga3_req *msg) +{ + switch (msg->render_mode) { + case BITBLT_MODE: + RGA3_set_reg_win0_info(base, msg); + RGA3_set_reg_win1_info(base, msg); + RGA3_set_reg_overlap_info(base, msg); + RGA3_set_reg_wr_info(base, msg); + break; + default: + pr_err("error msg render mode %d\n", msg->render_mode); + break; + } + + return 0; +} + +static void addr_copy(struct rga_win_info_t *win, struct rga_img_info_t *img) +{ + win->yrgb_addr = img->yrgb_addr; + win->uv_addr = img->uv_addr; + win->v_addr = img->v_addr; + win->enable = 1; +} + +static void set_win_info(struct rga_win_info_t *win, struct rga_img_info_t *img) +{ + win->x_offset = img->x_offset; + win->y_offset = img->y_offset; + win->src_act_w = img->act_w; + win->src_act_h = img->act_h; + win->vir_w = img->vir_w; + win->vir_h = img->vir_h; + if (img->rd_mode == RGA_RASTER_MODE) + win->rd_mode = 0; + else if (img->rd_mode == RGA_FBC_MODE) + win->rd_mode = 1; + else if (img->rd_mode == RGA_TILE_MODE) + win->rd_mode = 2; + + switch (img->compact_mode) { + case RGA_10BIT_INCOMPACT: + win->is_10b_compact = 0; + break; + case RGA_10BIT_COMPACT: + default: + win->is_10b_compact = 1; + break; + } + + win->is_10b_endian = img->is_10b_endian; +} + +static void set_wr_info(struct rga_req *req_rga, struct rga3_req *req) +{ + /* The output w/h are bound to the dst_act_w/h of win0. */ + req->wr.dst_act_w = req->win0.dst_act_w; + req->wr.dst_act_h = req->win0.dst_act_h; + + /* Some configurations need to be all equal to the output w/h. */ + req->wr.vir_w = req_rga->dst.vir_w; + req->wr.vir_h = req_rga->dst.vir_h; + + if (req_rga->dst.rd_mode == RGA_RASTER_MODE) + req->wr.rd_mode = 0; + else if (req_rga->dst.rd_mode == RGA_FBC_MODE) + req->wr.rd_mode = 1; + else if (req_rga->dst.rd_mode == RGA_TILE_MODE) + req->wr.rd_mode = 2; + + switch (req_rga->dst.compact_mode) { + case RGA_10BIT_INCOMPACT: + req->wr.is_10b_compact = 0; + break; + case RGA_10BIT_COMPACT: + default: + req->wr.is_10b_compact = 1; + break; + } + + req->wr.is_10b_endian = req_rga->dst.is_10b_endian; +} + +/* TODO: common part */ +static void rga_cmd_to_rga3_cmd(struct rga_req *req_rga, struct rga3_req *req) +{ + struct rga_img_info_t tmp; + + req->render_mode = BITBLT_MODE; + + /* rotate & mirror */ + switch (req_rga->rotate_mode & 0x0f) { + case 0x1: + if (req_rga->sina == 65536 && req_rga->cosa == 0) { + /* rot-90 */ + req->rotate_mode = RGA3_ROT_BIT_ROT_90; + } else if (req_rga->sina == 0 && req_rga->cosa == -65536) { + /* rot-180 = X-mirror + Y-mirror */ + req->rotate_mode = RGA3_ROT_BIT_X_MIRROR | RGA3_ROT_BIT_Y_MIRROR; + } else if (req_rga->sina == -65536 && req_rga->cosa == 0) { + /* rot-270 or -90 = rot-90 + X-mirror + Y-mirror */ + req->rotate_mode = RGA3_ROT_BIT_X_MIRROR | RGA3_ROT_BIT_Y_MIRROR | + RGA3_ROT_BIT_ROT_90; + } else if (req_rga->sina == 0 && req_rga->cosa == 65536) { + /* bypass */ + req->rotate_mode = 0; + } + break; + case 0x2: + /* X-mirror */ + req->rotate_mode = RGA3_ROT_BIT_X_MIRROR; + break; + case 0x3: + /* Y-mirror */ + req->rotate_mode = RGA3_ROT_BIT_Y_MIRROR; + break; + case 0x4: + /* X-mirror + Y-mirror */ + req->rotate_mode = RGA3_ROT_BIT_X_MIRROR | RGA3_ROT_BIT_Y_MIRROR; + break; + default: + req->rotate_mode = 0; + break; + } + + /* The upper four bits are only allowed to configure the mirror. */ + switch ((req_rga->rotate_mode & 0xf0) >> 4) { + case 2: + /* X-mirror */ + req->rotate_mode ^= RGA3_ROT_BIT_X_MIRROR; + break; + case 3: + /* Y-mirror */ + req->rotate_mode ^= RGA3_ROT_BIT_Y_MIRROR; + break; + case 0x4: + /* X-mirror + Y-mirror */ + req->rotate_mode ^= RGA3_ROT_BIT_X_MIRROR | RGA3_ROT_BIT_Y_MIRROR; + break; + } + + req->win0_a_global_val = req_rga->alpha_global_value; + req->win1_a_global_val = req_rga->alpha_global_value; + + /* fixup yuv/rgb convert to rgba missing alpha channel */ + if (!(req_rga->alpha_rop_flag & 1)) { + if (!rga_is_alpha_format(req_rga->src.format) && + rga_is_alpha_format(req_rga->dst.format)) { + req->alpha_config.fg_global_alpha_value = 0xff; + req->alpha_config.bg_global_alpha_value = 0xff; + } + } + + /* simple win can not support dst offset */ + if ((!((req_rga->alpha_rop_flag) & 1)) && + (req_rga->dst.x_offset == 0 && req_rga->dst.y_offset == 0) && + (req_rga->src.yrgb_addr != req_rga->dst.yrgb_addr)) { + /* + * ABB mode Layer binding: + * src => win0 + * dst => wr + */ + + /* + * enabled by default bot_blend_m1 && bot_alpha_cal_m1 for src channel(win0) + * In ABB mode, the number will be fetched according to 16*16, so it needs to + * be enabled top_blend_m1 && top_alpha_cal_m1 for dst channel(wr). + */ + if (rga_is_alpha_format(req_rga->src.format)) + req->abb_alpha_pass = true; + + set_win_info(&req->win0, &req_rga->src); + + /* enable win0 rotate */ + req->win0.rotate_mode = 1; + + /* set win dst size */ + req->win0.dst_act_w = req_rga->dst.act_w; + req->win0.dst_act_h = req_rga->dst.act_h; + + addr_copy(&req->win0, &req_rga->src); + addr_copy(&req->wr, &req_rga->dst); + + req->win0.format = req_rga->src.format; + req->wr.format = req_rga->dst.format; + } else { + /* + * ABC mode Layer binding: + * src => win1 + * src1/dst => win0 + * dst => wr + */ + + /* + * enabled by default top_blend_m1 && top_alpha_cal_m1 for src channel(win1) + * In ABB mode, the number will be fetched according to 16*16, so it needs to + * be enabled bot_blend_m1 && bot_alpha_cal_m1 for src1/dst channel(win0). + */ + if (rga_is_alpha_format(req_rga->src.format)) + req->abb_alpha_pass = true; + + if (req_rga->pat.yrgb_addr != 0) { + if (req_rga->src.yrgb_addr == req_rga->dst.yrgb_addr) { + /* Convert ABC mode to ABB mode. */ + memcpy(&req_rga->src, &req_rga->pat, sizeof(req_rga->src)); + memset(&req_rga->pat, 0x0, sizeof(req_rga->pat)); + req_rga->bsfilter_flag = 0; + + rga_swap_pd_mode(req_rga); + } else if ((req_rga->dst.x_offset + req_rga->src.act_w > + req_rga->pat.act_w) || + (req_rga->dst.y_offset + req_rga->src.act_h > + req_rga->pat.act_h)) { + /* wr_offset + win1.act_size need > win0.act_size */ + memcpy(&tmp, &req_rga->src, sizeof(tmp)); + memcpy(&req_rga->src, &req_rga->pat, sizeof(req_rga->src)); + memcpy(&req_rga->pat, &tmp, sizeof(req_rga->pat)); + + rga_swap_pd_mode(req_rga); + } + } + + set_win_info(&req->win1, &req_rga->src); + + /* enable win1 rotate */ + req->win1.rotate_mode = 1; + + addr_copy(&req->win1, &req_rga->src); + addr_copy(&req->wr, &req_rga->dst); + + req->win1.format = req_rga->src.format; + req->wr.format = req_rga->dst.format; + + if (req_rga->pat.yrgb_addr != 0) { + /* A+B->C mode */ + set_win_info(&req->win0, &req_rga->pat); + addr_copy(&req->win0, &req_rga->pat); + req->win0.format = req_rga->pat.format; + + /* set win0 dst size */ + if (req->win0.x_offset || req->win0.y_offset) { + req->win0.src_act_w = req->win0.src_act_w + req->win0.x_offset; + req->win0.src_act_h = req->win0.src_act_h + req->win0.y_offset; + req->win0.dst_act_w = req_rga->dst.act_w + req->win0.x_offset; + req->win0.dst_act_h = req_rga->dst.act_h + req->win0.y_offset; + + req->win0.x_offset = 0; + req->win0.y_offset = 0; + } else { + req->win0.dst_act_w = req_rga->dst.act_w; + req->win0.dst_act_h = req_rga->dst.act_h; + } + /* set win1 dst size */ + req->win1.dst_act_w = req_rga->dst.act_w; + req->win1.dst_act_h = req_rga->dst.act_h; + } else { + /* A+B->B mode */ + set_win_info(&req->win0, &req_rga->dst); + addr_copy(&req->win0, &req_rga->dst); + req->win0.format = req_rga->dst.format; + + /* only win1 && wr support fbcd, win0 default raster */ + req->win0.rd_mode = 0; + + /* set win0 dst size */ + req->win0.dst_act_w = req_rga->dst.act_w; + req->win0.dst_act_h = req_rga->dst.act_h; + /* set win1 dst size */ + req->win1.dst_act_w = req_rga->dst.act_w; + req->win1.dst_act_h = req_rga->dst.act_h; + } + + /* dst offset need to config overlap offset */ + req->wr.x_offset = req_rga->dst.x_offset; + req->wr.y_offset = req_rga->dst.y_offset; + } + set_wr_info(req_rga, req); + + if (req->rotate_mode & RGA3_ROT_BIT_ROT_90) { + if (req->win1.yrgb_addr != 0) { + /* ABB */ + if (req->win0.yrgb_addr == req->wr.yrgb_addr) { + req->win1.dst_act_w = req_rga->dst.act_h; + req->win1.dst_act_h = req_rga->dst.act_w; + + /* win0 do not need rotate, but net equal to wr */ + req->win0.dst_act_w = req_rga->dst.act_h; + req->win0.dst_act_h = req_rga->dst.act_w; + req->win0.src_act_w = req_rga->dst.act_h; + req->win0.src_act_h = req_rga->dst.act_w; + } + } else { + req->win0.rotate_mode = 1; + req->win0.dst_act_w = req_rga->dst.act_h; + req->win0.dst_act_h = req_rga->dst.act_w; + } + } + + /* overlap */ + /* Alpha blend mode */ + if (((req_rga->alpha_rop_flag) & 1)) { + if ((req_rga->alpha_rop_flag >> 3) & 1) { + req->alpha_config.enable = true; + + if ((req_rga->alpha_rop_flag >> 9) & 1) { + req->alpha_config.fg_pre_multiplied = false; + req->alpha_config.bg_pre_multiplied = false; + } else { + req->alpha_config.fg_pre_multiplied = true; + req->alpha_config.bg_pre_multiplied = true; + } + + req->alpha_config.fg_pixel_alpha_en = rga_is_alpha_format(req->win1.format); + req->alpha_config.bg_pixel_alpha_en = rga_is_alpha_format(req->win0.format); + + if (req_rga->feature.global_alpha_en) { + if (req_rga->fg_global_alpha < 0xff) { + req->alpha_config.fg_global_alpha_en = true; + req->alpha_config.fg_global_alpha_value = + req_rga->fg_global_alpha; + } else if (!req->alpha_config.fg_pixel_alpha_en) { + req->alpha_config.fg_global_alpha_en = true; + req->alpha_config.fg_global_alpha_value = 0xff; + } + + if (req_rga->bg_global_alpha < 0xff) { + req->alpha_config.bg_global_alpha_en = true; + req->alpha_config.bg_global_alpha_value = + req_rga->bg_global_alpha; + } else if (!req->alpha_config.bg_pixel_alpha_en) { + req->alpha_config.bg_global_alpha_en = true; + req->alpha_config.bg_global_alpha_value = 0xff; + } + } else { + req->alpha_config.bg_global_alpha_value = 0xff; + req->alpha_config.bg_global_alpha_value = 0xff; + } + + req->alpha_config.mode = req_rga->PD_mode; + } + } + + /* yuv to rgb */ + /* 601 limit */ + if (req_rga->yuv2rgb_mode == 1) { + req->win0.y2r_mode = 0; + req->win1.y2r_mode = 0; + /* 601 full */ + } else if (req_rga->yuv2rgb_mode == 2) { + req->win0.y2r_mode = 2; + req->win1.y2r_mode = 2; + /* 709 limit */ + } else if (req_rga->yuv2rgb_mode == 3) { + req->win0.y2r_mode = 1; + req->win1.y2r_mode = 1; + } + + /* rgb to yuv */ + /* 601 limit */ + if ((req_rga->yuv2rgb_mode >> 2) == 2) { + req->win0.r2y_mode = 0; + req->win1.r2y_mode = 0; + /* 601 full */ + } else if ((req_rga->yuv2rgb_mode >> 2) == 1) { + req->win0.r2y_mode = 2; + req->win1.r2y_mode = 2; + /* 709 limit */ + } else if ((req_rga->yuv2rgb_mode >> 2) == 3) { + req->win0.r2y_mode = 1; + req->win1.r2y_mode = 1; + } + + /* color key: 8bit->10bit */ + req->color_key_min = (req_rga->color_key_min & 0xff) << 22 | + ((req_rga->color_key_min >> 8) & 0xff) << 2 | + ((req_rga->color_key_min >> 16) & 0xff) << 12; + req->color_key_max = (req_rga->color_key_max & 0xff) << 22 | + ((req_rga->color_key_max >> 8) & 0xff) << 2 | + ((req_rga->color_key_max >> 16) & 0xff) << 12; + + if (req_rga->mmu_info.mmu_en && (req_rga->mmu_info.mmu_flag & 1) == 1) { + req->mmu_info.src0_mmu_flag = 1; + req->mmu_info.src1_mmu_flag = 1; + req->mmu_info.dst_mmu_flag = 1; + } +} + +static void rga3_soft_reset(struct rga_scheduler_t *scheduler) +{ + u32 i; + u32 iommu_dte_addr = 0; + + if (scheduler->data->mmu == RGA_IOMMU) + iommu_dte_addr = rga_read(RGA_IOMMU_DTE_ADDR, scheduler); + + rga_write(s_RGA3_SYS_CTRL_CCLK_SRESET(1) | s_RGA3_SYS_CTRL_ACLK_SRESET(1), + RGA3_SYS_CTRL, scheduler); + + for (i = 0; i < RGA_RESET_TIMEOUT; i++) { + if (rga_read(RGA3_RO_SRST, scheduler) & m_RGA3_RO_SRST_RO_RST_DONE) + break; + + udelay(1); + } + + rga_write(s_RGA3_SYS_CTRL_CCLK_SRESET(0) | s_RGA3_SYS_CTRL_ACLK_SRESET(0), + RGA3_SYS_CTRL, scheduler); + + if (scheduler->data->mmu == RGA_IOMMU) { + rga_write(iommu_dte_addr, RGA_IOMMU_DTE_ADDR, scheduler); + /* enable iommu */ + rga_write(RGA_IOMMU_CMD_ENABLE_PAGING, RGA_IOMMU_COMMAND, scheduler); + } + + if (i == RGA_RESET_TIMEOUT) + pr_err("RGA3 core[%d] soft reset timeout. SYS_CTRL[0x%x], RO_SRST[0x%x]\n", + scheduler->core, rga_read(RGA3_SYS_CTRL, scheduler), + rga_read(RGA3_RO_SRST, scheduler)); + else + pr_info("RGA3 core[%d] soft reset complete.\n", scheduler->core); +} + +static int rga3_scale_check(const struct rga3_req *req) +{ + u32 win0_saw, win0_sah, win0_daw, win0_dah; + u32 win1_saw, win1_sah, win1_daw, win1_dah; + + win0_saw = req->win0.src_act_w; + win0_sah = req->win0.src_act_h; + win0_daw = req->win0.dst_act_w; + win0_dah = req->win0.dst_act_h; + + if (((win0_saw >> 3) > win0_daw) || ((win0_sah >> 3) > win0_dah)) { + pr_info("win0 unsupported to scaling less than 1/8 times.\n"); + return -EINVAL; + } + if (((win0_daw >> 3) > win0_saw) || ((win0_dah >> 3) > win0_sah)) { + pr_info("win0 unsupported to scaling more than 8 times.\n"); + return -EINVAL; + } + + if (req->win1.yrgb_addr != 0) { + win1_saw = req->win1.src_act_w; + win1_sah = req->win1.src_act_h; + win1_daw = req->win1.dst_act_w; + win1_dah = req->win1.dst_act_h; + + if (((win1_saw >> 3) > win1_daw) || ((win1_sah >> 3) > win1_dah)) { + pr_info("win1 unsupported to scaling less than 1/8 times.\n"); + return -EINVAL; + } + if (((win1_daw >> 3) > win1_saw) || ((win1_dah >> 3) > win1_sah)) { + pr_info("win1 unsupported to scaling more than 8 times.\n"); + return -EINVAL; + } + } + + return 0; +} + +static int rga3_check_param(const struct rga_hw_data *data, const struct rga3_req *req) +{ + if (unlikely(rga_hw_out_of_range(&(data->input_range), + req->win0.src_act_w, req->win0.src_act_h) || + rga_hw_out_of_range(&(data->input_range), + req->win0.dst_act_w, req->win0.dst_act_h) || + rga_hw_out_of_range(&(data->input_range), + req->win0.src_act_w + req->win0.x_offset, + req->win0.src_act_h + req->win0.y_offset))) { + pr_err("invalid win0, src[w,h] = [%d, %d], dst[w,h] = [%d, %d], off[x,y] = [%d,%d]\n", + req->win0.src_act_w, req->win0.src_act_h, + req->win0.dst_act_w, req->win0.dst_act_h, + req->win0.x_offset, req->win0.y_offset); + return -EINVAL; + } + + if (unlikely(req->win0.vir_w * rga_get_pixel_stride_from_format(req->win0.format) > + data->max_byte_stride * 8)) { + pr_err("invalid win0 stride, stride = %d, pixel_stride = %d, max_byte_stride = %d\n", + req->win0.vir_w, rga_get_pixel_stride_from_format(req->win0.format), + data->max_byte_stride); + return -EINVAL; + } + + if (unlikely(rga_hw_out_of_range(&(data->output_range), + req->wr.dst_act_w, req->wr.dst_act_h))) { + pr_err("invalid wr, [w,h] = [%d, %d]\n", req->wr.dst_act_w, req->wr.dst_act_h); + return -EINVAL; + } + + if (unlikely(req->wr.vir_w * rga_get_pixel_stride_from_format(req->wr.format) > + data->max_byte_stride * 8)) { + pr_err("invalid wr stride, stride = %d, pixel_stride = %d, max_byte_stride = %d\n", + req->wr.vir_w, rga_get_pixel_stride_from_format(req->wr.format), + data->max_byte_stride); + return -EINVAL; + } + + if (req->win1.yrgb_addr != 0) { + if (unlikely(rga_hw_out_of_range(&(data->input_range), + req->win1.src_act_w, req->win1.src_act_h) || + rga_hw_out_of_range(&(data->input_range), + req->win1.dst_act_w, req->win1.dst_act_h) || + rga_hw_out_of_range(&(data->input_range), + req->win1.src_act_w + req->win1.x_offset, + req->win1.src_act_h + req->win1.y_offset))) { + pr_err("invalid win1, src[w,h] = [%d, %d], dst[w,h] = [%d, %d], off[x,y] = [%d,%d]\n", + req->win1.src_act_w, req->win1.src_act_h, + req->win1.dst_act_w, req->win1.dst_act_h, + req->win1.x_offset, req->win1.y_offset); + return -EINVAL; + } + + if (unlikely(req->win1.vir_w * rga_get_pixel_stride_from_format(req->win1.format) > + data->max_byte_stride * 8)) { + pr_err("invalid win1 stride, stride = %d, pixel_stride = %d, max_byte_stride = %d\n", + req->win1.vir_w, rga_get_pixel_stride_from_format(req->win1.format), + data->max_byte_stride); + return -EINVAL; + } + + /* warning: rotate mode skip this judge */ + if (req->rotate_mode == 0) { + /* check win0 dst size > win1 dst size */ + if (unlikely((req->win1.dst_act_w > req->win0.dst_act_w) || + (req->win1.dst_act_h > req->win0.dst_act_h))) { + pr_err("invalid output param win0[w,h] = [%d, %d], win1[w,h] = [%d, %d]\n", + req->win0.dst_act_w, req->win0.dst_act_h, + req->win1.dst_act_w, req->win1.dst_act_h); + return -EINVAL; + } + } + } + + if (rga3_scale_check(req) < 0) + return -EINVAL; + + return 0; +} + +static void print_debug_info(struct rga3_req *req) +{ + pr_info("render_mode:%s, bitblit_mode=%d, rotate_mode:%x\n", + rga_get_render_mode_str(req->render_mode), req->bitblt_mode, + req->rotate_mode); + pr_info("win0: y = %lx uv = %lx v = %lx src_w = %d src_h = %d\n", + req->win0.yrgb_addr, req->win0.uv_addr, req->win0.v_addr, + req->win0.src_act_w, req->win0.src_act_h); + pr_info("win0: vw = %d vh = %d xoff = %d yoff = %d format = %s\n", + req->win0.vir_w, req->win0.vir_h, + req->win0.x_offset, req->win0.y_offset, + rga_get_format_name(req->win0.format)); + pr_info("win0: dst_w = %d, dst_h = %d, rd_mode = %d\n", + req->win0.dst_act_w, req->win0.dst_act_h, req->win0.rd_mode); + pr_info("win0: rot_mode = %d, en = %d, compact = %d, endian = %d\n", + req->win0.rotate_mode, req->win0.enable, + req->win0.is_10b_compact, req->win0.is_10b_endian); + + if (req->win1.yrgb_addr != 0 || req->win1.uv_addr != 0 + || req->win1.v_addr != 0) { + pr_info("win1: y = %lx uv = %lx v = %lx src_w = %d src_h = %d\n", + req->win1.yrgb_addr, req->win1.uv_addr, + req->win1.v_addr, req->win1.src_act_w, + req->win1.src_act_h); + pr_info("win1: vw = %d vh = %d xoff = %d yoff = %d format = %s\n", + req->win1.vir_w, req->win1.vir_h, + req->win1.x_offset, req->win1.y_offset, + rga_get_format_name(req->win1.format)); + pr_info("win1: dst_w = %d, dst_h = %d, rd_mode = %d\n", + req->win1.dst_act_w, req->win1.dst_act_h, + req->win1.rd_mode); + pr_info("win1: rot_mode = %d, en = %d, compact = %d, endian = %d\n", + req->win1.rotate_mode, req->win1.enable, + req->win1.is_10b_compact, req->win1.is_10b_endian); + } + + pr_info("wr: y = %lx uv = %lx v = %lx vw = %d vh = %d\n", + req->wr.yrgb_addr, req->wr.uv_addr, req->wr.v_addr, + req->wr.vir_w, req->wr.vir_h); + pr_info("wr: ovlp_xoff = %d ovlp_yoff = %d format = %s rdmode = %d\n", + req->wr.x_offset, req->wr.y_offset, + rga_get_format_name(req->wr.format), req->wr.rd_mode); + + pr_info("mmu: win0 = %.2x win1 = %.2x wr = %.2x\n", + req->mmu_info.src0_mmu_flag, req->mmu_info.src1_mmu_flag, + req->mmu_info.dst_mmu_flag); + pr_info("alpha: flag %x mode=%s\n", + req->alpha_rop_flag, rga_get_blend_mode_str(req->alpha_config.mode)); + pr_info("alpha: pre_multi=[%d,%d] pixl=[%d,%d] glb=[%d,%d]\n", + req->alpha_config.fg_pre_multiplied, req->alpha_config.bg_pre_multiplied, + req->alpha_config.fg_pixel_alpha_en, req->alpha_config.bg_pixel_alpha_en, + req->alpha_config.fg_global_alpha_en, req->alpha_config.bg_global_alpha_en); + pr_info("alpha: fg_global_alpha=%x bg_global_alpha=%x\n", + req->alpha_config.fg_global_alpha_value, req->alpha_config.bg_global_alpha_value); + pr_info("yuv2rgb mode is %x\n", req->yuv2rgb_mode); +} + +static int rga3_align_check(struct rga3_req *req) +{ + if (rga_is_yuv10bit_format(req->win0.format)) + if ((req->win0.vir_w % 64) || (req->win0.x_offset % 4) || + (req->win0.src_act_w % 4) || (req->win0.y_offset % 4) || + (req->win0.src_act_h % 4) || (req->win0.vir_h % 2)) + pr_info("yuv10bit err win0 wstride is not align\n"); + if (rga_is_yuv10bit_format(req->win1.format)) + if ((req->win1.vir_w % 64) || (req->win1.x_offset % 4) || + (req->win1.src_act_w % 4) || (req->win1.y_offset % 4) || + (req->win1.src_act_h % 4) || (req->win1.vir_h % 2)) + pr_info("yuv10bit err win1 wstride is not align\n"); + if (rga_is_yuv8bit_format(req->win0.format)) + if ((req->win0.vir_w % 16) || (req->win0.x_offset % 2) || + (req->win0.src_act_w % 2) || (req->win0.y_offset % 2) || + (req->win0.src_act_h % 2) || (req->win0.vir_h % 2)) + pr_info("yuv8bit err win0 wstride is not align\n"); + if (rga_is_yuv8bit_format(req->win1.format)) + if ((req->win1.vir_w % 16) || (req->win1.x_offset % 2) || + (req->win1.src_act_w % 2) || (req->win1.y_offset % 2) || + (req->win1.src_act_h % 2) || (req->win1.vir_h % 2)) + pr_info("yuv8bit err win1 wstride is not align\n"); + return 0; +} + +static int rga3_init_reg(struct rga_job *job) +{ + struct rga3_req req; + int ret = 0; + struct rga_scheduler_t *scheduler = NULL; + ktime_t timestamp = ktime_get(); + + scheduler = job->scheduler; + if (unlikely(scheduler == NULL)) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + + memset(&req, 0x0, sizeof(req)); + + rga_cmd_to_rga3_cmd(&job->rga_command_base, &req); + + /* check value if legal */ + ret = rga3_check_param(scheduler->data, &req); + if (ret == -EINVAL) { + pr_err("req argument is inval\n"); + return ret; + } + + rga3_align_check(&req); + + /* for debug */ + if (DEBUGGER_EN(MSG)) + print_debug_info(&req); + + if (rga3_gen_reg_info((uint8_t *) job->cmd_reg, &req) == -1) { + pr_err("RKA: gen reg info error\n"); + return -EINVAL; + } + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], generate register cost time %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + + return ret; +} + +static void rga3_dump_read_back_reg(struct rga_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + uint32_t cmd_reg[48] = {0}; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + for (i = 0; i < 48; i++) + cmd_reg[i] = rga_read(0x100 + i * 4, scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + pr_info("CMD_READ_BACK_REG\n"); + for (i = 0; i < 12; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", i, + cmd_reg[0 + i * 4], cmd_reg[1 + i * 4], + cmd_reg[2 + i * 4], cmd_reg[3 + i * 4]); +} + +static int rga3_set_reg(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + int i; + bool master_mode_en; + uint32_t sys_ctrl; + ktime_t now = ktime_get(); + + /* + * Currently there is no iova allocated for storing cmd for the IOMMU device, + * so the iommu device needs to use the slave mode. + */ + if (scheduler->data->mmu != RGA_IOMMU) + master_mode_en = true; + else + master_mode_en = false; + + if (DEBUGGER_EN(REG)) { + uint32_t *p; + + p = job->cmd_reg; + pr_info("CMD_REG\n"); + for (i = 0; i < 12; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", i, + p[0 + i * 4], p[1 + i * 4], + p[2 + i * 4], p[3 + i * 4]); + } + + /* All CMD finish int */ + rga_write(m_RGA3_INT_FRM_DONE | m_RGA3_INT_CMD_LINE_FINISH | m_RGA3_INT_ERROR_MASK, + RGA3_INT_EN, scheduler); + + if (master_mode_en) { + /* master mode */ + sys_ctrl = s_RGA3_SYS_CTRL_CMD_MODE(1); + + /* cmd buffer flush cache to ddr */ + rga_dma_sync_flush_range(&job->cmd_reg[0], &job->cmd_reg[50], scheduler); + + rga_write(virt_to_phys(job->cmd_reg), RGA3_CMD_ADDR, scheduler); + rga_write(sys_ctrl, RGA3_SYS_CTRL, scheduler); + rga_write(m_RGA3_CMD_CTRL_CMD_LINE_ST_P, RGA3_CMD_CTRL, scheduler); + } else { + /* slave mode */ + sys_ctrl = s_RGA3_SYS_CTRL_CMD_MODE(0) | m_RGA3_SYS_CTRL_RGA_SART; + + for (i = 0; i <= 50; i++) + rga_write(job->cmd_reg[i], 0x100 + i * 4, scheduler); + + rga_write(sys_ctrl, RGA3_SYS_CTRL, scheduler); + } + + if (DEBUGGER_EN(REG)) { + pr_info("sys_ctrl = 0x%x, int_en = 0x%x, int_raw = 0x%x\n", + rga_read(RGA3_SYS_CTRL, scheduler), + rga_read(RGA3_INT_EN, scheduler), + rga_read(RGA3_INT_RAW, scheduler)); + + pr_info("hw_status = 0x%x, cmd_status = 0x%x\n", + rga_read(RGA3_STATUS0, scheduler), + rga_read(RGA3_CMD_STATE, scheduler)); + } + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], set register cost time %lld us\n", + job->request_id, ktime_us_delta(now, job->timestamp)); + + job->hw_running_time = now; + job->hw_recoder_time = now; + + if (DEBUGGER_EN(REG)) + rga3_dump_read_back_reg(scheduler); + + return 0; +} + +static int rga3_get_version(struct rga_scheduler_t *scheduler) +{ + u32 major_version, minor_version, svn_version; + u32 reg_version; + + if (!scheduler) { + pr_err("scheduler is null\n"); + return -EINVAL; + } + + reg_version = rga_read(RGA3_VERSION_NUM, scheduler); + + major_version = (reg_version & RGA3_MAJOR_VERSION_MASK) >> 28; + minor_version = (reg_version & RGA3_MINOR_VERSION_MASK) >> 20; + svn_version = (reg_version & RGA3_SVN_VERSION_MASK); + + snprintf(scheduler->version.str, 10, "%x.%01x.%05x", major_version, + minor_version, svn_version); + + scheduler->version.major = major_version; + scheduler->version.minor = minor_version; + scheduler->version.revision = svn_version; + + return 0; +} + +static int rga3_irq(struct rga_scheduler_t *scheduler) +{ + struct rga_job *job = scheduler->running_job; + + if (job == NULL) + return IRQ_HANDLED; + + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) + return IRQ_WAKE_THREAD; + + job->intr_status = rga_read(RGA3_INT_RAW, scheduler); + job->hw_status = rga_read(RGA3_STATUS0, scheduler); + job->cmd_status = rga_read(RGA3_CMD_STATE, scheduler); + + if (DEBUGGER_EN(INT_FLAG)) + pr_info("irq handler, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + job->intr_status, job->hw_status, job->cmd_status); + + if (job->intr_status & (m_RGA3_INT_FRM_DONE | m_RGA3_INT_CMD_LINE_FINISH)) { + set_bit(RGA_JOB_STATE_FINISH, &job->state); + } else if (job->intr_status & m_RGA3_INT_ERROR_MASK) { + set_bit(RGA_JOB_STATE_INTR_ERR, &job->state); + + pr_err("irq handler err! INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + job->intr_status, job->hw_status, job->cmd_status); + scheduler->ops->soft_reset(scheduler); + } + + /*clear INTR */ + rga_write(m_RGA3_INT_FRM_DONE | m_RGA3_INT_CMD_LINE_FINISH | m_RGA3_INT_ERROR_MASK, + RGA3_INT_CLR, scheduler); + + return IRQ_WAKE_THREAD; +} + +static int rga3_isr_thread(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + if (DEBUGGER_EN(INT_FLAG)) + pr_info("isr thread, INTR[0x%x], HW_STATUS[0x%x], CMD_STATUS[0x%x]\n", + rga_read(RGA3_INT_RAW, scheduler), + rga_read(RGA3_STATUS0, scheduler), + rga_read(RGA3_CMD_STATE, scheduler)); + + if (test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) { + if (job->intr_status & m_RGA3_INT_RAG_MI_RD_BUS_ERR) { + pr_err("DMA read bus error, please check size of the input_buffer or whether the buffer has been freed.\n"); + job->ret = -EFAULT; + } else if (job->intr_status & m_RGA3_INT_WIN0_FBCD_DEC_ERR) { + pr_err("win0 FBC decoder error, please check the fbc image of the source.\n"); + job->ret = -EFAULT; + } else if (job->intr_status & m_RGA3_INT_WIN1_FBCD_DEC_ERR) { + pr_err("win1 FBC decoder error, please check the fbc image of the source.\n"); + job->ret = -EFAULT; + } else if (job->intr_status & m_RGA3_INT_RGA_MI_WR_BUS_ERR) { + pr_err("wr buss error, please check size of the output_buffer or whether the buffer has been freed.\n"); + job->ret = -EFAULT; + } + + if (job->ret == 0) { + pr_err("rga intr error[0x%x]!\n", job->intr_status); + job->ret = -EFAULT; + } + } + + return IRQ_HANDLED; +} + +const struct rga_backend_ops rga3_ops = { + .get_version = rga3_get_version, + .set_reg = rga3_set_reg, + .init_reg = rga3_init_reg, + .soft_reset = rga3_soft_reset, + .read_back_reg = NULL, + .irq = rga3_irq, + .isr_thread = rga3_isr_thread, +}; diff --git a/drivers/video/rockchip/rga3/rga_common.c b/drivers/video/rockchip/rga3/rga_common.c new file mode 100644 index 000000000..80d4821fb --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_common.c @@ -0,0 +1,772 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Cerf Yu + */ + +#define pr_fmt(fmt) "rga_common: " fmt + +#include "rga.h" +#include "rga_common.h" + +bool rga_is_rgb_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_RGBX_8888: + case RGA_FORMAT_RGB_888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_BGRX_8888: + case RGA_FORMAT_BGR_888: + case RGA_FORMAT_RGB_565: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGR_565: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_XRGB_8888: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_XBGR_8888: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + return true; + default: + return false; + } +} + +bool rga_is_yuv_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_Y4: + case RGA_FORMAT_YCbCr_400: + + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_422_SP: + case RGA_FORMAT_YCrCb_422_P: + case RGA_FORMAT_YCrCb_420_SP: + case RGA_FORMAT_YCrCb_420_P: + + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_422: + case RGA_FORMAT_UYVY_420: + + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + return true; + default: + return false; + } +} + +bool rga_is_alpha_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + return true; + default: + return false; + } +} + +bool rga_is_yuv420_packed_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_420: + return true; + default: + return false; + } +} + +bool rga_is_yuv420_planar_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_420_P: + return true; + default: + return false; + } +} + +bool rga_is_yuv420_semi_planar_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCrCb_420_SP: + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + return true; + default: + return false; + } +} + +bool rga_is_yuv422_packed_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_UYVY_422: + return true; + default: + return false; + } +} + +bool rga_is_yuv422_planar_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_P: + return true; + default: + return false; + } +} + +bool rga_is_yuv422_semi_planar_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCrCb_422_SP: + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + return true; + default: + return false; + } +} + +bool rga_is_yuv8bit_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_Y4: + case RGA_FORMAT_YCbCr_400: + + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_422_SP: + case RGA_FORMAT_YCrCb_422_P: + case RGA_FORMAT_YCrCb_420_SP: + case RGA_FORMAT_YCrCb_420_P: + + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_422: + case RGA_FORMAT_UYVY_420: + return true; + default: + return false; + } +} + +bool rga_is_yuv10bit_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + return true; + default: + return false; + } +} + +bool rga_is_yuv422p_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_P: + return true; + default: + return false; + } +} + +bool rga_is_only_y_format(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_YCbCr_400: + case RGA_FORMAT_Y4: + return true; + default: + return false; + } +} + +const char *rga_get_format_name(uint32_t format) +{ + switch (format) { + case RGA_FORMAT_RGBA_8888: + return "RGBA8888"; + case RGA_FORMAT_RGBX_8888: + return "RGBX8888"; + case RGA_FORMAT_RGB_888: + return "RGB888"; + case RGA_FORMAT_BGRA_8888: + return "BGRA8888"; + case RGA_FORMAT_BGRX_8888: + return "BGRX8888"; + case RGA_FORMAT_BGR_888: + return "BGR888"; + case RGA_FORMAT_RGB_565: + return "RGB565"; + case RGA_FORMAT_RGBA_5551: + return "RGBA5551"; + case RGA_FORMAT_RGBA_4444: + return "RGBA4444"; + case RGA_FORMAT_BGR_565: + return "BGR565"; + case RGA_FORMAT_BGRA_5551: + return "BGRA5551"; + case RGA_FORMAT_BGRA_4444: + return "BGRA4444"; + + case RGA_FORMAT_YCbCr_422_SP: + return "YCbCr422SP"; + case RGA_FORMAT_YCbCr_422_P: + return "YCbCr422P"; + case RGA_FORMAT_YCbCr_420_SP: + return "YCbCr420SP"; + case RGA_FORMAT_YCbCr_420_P: + return "YCbCr420P"; + case RGA_FORMAT_YCrCb_422_SP: + return "YCrCb422SP"; + case RGA_FORMAT_YCrCb_422_P: + return "YCrCb422P"; + case RGA_FORMAT_YCrCb_420_SP: + return "YCrCb420SP"; + case RGA_FORMAT_YCrCb_420_P: + return "YCrCb420P"; + + case RGA_FORMAT_YVYU_422: + return "YVYU422"; + case RGA_FORMAT_YVYU_420: + return "YVYU420"; + case RGA_FORMAT_VYUY_422: + return "VYUY422"; + case RGA_FORMAT_VYUY_420: + return "VYUY420"; + case RGA_FORMAT_YUYV_422: + return "YUYV422"; + case RGA_FORMAT_YUYV_420: + return "YUYV420"; + case RGA_FORMAT_UYVY_422: + return "UYVY422"; + case RGA_FORMAT_UYVY_420: + return "UYVY420"; + + case RGA_FORMAT_YCbCr_420_SP_10B: + return "YCrCb420SP10B"; + case RGA_FORMAT_YCrCb_420_SP_10B: + return "YCbCr420SP10B"; + case RGA_FORMAT_YCbCr_422_SP_10B: + return "YCbCr422SP10B"; + case RGA_FORMAT_YCrCb_422_SP_10B: + return "YCrCb422SP10B"; + case RGA_FORMAT_BPP1: + return "BPP1"; + case RGA_FORMAT_BPP2: + return "BPP2"; + case RGA_FORMAT_BPP4: + return "BPP4"; + case RGA_FORMAT_BPP8: + return "BPP8"; + case RGA_FORMAT_YCbCr_400: + return "YCbCr400"; + case RGA_FORMAT_Y4: + return "y4"; + + case RGA_FORMAT_ARGB_8888: + return "ARGB8888"; + case RGA_FORMAT_XRGB_8888: + return "XRGB8888"; + case RGA_FORMAT_ARGB_5551: + return "ARGB5551"; + case RGA_FORMAT_ARGB_4444: + return "ARGB4444"; + case RGA_FORMAT_ABGR_8888: + return "ABGR8888"; + case RGA_FORMAT_XBGR_8888: + return "XBGR8888"; + case RGA_FORMAT_ABGR_5551: + return "ABGR5551"; + case RGA_FORMAT_ABGR_4444: + return "ABGR4444"; + + case RGA_FORMAT_RGBA_2BPP: + return "RGBA2BPP"; + + default: + return "UNF"; + } +} + +int rga_get_format_bits(uint32_t format) +{ + int bits = 0; + + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_RGBX_8888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_BGRX_8888: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_XRGB_8888: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_XBGR_8888: + bits = 32; + break; + case RGA_FORMAT_RGB_888: + case RGA_FORMAT_BGR_888: + bits = 24; + break; + case RGA_FORMAT_RGB_565: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGR_565: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_SP: + case RGA_FORMAT_YCrCb_422_P: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_UYVY_422: + case RGA_FORMAT_VYUY_422: + /* YUV 420 packed according to the arrangement of YUV422 packed. */ + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_UYVY_420: + case RGA_FORMAT_VYUY_420: + bits = 16; + break; + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_420_SP: + case RGA_FORMAT_YCrCb_420_P: + bits = 12; + break; + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + bits = 15; + break; + case RGA_FORMAT_YCbCr_400: + case RGA_FORMAT_BPP8: + bits = 8; + break; + case RGA_FORMAT_Y4: + case RGA_FORMAT_BPP4: + bits = 4; + break; + case RGA_FORMAT_BPP2: + bits = 2; + break; + case RGA_FORMAT_BPP1: + bits = 1; + break; + default: + pr_err("unknown format [0x%x]\n", format); + return -1; + } + + return bits; +} + +int rga_get_pixel_stride_from_format(uint32_t format) +{ + int pixel_stride = 0; + + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_RGBX_8888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_BGRX_8888: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_XRGB_8888: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_XBGR_8888: + pixel_stride = 32; + break; + case RGA_FORMAT_RGB_888: + case RGA_FORMAT_BGR_888: + pixel_stride = 24; + break; + case RGA_FORMAT_RGB_565: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGR_565: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_422: + case RGA_FORMAT_UYVY_420: + pixel_stride = 16; + break; + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + pixel_stride = 10; + break; + case RGA_FORMAT_BPP1: + case RGA_FORMAT_BPP2: + case RGA_FORMAT_BPP4: + case RGA_FORMAT_BPP8: + case RGA_FORMAT_YCbCr_400: + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_420_SP: + case RGA_FORMAT_YCrCb_420_P: + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_SP: + case RGA_FORMAT_YCrCb_422_P: + pixel_stride = 8; + break; + case RGA_FORMAT_Y4: + pixel_stride = 4; + break; + default: + pr_err("unknown format [0x%x]\n", format); + return -1; + } + + return pixel_stride; +} + +const char *rga_get_render_mode_str(uint8_t mode) +{ + switch (mode) { + case 0x0: + return "bitblt"; + case 0x1: + return "RGA_COLOR_PALETTE"; + case 0x2: + return "RGA_COLOR_FILL"; + case 0x3: + return "update_palette_table"; + case 0x4: + return "update_patten_buff"; + default: + return "UNF"; + } +} + +const char *rga_get_rotate_mode_str(uint8_t mode) +{ + switch (mode) { + case 0x0: + return "0"; + case 0x1: + return "90 degree"; + case 0x2: + return "180 degree"; + case 0x3: + return "270 degree"; + case 0x10: + return "xmirror"; + case 0x20: + return "ymirror"; + case 0x30: + return "xymirror"; + default: + return "UNF"; + } +} + +const char *rga_get_blend_mode_str(enum rga_alpha_blend_mode mode) +{ + switch (mode) { + case RGA_ALPHA_NONE: + return "no blend"; + + case RGA_ALPHA_BLEND_SRC: + return "src"; + + case RGA_ALPHA_BLEND_DST: + return "dst"; + + case RGA_ALPHA_BLEND_SRC_OVER: + return "src-over"; + + case RGA_ALPHA_BLEND_DST_OVER: + return "dst-over"; + + case RGA_ALPHA_BLEND_SRC_IN: + return "src-in"; + + case RGA_ALPHA_BLEND_DST_IN: + return "dst-in"; + + case RGA_ALPHA_BLEND_SRC_OUT: + return "src-out"; + + case RGA_ALPHA_BLEND_DST_OUT: + return "dst-out"; + + case RGA_ALPHA_BLEND_SRC_ATOP: + return "src-atop"; + + case RGA_ALPHA_BLEND_DST_ATOP: + return "dst-atop"; + + case RGA_ALPHA_BLEND_XOR: + return "xor"; + + case RGA_ALPHA_BLEND_CLEAR: + return "clear"; + + default: + return "check reg for more imformation"; + } +} + +const char *rga_get_memory_type_str(uint8_t type) +{ + switch (type) { + case RGA_DMA_BUFFER: + return "dma_fd"; + case RGA_VIRTUAL_ADDRESS: + return "virt_addr"; + case RGA_PHYSICAL_ADDRESS: + return "phys_addr"; + case RGA_DMA_BUFFER_PTR: + return "dma_buf_ptr"; + default: + return "UNF"; + } +} + +const char *rga_get_mmu_type_str(enum rga_mmu mmu_type) +{ + switch (mmu_type) { + case RGA_MMU: + return "RGA_MMU"; + case RGA_IOMMU: + return "RK_IOMMU"; + default: + return "NONE_MMU"; + } +} + +const char *rga_get_core_name(enum RGA_SCHEDULER_CORE core) +{ + switch (core) { + case RGA_SCHEDULER_RGA3_CORE0: + return "RGA3_core0"; + case RGA_SCHEDULER_RGA3_CORE1: + return "RGA3_core1"; + case RGA_SCHEDULER_RGA2_CORE0: + return "RGA2_core0"; + default: + return "unknown_core"; + } +} + +void rga_convert_addr(struct rga_img_info_t *img, bool before_vir_get_channel) +{ + /* + * If it is not using dma fd, the virtual/phyical address is assigned + * to the address of the corresponding channel. + */ + + //img->yrgb_addr = img->uv_addr; + + /* + * if before_vir_get_channel is true, then convert addr by default + * when has iova (before_vir_get_channel is false), + * need to consider whether fbc case + */ + if (img->rd_mode != RGA_FBC_MODE || before_vir_get_channel) { + img->uv_addr = img->yrgb_addr + (img->vir_w * img->vir_h); + + //warning: rga3 may need /2 for all + if (rga_is_yuv422p_format(img->format)) + img->v_addr = + img->uv_addr + (img->vir_w * img->vir_h) / 2; + else + img->v_addr = + img->uv_addr + (img->vir_w * img->vir_h) / 4; + } else { + img->uv_addr = img->yrgb_addr; + img->v_addr = 0; + } +} + +void rga_swap_pd_mode(struct rga_req *req_rga) +{ + if (((req_rga->alpha_rop_flag) & 1)) { + if ((req_rga->alpha_rop_flag >> 3) & 1) { + if (req_rga->PD_mode == 1) + req_rga->PD_mode = 2; + else if (req_rga->PD_mode == 2) + req_rga->PD_mode = 1; + else if (req_rga->PD_mode == 3) + req_rga->PD_mode = 4; + else if (req_rga->PD_mode == 4) + req_rga->PD_mode = 3; + } + } +} + +int rga_image_size_cal(int w, int h, int format, + int *yrgb_size, int *uv_size, int *v_size) +{ + int yrgb = 0; + int uv = 0; + int v = 0; + + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_RGBX_8888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_BGRX_8888: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_XRGB_8888: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_XBGR_8888: + yrgb = w * h * 4; + break; + case RGA_FORMAT_RGB_888: + case RGA_FORMAT_BGR_888: + yrgb = w * h * 3; + break; + case RGA_FORMAT_RGB_565: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGR_565: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_UYVY_422: + /* YUV 420 packed according to the arrangement of YUV422 packed. */ + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_420: + yrgb = w * h * 2; + break; + /* YUV FORMAT */ + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCrCb_422_SP: + /* 10bit format stride is externally configured. */ + case RGA_FORMAT_YCbCr_422_SP_10B: + case RGA_FORMAT_YCrCb_422_SP_10B: + yrgb = w * h; + uv = w * h; + break; + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_P: + yrgb = w * h; + uv = (w * h) >> 1; + v = uv; + break; + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCrCb_420_SP: + /* 10bit format stride is externally configured. */ + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + yrgb = w * h; + uv = (w * h) >> 1; + break; + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_420_P: + yrgb = w * h; + uv = (w * h) >> 2; + v = uv; + break; + case RGA_FORMAT_YCbCr_400: + yrgb = w * h; + break; + case RGA_FORMAT_Y4: + yrgb = (w * h) >> 1; + break; + default: + pr_err("Unsuport format [0x%x]\n", format); + return -EFAULT; + } + + if (yrgb_size != NULL) + *yrgb_size = yrgb; + if (uv_size != NULL) + *uv_size = uv; + if (v_size != NULL) + *v_size = v; + + return (yrgb + uv + v); +} + +void rga_dump_memory_parm(struct rga_memory_parm *parm) +{ + pr_info("memory param: w = %d, h = %d, f = %s(0x%x), size = %d\n", + parm->width, parm->height, rga_get_format_name(parm->format), + parm->format, parm->size); +} + +void rga_dump_external_buffer(struct rga_external_buffer *buffer) +{ + pr_info("external: memory = 0x%lx, type = %s\n", + (unsigned long)buffer->memory, rga_get_memory_type_str(buffer->type)); + rga_dump_memory_parm(&buffer->memory_parm); +} diff --git a/drivers/video/rockchip/rga3/rga_debugger.c b/drivers/video/rockchip/rga3/rga_debugger.c new file mode 100644 index 000000000..fe2103147 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_debugger.c @@ -0,0 +1,970 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + * Huang Lee + */ + +#define pr_fmt(fmt) "rga_debugger: " fmt + +#include +#include +#include +#include +#include +#include + +#include "rga.h" +#include "rga_debugger.h" +#include "rga_drv.h" +#include "rga_mm.h" +#include "rga_common.h" + +#define RGA_DEBUGGER_ROOT_NAME "rkrga" + +#define STR_ENABLE(en) (en ? "EN" : "DIS") + +int RGA_DEBUG_REG; +int RGA_DEBUG_MSG; +int RGA_DEBUG_TIME; +int RGA_DEBUG_INT_FLAG; +int RGA_DEBUG_MM; +int RGA_DEBUG_CHECK_MODE; +int RGA_DEBUG_NONUSE; +int RGA_DEBUG_DEBUG_MODE; +int RGA_DEBUG_DUMP_IMAGE; + +#ifdef CONFIG_NO_GKI +static char g_dump_path[100] = "/data"; +#endif + +static int rga_debug_show(struct seq_file *m, void *data) +{ + seq_printf(m, "REG [%s]\n" + "MSG [%s]\n" + "TIME [%s]\n" + "INT [%s]\n" + "MM [%s]\n" + "CHECK [%s]\n" + "STOP [%s]\n", + STR_ENABLE(RGA_DEBUG_REG), + STR_ENABLE(RGA_DEBUG_MSG), + STR_ENABLE(RGA_DEBUG_TIME), + STR_ENABLE(RGA_DEBUG_INT_FLAG), + STR_ENABLE(RGA_DEBUG_MM), + STR_ENABLE(RGA_DEBUG_CHECK_MODE), + STR_ENABLE(RGA_DEBUG_NONUSE)); + + seq_puts(m, "\nhelp:\n"); + seq_puts(m, " 'echo reg > debug' to enable/disable register log printing.\n"); + seq_puts(m, " 'echo msg > debug' to enable/disable message log printing.\n"); + seq_puts(m, " 'echo time > debug' to enable/disable time log printing.\n"); + seq_puts(m, " 'echo int > debug' to enable/disable interruppt log printing.\n"); + seq_puts(m, " 'echo mm > debug' to enable/disable memory manager log printing.\n"); + seq_puts(m, " 'echo check > debug' to enable/disable check mode.\n"); + seq_puts(m, " 'echo stop > debug' to enable/disable stop using hardware\n"); + + return 0; +} + +static ssize_t rga_debug_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + char buf[14]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + buf[len - 1] = '\0'; + + if (strncmp(buf, "reg", 4) == 0) { + if (RGA_DEBUG_REG) { + RGA_DEBUG_REG = 0; + pr_info("close rga reg!\n"); + } else { + RGA_DEBUG_REG = 1; + pr_info("open rga reg!\n"); + } + } else if (strncmp(buf, "msg", 3) == 0) { + if (RGA_DEBUG_MSG) { + RGA_DEBUG_MSG = 0; + pr_info("close rga test MSG!\n"); + } else { + RGA_DEBUG_MSG = 1; + pr_info("open rga test MSG!\n"); + } + } else if (strncmp(buf, "time", 4) == 0) { + if (RGA_DEBUG_TIME) { + RGA_DEBUG_TIME = 0; + pr_info("close rga test time!\n"); + } else { + RGA_DEBUG_TIME = 1; + pr_info("open rga test time!\n"); + } + } else if (strncmp(buf, "int", 3) == 0) { + if (RGA_DEBUG_INT_FLAG) { + RGA_DEBUG_INT_FLAG = 0; + pr_info("close inturrupt MSG!\n"); + } else { + RGA_DEBUG_INT_FLAG = 1; + pr_info("open inturrupt MSG!\n"); + } + } else if (strncmp(buf, "mm", 2) == 0) { + if (RGA_DEBUG_MM) { + RGA_DEBUG_MM = 0; + pr_info("close rga mm log!\n"); + } else { + RGA_DEBUG_MM = 1; + pr_info("open rga mm log!\n"); + } + } else if (strncmp(buf, "check", 5) == 0) { + if (RGA_DEBUG_CHECK_MODE) { + RGA_DEBUG_CHECK_MODE = 0; + pr_info("close rga check flag!\n"); + } else { + RGA_DEBUG_CHECK_MODE = 1; + pr_info("open rga check flag!\n"); + } + } else if (strncmp(buf, "stop", 4) == 0) { + if (RGA_DEBUG_NONUSE) { + RGA_DEBUG_NONUSE = 0; + pr_info("using rga hardware!\n"); + } else { + RGA_DEBUG_NONUSE = 1; + pr_info("stop using rga hardware!\n"); + } + } else if (strncmp(buf, "debug", 3) == 0) { + if (RGA_DEBUG_DEBUG_MODE) { + RGA_DEBUG_REG = 0; + RGA_DEBUG_MSG = 0; + RGA_DEBUG_TIME = 0; + RGA_DEBUG_INT_FLAG = 0; + + RGA_DEBUG_DEBUG_MODE = 0; + pr_info("close debug mode!\n"); + } else { + RGA_DEBUG_REG = 1; + RGA_DEBUG_MSG = 1; + RGA_DEBUG_TIME = 1; + RGA_DEBUG_INT_FLAG = 1; + + RGA_DEBUG_DEBUG_MODE = 1; + pr_info("open debug mode!\n"); + } + } else if (strncmp(buf, "slt", 3) == 0) { + pr_err("Null"); + } + + return len; +} + +static int rga_version_show(struct seq_file *m, void *data) +{ + seq_printf(m, "%s: v%s\n", DRIVER_DESC, DRIVER_VERSION); + + return 0; +} + +static int rga_load_show(struct seq_file *m, void *data) +{ + struct rga_scheduler_t *scheduler = NULL; + struct rga_session_manager *session_manager = NULL; + struct rga_session *session = NULL; + unsigned long flags; + int id = 0; + int i; + int load; + u32 busy_time_total; + + session_manager = rga_drvdata->session_manager; + + seq_printf(m, "num of scheduler = %d\n", rga_drvdata->num_of_scheduler); + seq_printf(m, "================= load ==================\n"); + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + + seq_printf(m, "scheduler[%d]: %s\n", + i, dev_driver_string(scheduler->dev)); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + busy_time_total = scheduler->timer.busy_time_record; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + load = (busy_time_total * 100 / RGA_LOAD_INTERVAL_US); + if (load > 100) + load = 100; + + seq_printf(m, "\t load = %d%%\n", load); + seq_printf(m, "-----------------------------------\n"); + } + + mutex_lock(&session_manager->lock); + + idr_for_each_entry(&session_manager->ctx_id_idr, session, id) + seq_printf(m, "\t process %d: pid = %d, name: %s\n", id, + session->tgid, session->pname); + + mutex_unlock(&session_manager->lock); + + return 0; +} + +static int rga_scheduler_show(struct seq_file *m, void *data) +{ + struct rga_scheduler_t *scheduler = NULL; + int i; + + seq_printf(m, "num of scheduler = %d\n", rga_drvdata->num_of_scheduler); + seq_printf(m, "===================================\n"); + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + + seq_printf(m, "scheduler[%d]: %s\n", + i, dev_driver_string(scheduler->dev)); + seq_printf(m, "-----------------------------------\n"); + seq_printf(m, "pd_ref = %d\n", scheduler->pd_refcount); + } + + return 0; +} + +static int rga_mm_session_show(struct seq_file *m, void *data) +{ + int id; + struct rga_mm *mm_session = NULL; + struct rga_internal_buffer *dump_buffer; + + mm_session = rga_drvdata->mm; + + mutex_lock(&mm_session->lock); + + seq_puts(m, "rga_mm dump:\n"); + seq_printf(m, "buffer count = %d\n", mm_session->buffer_count); + seq_puts(m, "===============================================================\n"); + + idr_for_each_entry(&mm_session->memory_idr, dump_buffer, id) { + seq_printf(m, "handle = %d refcount = %d mm_flag = 0x%x tgid = %d\n", + dump_buffer->handle, kref_read(&dump_buffer->refcount), + dump_buffer->mm_flag, dump_buffer->session->tgid); + + switch (dump_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + if (rga_mm_is_invalid_dma_buffer(dump_buffer->dma_buffer)) + break; + + seq_puts(m, "dma_buffer:\n"); + seq_printf(m, "\t dma_buf = %p, iova = 0x%lxsgt = 0x%p, size = %ld, map_core = 0x%x\n", + dump_buffer->dma_buffer->dma_buf, + (unsigned long)dump_buffer->dma_buffer->iova, + dump_buffer->dma_buffer->sgt, + dump_buffer->dma_buffer->size, + dump_buffer->dma_buffer->scheduler->core); + + if (dump_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + seq_printf(m, "\t is contiguous, pa = 0x%lx\n", + (unsigned long)dump_buffer->phys_addr); + + break; + case RGA_VIRTUAL_ADDRESS: + if (dump_buffer->virt_addr == NULL) + break; + seq_puts(m, "virtual address:\n"); + seq_printf(m, "\t va = 0x%lx, pages = 0x%p, size = %ld\n", + (unsigned long)dump_buffer->virt_addr->addr, + dump_buffer->virt_addr->pages, + dump_buffer->virt_addr->size); + + if (rga_mm_is_invalid_dma_buffer(dump_buffer->dma_buffer)) + break; + + seq_printf(m, "\t iova = 0x%lx, offset = 0x%lx, sgt = 0x%p, size = %ld, map_core = 0x%x\n", + (unsigned long)dump_buffer->dma_buffer->iova, + (unsigned long)dump_buffer->dma_buffer->offset, + dump_buffer->dma_buffer->sgt, + dump_buffer->dma_buffer->size, + dump_buffer->dma_buffer->scheduler->core); + + if (dump_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + seq_printf(m, "\t is contiguous, pa = 0x%lx\n", + (unsigned long)dump_buffer->phys_addr); + + break; + case RGA_PHYSICAL_ADDRESS: + seq_puts(m, "physical address:\n"); + seq_printf(m, "\t pa = 0x%lx\n", (unsigned long)dump_buffer->phys_addr); + break; + default: + seq_puts(m, "Illegal external buffer!\n"); + break; + } + + seq_puts(m, "---------------------------------------------------------------\n"); + } + mutex_unlock(&mm_session->lock); + + return 0; +} + +static int rga_request_manager_show(struct seq_file *m, void *data) +{ + int id, i; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + struct rga_req *task_list; + unsigned long flags; + int task_count = 0; + int finished_task_count = 0, failed_task_count = 0; + + request_manager = rga_drvdata->pend_request_manager; + + seq_puts(m, "rga internal request dump:\n"); + seq_printf(m, "request count = %d\n", request_manager->request_count); + seq_puts(m, "===============================================================\n"); + + mutex_lock(&request_manager->lock); + + idr_for_each_entry(&request_manager->request_idr, request, id) { + seq_printf(m, "------------------ request: %d ------------------\n", request->id); + + spin_lock_irqsave(&request->lock, flags); + + task_count = request->task_count; + finished_task_count = request->finished_task_count; + failed_task_count = request->failed_task_count; + task_list = request->task_list; + + spin_unlock_irqrestore(&request->lock, flags); + + if (task_list == NULL) { + seq_puts(m, "\t can not find task list from id\n"); + continue; + } + + seq_printf(m, "\t set cmd num: %d, finish job: %d, failed job: %d, flags = 0x%x, ref = %d\n", + task_count, finished_task_count, failed_task_count, + request->flags, kref_read(&request->refcount)); + + seq_puts(m, "\t cmd dump:\n\n"); + + for (i = 0; i < request->task_count; i++) + rga_request_task_debug_info(m, &(task_list[i])); + } + + mutex_unlock(&request_manager->lock); + + return 0; +} + +#ifdef CONFIG_NO_GKI +static int rga_dump_path_show(struct seq_file *m, void *data) +{ + seq_printf(m, "dump path: %s\n", g_dump_path); + + return 0; +} + +static ssize_t rga_dump_path_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + char buf[100]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + buf[len - 1] = '\0'; + + snprintf(g_dump_path, sizeof(buf), "%s", buf); + pr_info("dump path change to: %s\n", g_dump_path); + + return len; +} + +static int rga_dump_image_show(struct seq_file *m, void *data) +{ + seq_printf(m, "dump image count: %d\n", RGA_DEBUG_DUMP_IMAGE); + + return 0; +} + +static ssize_t rga_dump_image_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + int ret; + int dump_count = 0; + char buf[14]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + buf[len - 1] = '\0'; + + ret = kstrtoint(buf, 10, &dump_count); + if (ret) { + pr_err("Failed to parse str[%s]\n", buf); + return -EFAULT; + } + + if (dump_count <= 0) { + pr_err("dump_image count is invalid [%d]!\n", dump_count); + return -EINVAL; + } + + RGA_DEBUG_DUMP_IMAGE = dump_count; + pr_info("dump image %d\n", RGA_DEBUG_DUMP_IMAGE); + + return len; +} +#endif /* #ifdef CONFIG_NO_GKI */ + +static int rga_hardware_show(struct seq_file *m, void *data) +{ + struct rga_scheduler_t *scheduler = NULL; + const struct rga_hw_data *hw_data = NULL; + int i; + + seq_puts(m, "===================================\n"); + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + hw_data = scheduler->data; + + seq_printf(m, "%s, core %d: version: %s\n", + dev_driver_string(scheduler->dev), + scheduler->core, scheduler->version.str); + seq_printf(m, "input range: %dx%d ~ %dx%d\n", + hw_data->input_range.min.width, hw_data->input_range.min.height, + hw_data->input_range.max.width, hw_data->input_range.max.height); + seq_printf(m, "output range: %dx%d ~ %dx%d\n", + hw_data->output_range.min.width, hw_data->output_range.min.height, + hw_data->output_range.max.width, hw_data->output_range.max.height); + seq_printf(m, "scale limit: 1/%d ~ %d\n", + (1 << hw_data->max_downscale_factor), + (1 << hw_data->max_upscale_factor)); + seq_printf(m, "byte_stride_align: %d\n", hw_data->byte_stride_align); + seq_printf(m, "max_byte_stride: %d\n", hw_data->max_byte_stride); + seq_printf(m, "csc: RGB2YUV 0x%x YUV2RGB 0x%x\n", + hw_data->csc_r2y_mode, hw_data->csc_y2r_mode); + seq_printf(m, "feature: 0x%x\n", hw_data->feature); + seq_printf(m, "mmu: %s\n", rga_get_mmu_type_str(hw_data->mmu)); + seq_puts(m, "-----------------------------------\n"); + } + + return 0; +} + +static struct rga_debugger_list rga_debugger_root_list[] = { + {"debug", rga_debug_show, rga_debug_write, NULL}, + {"driver_version", rga_version_show, NULL, NULL}, + {"load", rga_load_show, NULL, NULL}, + {"scheduler_status", rga_scheduler_show, NULL, NULL}, + {"mm_session", rga_mm_session_show, NULL, NULL}, + {"request_manager", rga_request_manager_show, NULL, NULL}, +#ifdef CONFIG_NO_GKI + {"dump_path", rga_dump_path_show, rga_dump_path_write, NULL}, + {"dump_image", rga_dump_image_show, rga_dump_image_write, NULL}, +#endif + {"hardware", rga_hardware_show, NULL, NULL}, +}; + +static ssize_t rga_debugger_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *priv = file->private_data; + struct rga_debugger_node *node = priv->private; + + if (node->info_ent->write) + return node->info_ent->write(file, ubuf, len, offp); + else + return len; +} + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS +static int rga_debugfs_open(struct inode *inode, struct file *file) +{ + struct rga_debugger_node *node = inode->i_private; + + return single_open(file, node->info_ent->show, node); +} + +static const struct file_operations rga_debugfs_fops = { + .owner = THIS_MODULE, + .open = rga_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = rga_debugger_write, +}; + +static int rga_debugfs_remove_files(struct rga_debugger *debugger) +{ + struct rga_debugger_node *pos, *q; + struct list_head *entry_list; + + mutex_lock(&debugger->debugfs_lock); + + /* Delete debugfs entry list */ + entry_list = &debugger->debugfs_entry_list; + list_for_each_entry_safe(pos, q, entry_list, list) { + if (pos->dent == NULL) + continue; + list_del(&pos->list); + kfree(pos); + pos = NULL; + } + + /* Delete all debugfs node in this directory */ + debugfs_remove_recursive(debugger->debugfs_dir); + debugger->debugfs_dir = NULL; + + mutex_unlock(&debugger->debugfs_lock); + + return 0; +} + +static int rga_debugfs_create_files(const struct rga_debugger_list *files, + int count, struct dentry *root, + struct rga_debugger *debugger) +{ + int i; + struct dentry *ent; + struct rga_debugger_node *tmp; + + for (i = 0; i < count; i++) { + tmp = kmalloc(sizeof(struct rga_debugger_node), GFP_KERNEL); + if (tmp == NULL) { + pr_err("Cannot alloc node path /sys/kernel/debug/%pd/%s\n", + root, files[i].name); + goto MALLOC_FAIL; + } + + tmp->info_ent = &files[i]; + tmp->debugger = debugger; + + ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, + root, tmp, &rga_debugfs_fops); + if (!ent) { + pr_err("Cannot create /sys/kernel/debug/%pd/%s\n", root, + files[i].name); + goto CREATE_FAIL; + } + + tmp->dent = ent; + + mutex_lock(&debugger->debugfs_lock); + list_add_tail(&tmp->list, &debugger->debugfs_entry_list); + mutex_unlock(&debugger->debugfs_lock); + } + + return 0; + +CREATE_FAIL: + kfree(tmp); +MALLOC_FAIL: + rga_debugfs_remove_files(debugger); + + return -1; +} + +int rga_debugfs_remove(void) +{ + struct rga_debugger *debugger; + + debugger = rga_drvdata->debugger; + + rga_debugfs_remove_files(debugger); + + return 0; +} + +int rga_debugfs_init(void) +{ + int ret; + struct rga_debugger *debugger; + + debugger = rga_drvdata->debugger; + + debugger->debugfs_dir = + debugfs_create_dir(RGA_DEBUGGER_ROOT_NAME, NULL); + if (IS_ERR_OR_NULL(debugger->debugfs_dir)) { + pr_err("failed on mkdir /sys/kernel/debug/%s\n", + RGA_DEBUGGER_ROOT_NAME); + debugger->debugfs_dir = NULL; + return -EIO; + } + + ret = rga_debugfs_create_files(rga_debugger_root_list, ARRAY_SIZE(rga_debugger_root_list), + debugger->debugfs_dir, debugger); + if (ret) { + pr_err("Could not install rga_debugger_root_list debugfs\n"); + goto CREATE_FAIL; + } + + return 0; + +CREATE_FAIL: + rga_debugfs_remove(); + + return ret; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS */ + +#ifdef CONFIG_ROCKCHIP_RGA_PROC_FS +static int rga_procfs_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + struct rga_debugger_node *node = pde_data(inode); +#else + struct rga_debugger_node *node = PDE_DATA(inode); +#endif + + return single_open(file, node->info_ent->show, node); +} + +static const struct proc_ops rga_procfs_fops = { + .proc_open = rga_procfs_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = rga_debugger_write, +}; + +static int rga_procfs_remove_files(struct rga_debugger *debugger) +{ + struct rga_debugger_node *pos, *q; + struct list_head *entry_list; + + mutex_lock(&debugger->procfs_lock); + + /* Delete procfs entry list */ + entry_list = &debugger->procfs_entry_list; + list_for_each_entry_safe(pos, q, entry_list, list) { + if (pos->pent == NULL) + continue; + list_del(&pos->list); + kfree(pos); + pos = NULL; + } + + /* Delete all procfs node in this directory */ + proc_remove(debugger->procfs_dir); + debugger->procfs_dir = NULL; + + mutex_unlock(&debugger->procfs_lock); + + return 0; +} + +static int rga_procfs_create_files(const struct rga_debugger_list *files, + int count, struct proc_dir_entry *root, + struct rga_debugger *debugger) +{ + int i; + struct proc_dir_entry *ent; + struct rga_debugger_node *tmp; + + for (i = 0; i < count; i++) { + tmp = kmalloc(sizeof(struct rga_debugger_node), GFP_KERNEL); + if (tmp == NULL) { + pr_err("Cannot alloc node path for /proc/%s/%s\n", + RGA_DEBUGGER_ROOT_NAME, files[i].name); + goto MALLOC_FAIL; + } + + tmp->info_ent = &files[i]; + tmp->debugger = debugger; + + ent = proc_create_data(files[i].name, S_IFREG | S_IRUGO, + root, &rga_procfs_fops, tmp); + if (!ent) { + pr_err("Cannot create /proc/%s/%s\n", + RGA_DEBUGGER_ROOT_NAME, files[i].name); + goto CREATE_FAIL; + } + + tmp->pent = ent; + + mutex_lock(&debugger->procfs_lock); + list_add_tail(&tmp->list, &debugger->procfs_entry_list); + mutex_unlock(&debugger->procfs_lock); + } + + return 0; + +CREATE_FAIL: + kfree(tmp); +MALLOC_FAIL: + rga_procfs_remove_files(debugger); + return -1; +} + +int rga_procfs_remove(void) +{ + struct rga_debugger *debugger; + + debugger = rga_drvdata->debugger; + + rga_procfs_remove_files(debugger); + + return 0; +} + +int rga_procfs_init(void) +{ + int ret; + struct rga_debugger *debugger; + + debugger = rga_drvdata->debugger; + + debugger->procfs_dir = proc_mkdir(RGA_DEBUGGER_ROOT_NAME, NULL); + if (IS_ERR_OR_NULL(debugger->procfs_dir)) { + pr_err("failed on mkdir /proc/%s\n", RGA_DEBUGGER_ROOT_NAME); + debugger->procfs_dir = NULL; + return -EIO; + } + + ret = rga_procfs_create_files(rga_debugger_root_list, ARRAY_SIZE(rga_debugger_root_list), + debugger->procfs_dir, debugger); + if (ret) { + pr_err("Could not install rga_debugger_root_list procfs\n"); + goto CREATE_FAIL; + } + + return 0; + +CREATE_FAIL: + rga_procfs_remove(); + + return ret; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RGA_PROC_FS */ + +void rga_request_task_debug_info(struct seq_file *m, struct rga_req *req) +{ + seq_printf(m, "\t\t rotate_mode = %d\n", req->rotate_mode); + seq_printf(m, "\t\t src: y = %lx uv = %lx v = %lx aw = %d ah = %d vw = %d vh = %d\n", + (unsigned long)req->src.yrgb_addr, (unsigned long)req->src.uv_addr, + (unsigned long)req->src.v_addr, req->src.act_w, req->src.act_h, + req->src.vir_w, req->src.vir_h); + seq_printf(m, "\t\t src: xoff = %d, yoff = %d, format = 0x%x, rd_mode = %d\n", + req->src.x_offset, req->src.y_offset, req->src.format, req->src.rd_mode); + + if (req->pat.yrgb_addr != 0 || req->pat.uv_addr != 0 + || req->pat.v_addr != 0) { + seq_printf(m, "\t\t pat: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->pat.yrgb_addr, (unsigned long)req->pat.uv_addr, + (unsigned long)req->pat.v_addr, req->pat.act_w, req->pat.act_h, + req->pat.vir_w, req->pat.vir_h); + seq_printf(m, "\t\t xoff = %d yoff = %d, format = 0x%x, rd_mode = %d\n", + req->pat.x_offset, req->pat.y_offset, req->pat.format, req->pat.rd_mode); + } + + seq_printf(m, "\t\t dst: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->dst.yrgb_addr, (unsigned long)req->dst.uv_addr, + (unsigned long)req->dst.v_addr, req->dst.act_w, req->dst.act_h, + req->dst.vir_w, req->dst.vir_h); + seq_printf(m, "\t\t dst: xoff = %d, yoff = %d, format = 0x%x, rd_mode = %d\n", + req->dst.x_offset, req->dst.y_offset, req->dst.format, req->dst.rd_mode); + + seq_printf(m, "\t\t mmu: mmu_flag=%x en=%x\n", + req->mmu_info.mmu_flag, req->mmu_info.mmu_en); + seq_printf(m, "\t\t alpha: rop_mode = %x\n", req->alpha_rop_mode); + seq_printf(m, "\t\t yuv2rgb mode is %x\n", req->yuv2rgb_mode); + seq_printf(m, "\t\t set core = %d, priority = %d, in_fence_fd = %d\n", + req->core, req->priority, req->in_fence_fd); +} + +void rga_cmd_print_debug_info(struct rga_req *req) +{ + pr_info("render_mode = %d, bitblit_mode=%d, rotate_mode = %d\n", + req->render_mode, req->bsfilter_flag, + req->rotate_mode); + + pr_info("src: y = %lx uv = %lx v = %lx aw = %d ah = %d vw = %d vh = %d\n", + (unsigned long)req->src.yrgb_addr, + (unsigned long)req->src.uv_addr, + (unsigned long)req->src.v_addr, + req->src.act_w, req->src.act_h, + req->src.vir_w, req->src.vir_h); + pr_info("src: xoff = %d, yoff = %d, format = 0x%x, rd_mode = %d\n", + req->src.x_offset, req->src.y_offset, + req->src.format, req->src.rd_mode); + + if (req->pat.yrgb_addr != 0 || req->pat.uv_addr != 0 + || req->pat.v_addr != 0) { + pr_info("pat: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->pat.yrgb_addr, + (unsigned long)req->pat.uv_addr, + (unsigned long)req->pat.v_addr, + req->pat.act_w, req->pat.act_h, + req->pat.vir_w, req->pat.vir_h); + pr_info("pat: xoff = %d yoff = %d, format = 0x%x, rd_mode = %d\n", + req->pat.x_offset, req->pat.y_offset, + req->pat.format, req->pat.rd_mode); + } + + pr_info("dst: y=%lx uv=%lx v=%lx aw=%d ah=%d vw=%d vh=%d\n", + (unsigned long)req->dst.yrgb_addr, + (unsigned long)req->dst.uv_addr, + (unsigned long)req->dst.v_addr, + req->dst.act_w, req->dst.act_h, + req->dst.vir_w, req->dst.vir_h); + pr_info("dst: xoff = %d, yoff = %d, format = 0x%x, rd_mode = %d\n", + req->dst.x_offset, req->dst.y_offset, + req->dst.format, req->dst.rd_mode); + + pr_info("mmu: mmu_flag=%x en=%x\n", + req->mmu_info.mmu_flag, req->mmu_info.mmu_en); + pr_info("alpha: rop_mode = %x\n", req->alpha_rop_mode); + pr_info("yuv2rgb mode is %x\n", req->yuv2rgb_mode); + pr_info("set core = %d, priority = %d, in_fence_fd = %d\n", + req->core, req->priority, req->in_fence_fd); +} + +#ifdef CONFIG_NO_GKI +static int rga_dump_image_to_file(struct rga_internal_buffer *dump_buffer, + const char *channel_name, + int plane_id, + int core) +{ + char file_name[100]; + struct file *file; + size_t size = 0; + loff_t pos = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + int ret; + struct iosys_map map; +#endif + void *kvaddr = NULL; + void *kvaddr_origin = NULL; + + switch (dump_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + if (IS_ERR_OR_NULL(dump_buffer->dma_buffer->dma_buf)) { + pr_err("Failed to dump dma_buf 0x%px\n", + dump_buffer->dma_buffer->dma_buf); + return -EINVAL; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + ret = dma_buf_vmap(dump_buffer->dma_buffer->dma_buf, &map); + kvaddr = ret ? NULL : map.vaddr; +#else + kvaddr = dma_buf_vmap(dump_buffer->dma_buffer->dma_buf); +#endif + if (!kvaddr) { + pr_err("can't vmap the dma buffer!\n"); + return -EINVAL; + } + + kvaddr_origin = kvaddr; + kvaddr += dump_buffer->dma_buffer->offset; + break; + case RGA_VIRTUAL_ADDRESS: + kvaddr = vmap(dump_buffer->virt_addr->pages, dump_buffer->virt_addr->page_count, + VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + if (!kvaddr) { + pr_err("dump_vaddr vmap error!, 0x%lx\n", + (unsigned long)dump_buffer->virt_addr->addr); + return -EFAULT; + } + + kvaddr_origin = kvaddr; + kvaddr += dump_buffer->virt_addr->offset; + break; + case RGA_PHYSICAL_ADDRESS: + kvaddr = phys_to_virt(dump_buffer->phys_addr); + break; + default: + pr_err("unsupported memory type[%x]\n", dump_buffer->type); + return -EINVAL; + } + + size = dump_buffer->size; + + if (kvaddr == NULL) { + pr_err("dump addr is NULL!\n"); + return -EFAULT; + } + + if (size <= 0) { + pr_err("dump buffer size[%lx] is invalid!\n", (unsigned long)size); + return -EFAULT; + } + + if (dump_buffer->memory_parm.width == 0 && + dump_buffer->memory_parm.height == 0) + snprintf(file_name, 100, "%s/%d_core%d_%s_plane%d_%s_size%zu_%s.bin", + g_dump_path, + RGA_DEBUG_DUMP_IMAGE, core, channel_name, plane_id, + rga_get_memory_type_str(dump_buffer->type), + size, + rga_get_format_name(dump_buffer->memory_parm.format)); + else + snprintf(file_name, 100, "%s/%d_core%d_%s_plane%d_%s_w%d_h%d_%s.bin", + g_dump_path, + RGA_DEBUG_DUMP_IMAGE, core, channel_name, plane_id, + rga_get_memory_type_str(dump_buffer->type), + dump_buffer->memory_parm.width, + dump_buffer->memory_parm.height, + rga_get_format_name(dump_buffer->memory_parm.format)); + + file = filp_open(file_name, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (!IS_ERR(file)) { + kernel_write(file, kvaddr, size, &pos); + pr_info("dump image to: %s\n", file_name); + fput(file); + } else { + pr_info("open %s failed\n", file_name); + } + + switch (dump_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + dma_buf_vunmap(dump_buffer->dma_buffer->dma_buf, &map); +#else + dma_buf_vunmap(dump_buffer->dma_buffer->dma_buf, kvaddr_origin); +#endif + break; + case RGA_VIRTUAL_ADDRESS: + vunmap(kvaddr_origin); + break; + } + + return 0; +} + +static inline void rga_dump_channel_image(struct rga_job_buffer *job_buffer, + const char *channel_name, + int core) +{ + if (job_buffer->y_addr) + rga_dump_image_to_file(job_buffer->y_addr, channel_name, 0, core); + if (job_buffer->uv_addr) + rga_dump_image_to_file(job_buffer->uv_addr, channel_name, 1, core); + if (job_buffer->v_addr) + rga_dump_image_to_file(job_buffer->v_addr, channel_name, 2, core); +} + +void rga_dump_job_image(struct rga_job *dump_job) +{ + rga_dump_channel_image(&dump_job->src_buffer, "src", dump_job->core); + rga_dump_channel_image(&dump_job->src1_buffer, "src1", dump_job->core); + rga_dump_channel_image(&dump_job->dst_buffer, "dst", dump_job->core); + rga_dump_channel_image(&dump_job->els_buffer, "els", dump_job->core); + + if (RGA_DEBUG_DUMP_IMAGE > 0) + RGA_DEBUG_DUMP_IMAGE--; +} +#endif /* #ifdef CONFIG_NO_GKI */ diff --git a/drivers/video/rockchip/rga3/rga_dma_buf.c b/drivers/video/rockchip/rga3/rga_dma_buf.c new file mode 100644 index 000000000..3032ad475 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_dma_buf.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga_dma_buf: " fmt + +#include "rga_dma_buf.h" +#include "rga.h" +#include "rga_common.h" +#include "rga_job.h" +#include "rga_debugger.h" + +static int rga_dma_info_to_prot(enum dma_data_direction dir) +{ + switch (dir) { + case DMA_BIDIRECTIONAL: + return IOMMU_READ | IOMMU_WRITE; + case DMA_TO_DEVICE: + return IOMMU_READ; + case DMA_FROM_DEVICE: + return IOMMU_WRITE; + default: + return 0; + } +} + +int rga_buf_size_cal(unsigned long yrgb_addr, unsigned long uv_addr, + unsigned long v_addr, int format, uint32_t w, + uint32_t h, unsigned long *StartAddr, unsigned long *size) +{ + uint32_t size_yrgb = 0; + uint32_t size_uv = 0; + uint32_t size_v = 0; + uint32_t stride = 0; + unsigned long start, end; + uint32_t pageCount; + + switch (format) { + case RGA_FORMAT_RGBA_8888: + case RGA_FORMAT_RGBX_8888: + case RGA_FORMAT_BGRA_8888: + case RGA_FORMAT_BGRX_8888: + case RGA_FORMAT_ARGB_8888: + case RGA_FORMAT_XRGB_8888: + case RGA_FORMAT_ABGR_8888: + case RGA_FORMAT_XBGR_8888: + stride = (w * 4 + 3) & (~3); + size_yrgb = stride * h; + start = yrgb_addr >> PAGE_SHIFT; + end = yrgb_addr + size_yrgb; + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_RGB_888: + case RGA_FORMAT_BGR_888: + stride = (w * 3 + 3) & (~3); + size_yrgb = stride * h; + start = yrgb_addr >> PAGE_SHIFT; + end = yrgb_addr + size_yrgb; + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_RGB_565: + case RGA_FORMAT_RGBA_5551: + case RGA_FORMAT_RGBA_4444: + case RGA_FORMAT_BGR_565: + case RGA_FORMAT_BGRA_5551: + case RGA_FORMAT_BGRA_4444: + case RGA_FORMAT_ARGB_5551: + case RGA_FORMAT_ARGB_4444: + case RGA_FORMAT_ABGR_5551: + case RGA_FORMAT_ABGR_4444: + stride = (w * 2 + 3) & (~3); + size_yrgb = stride * h; + start = yrgb_addr >> PAGE_SHIFT; + end = yrgb_addr + size_yrgb; + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + + /* YUV FORMAT */ + case RGA_FORMAT_YCbCr_422_SP: + case RGA_FORMAT_YCrCb_422_SP: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = stride * h; + start = min(yrgb_addr, uv_addr); + start >>= PAGE_SHIFT; + end = max((yrgb_addr + size_yrgb), (uv_addr + size_uv)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YCbCr_422_P: + case RGA_FORMAT_YCrCb_422_P: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = ((stride >> 1) * h); + size_v = ((stride >> 1) * h); + start = min3(yrgb_addr, uv_addr, v_addr); + start = start >> PAGE_SHIFT; + end = + max3((yrgb_addr + size_yrgb), (uv_addr + size_uv), + (v_addr + size_v)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YCbCr_420_SP: + case RGA_FORMAT_YCrCb_420_SP: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = (stride * (h >> 1)); + start = min(yrgb_addr, uv_addr); + start >>= PAGE_SHIFT; + end = max((yrgb_addr + size_yrgb), (uv_addr + size_uv)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YCbCr_420_P: + case RGA_FORMAT_YCrCb_420_P: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = ((stride >> 1) * (h >> 1)); + size_v = ((stride >> 1) * (h >> 1)); + start = min3(yrgb_addr, uv_addr, v_addr); + start >>= PAGE_SHIFT; + end = + max3((yrgb_addr + size_yrgb), (uv_addr + size_uv), + (v_addr + size_v)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YCbCr_400: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + start = yrgb_addr >> PAGE_SHIFT; + end = yrgb_addr + size_yrgb; + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_Y4: + stride = ((w + 3) & (~3)) >> 1; + size_yrgb = stride * h; + start = yrgb_addr >> PAGE_SHIFT; + end = yrgb_addr + size_yrgb; + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YVYU_422: + case RGA_FORMAT_VYUY_422: + case RGA_FORMAT_YUYV_422: + case RGA_FORMAT_UYVY_422: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = stride * h; + start = min(yrgb_addr, uv_addr); + start >>= PAGE_SHIFT; + end = max((yrgb_addr + size_yrgb), (uv_addr + size_uv)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YVYU_420: + case RGA_FORMAT_VYUY_420: + case RGA_FORMAT_YUYV_420: + case RGA_FORMAT_UYVY_420: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = (stride * (h >> 1)); + start = min(yrgb_addr, uv_addr); + start >>= PAGE_SHIFT; + end = max((yrgb_addr + size_yrgb), (uv_addr + size_uv)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + case RGA_FORMAT_YCbCr_420_SP_10B: + case RGA_FORMAT_YCrCb_420_SP_10B: + stride = (w + 3) & (~3); + size_yrgb = stride * h; + size_uv = (stride * (h >> 1)); + start = min(yrgb_addr, uv_addr); + start >>= PAGE_SHIFT; + end = max((yrgb_addr + size_yrgb), (uv_addr + size_uv)); + end = (end + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pageCount = end - start; + break; + default: + pageCount = 0; + start = 0; + break; + } + + *StartAddr = start; + + if (size != NULL) + *size = size_yrgb + size_uv + size_v; + + return pageCount; +} + +static dma_addr_t rga_iommu_dma_alloc_iova(struct iommu_domain *domain, + size_t size, u64 dma_limit, + struct device *dev) +{ + struct rga_iommu_dma_cookie *cookie = (void *)domain->iova_cookie; + struct iova_domain *iovad = &cookie->iovad; + unsigned long shift, iova_len, iova = 0; + + shift = iova_shift(iovad); + iova_len = size >> shift; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) + /* + * Freeing non-power-of-two-sized allocations back into the IOVA caches + * will come back to bite us badly, so we have to waste a bit of space + * rounding up anything cacheable to make sure that can't happen. The + * order of the unadjusted size will still match upon freeing. + */ + if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1))) + iova_len = roundup_pow_of_two(iova_len); +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit); +#else + if (dev->bus_dma_mask) + dma_limit &= dev->bus_dma_mask; +#endif + + if (domain->geometry.force_aperture) + dma_limit = min(dma_limit, (u64)domain->geometry.aperture_end); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 19, 111) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + iova = alloc_iova_fast(iovad, iova_len, + min_t(dma_addr_t, dma_limit >> shift, iovad->end_pfn), + true); +#else + iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift, true); +#endif + + return (dma_addr_t)iova << shift; +} + +static void rga_iommu_dma_free_iova(struct iommu_domain *domain, + dma_addr_t iova, size_t size) +{ + struct rga_iommu_dma_cookie *cookie = (void *)domain->iova_cookie; + struct iova_domain *iovad = &cookie->iovad; + + free_iova_fast(iovad, iova_pfn(iovad, iova), size >> iova_shift(iovad)); +} + +static inline struct iommu_domain *rga_iommu_get_dma_domain(struct device *dev) +{ + return iommu_get_domain_for_dev(dev); +} + +void rga_iommu_unmap(struct rga_dma_buffer *buffer) +{ + if (buffer == NULL) + return; + if (buffer->iova == 0) + return; + + iommu_unmap(buffer->domain, buffer->iova, buffer->size); + rga_iommu_dma_free_iova(buffer->domain, buffer->iova, buffer->size); +} + +int rga_iommu_map_sgt(struct sg_table *sgt, size_t size, + struct rga_dma_buffer *buffer, + struct device *rga_dev) +{ + struct iommu_domain *domain = NULL; + struct rga_iommu_dma_cookie *cookie; + struct iova_domain *iovad; + dma_addr_t iova; + size_t map_size; + unsigned long align_size; + + if (sgt == NULL) { + pr_err("can not map iommu, because sgt is null!\n"); + return -EINVAL; + } + + domain = rga_iommu_get_dma_domain(rga_dev); + cookie = (void *)domain->iova_cookie; + iovad = &cookie->iovad; + align_size = iova_align(iovad, size); + + if (DEBUGGER_EN(MSG)) + pr_info("iova_align size = %ld", align_size); + + iova = rga_iommu_dma_alloc_iova(domain, align_size, rga_dev->coherent_dma_mask, rga_dev); + if (!iova) { + pr_err("rga_iommu_dma_alloc_iova failed"); + return -ENOMEM; + } + + map_size = iommu_map_sg(domain, iova, sgt->sgl, sgt->orig_nents, + rga_dma_info_to_prot(DMA_BIDIRECTIONAL), GFP_KERNEL); + if (map_size < align_size) { + pr_err("iommu can not map sgt to iova"); + rga_iommu_dma_free_iova(domain, iova, align_size); + return -EINVAL; + } + + buffer->domain = domain; + buffer->iova = iova; + buffer->size = align_size; + + return 0; +} + +int rga_iommu_map(phys_addr_t paddr, size_t size, + struct rga_dma_buffer *buffer, + struct device *rga_dev) +{ + int ret; + struct iommu_domain *domain = NULL; + struct rga_iommu_dma_cookie *cookie; + struct iova_domain *iovad; + dma_addr_t iova; + unsigned long align_size; + + if (paddr == 0) { + pr_err("can not map iommu, because phys_addr is 0!\n"); + return -EINVAL; + } + + domain = rga_iommu_get_dma_domain(rga_dev); + cookie = (void *)domain->iova_cookie; + iovad = &cookie->iovad; + align_size = iova_align(iovad, size); + + if (DEBUGGER_EN(MSG)) + pr_info("iova_align size = %ld", align_size); + + iova = rga_iommu_dma_alloc_iova(domain, align_size, rga_dev->coherent_dma_mask, rga_dev); + if (!iova) { + pr_err("rga_iommu_dma_alloc_iova failed"); + return -ENOMEM; + } + + ret = iommu_map(domain, iova, paddr, align_size, + rga_dma_info_to_prot(DMA_BIDIRECTIONAL), GFP_KERNEL); + if (ret) { + pr_err("iommu can not map phys_addr to iova"); + rga_iommu_dma_free_iova(domain, iova, align_size); + return ret; + } + + buffer->domain = domain; + buffer->iova = iova; + buffer->size = align_size; + + return 0; +} + +int rga_virtual_memory_check(void *vaddr, u32 w, u32 h, u32 format, int fd) +{ + int bits = 32; + int temp_data = 0; + void *one_line = NULL; + + bits = rga_get_format_bits(format); + if (bits < 0) + return -1; + + one_line = kzalloc(w * 4, GFP_KERNEL); + if (!one_line) { + pr_err("kzalloc fail %s[%d]\n", __func__, __LINE__); + return 0; + } + + temp_data = w * (h - 1) * bits >> 3; + if (fd > 0) { + pr_info("vaddr is%p, bits is %d, fd check\n", vaddr, bits); + memcpy(one_line, (char *)vaddr + temp_data, w * bits >> 3); + pr_info("fd check ok\n"); + } else { + pr_info("vir addr memory check.\n"); + memcpy((void *)((char *)vaddr + temp_data), one_line, + w * bits >> 3); + pr_info("vir addr check ok.\n"); + } + + kfree(one_line); + return 0; +} + +int rga_dma_memory_check(struct rga_dma_buffer *rga_dma_buffer, struct rga_img_info_t *img) +{ + int ret = 0; + void *vaddr; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + struct iosys_map map; +#endif + struct dma_buf *dma_buf; + + dma_buf = rga_dma_buffer->dma_buf; + + if (!IS_ERR_OR_NULL(dma_buf)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + ret = dma_buf_vmap(dma_buf, &map); + vaddr = ret ? NULL : map.vaddr; +#else + vaddr = dma_buf_vmap(dma_buf); +#endif + if (vaddr) { + ret = rga_virtual_memory_check(vaddr, img->vir_w, + img->vir_h, img->format, img->yrgb_addr); + } else { + pr_err("can't vmap the dma buffer!\n"); + return -EINVAL; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + dma_buf_vunmap(dma_buf, &map); +#else + dma_buf_vunmap(dma_buf, vaddr); +#endif + } + + return ret; +} + +int rga_dma_map_buf(struct dma_buf *dma_buf, struct rga_dma_buffer *rga_dma_buffer, + enum dma_data_direction dir, struct device *rga_dev) +{ + struct dma_buf_attachment *attach = NULL; + struct sg_table *sgt = NULL; + struct scatterlist *sg = NULL; + int i, ret = 0; + + if (dma_buf != NULL) { + get_dma_buf(dma_buf); + } else { + pr_err("dma_buf is invalid[%p]\n", dma_buf); + return -EINVAL; + } + + attach = dma_buf_attach(dma_buf, rga_dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + pr_err("Failed to attach dma_buf, ret[%d]\n", ret); + goto err_get_attach; + } + + sgt = dma_buf_map_attachment(attach, dir); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + pr_err("Failed to map attachment, ret[%d]\n", ret); + goto err_get_sgt; + } + + rga_dma_buffer->dma_buf = dma_buf; + rga_dma_buffer->attach = attach; + rga_dma_buffer->sgt = sgt; + rga_dma_buffer->iova = sg_dma_address(sgt->sgl); + rga_dma_buffer->dir = dir; + rga_dma_buffer->size = 0; + for_each_sgtable_sg(sgt, sg, i) + rga_dma_buffer->size += sg_dma_len(sg); + + return ret; + +err_get_sgt: + if (attach) + dma_buf_detach(dma_buf, attach); +err_get_attach: + if (dma_buf) + dma_buf_put(dma_buf); + + return ret; +} + +int rga_dma_map_fd(int fd, struct rga_dma_buffer *rga_dma_buffer, + enum dma_data_direction dir, struct device *rga_dev) +{ + struct dma_buf *dma_buf = NULL; + struct dma_buf_attachment *attach = NULL; + struct sg_table *sgt = NULL; + struct scatterlist *sg = NULL; + int i, ret = 0; + + dma_buf = dma_buf_get(fd); + if (IS_ERR(dma_buf)) { + ret = PTR_ERR(dma_buf); + pr_err("Fail to get dma_buf from fd[%d], ret[%d]\n", fd, ret); + return ret; + } + + attach = dma_buf_attach(dma_buf, rga_dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + pr_err("Failed to attach dma_buf, ret[%d]\n", ret); + goto err_get_attach; + } + + sgt = dma_buf_map_attachment(attach, dir); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + pr_err("Failed to map attachment, ret[%d]\n", ret); + goto err_get_sgt; + } + + rga_dma_buffer->dma_buf = dma_buf; + rga_dma_buffer->attach = attach; + rga_dma_buffer->sgt = sgt; + rga_dma_buffer->iova = sg_dma_address(sgt->sgl); + rga_dma_buffer->dir = dir; + rga_dma_buffer->size = 0; + for_each_sgtable_sg(sgt, sg, i) + rga_dma_buffer->size += sg_dma_len(sg); + + return ret; + +err_get_sgt: + if (attach) + dma_buf_detach(dma_buf, attach); +err_get_attach: + if (dma_buf) + dma_buf_put(dma_buf); + + return ret; +} + +void rga_dma_unmap_buf(struct rga_dma_buffer *rga_dma_buffer) +{ + if (rga_dma_buffer->attach && rga_dma_buffer->sgt) + dma_buf_unmap_attachment(rga_dma_buffer->attach, + rga_dma_buffer->sgt, + rga_dma_buffer->dir); + + if (rga_dma_buffer->attach) { + dma_buf_detach(rga_dma_buffer->dma_buf, rga_dma_buffer->attach); + dma_buf_put(rga_dma_buffer->dma_buf); + } +} + +void rga_dma_sync_flush_range(void *pstart, void *pend, struct rga_scheduler_t *scheduler) +{ + dma_sync_single_for_device(scheduler->dev, virt_to_phys(pstart), + pend - pstart, DMA_TO_DEVICE); +} diff --git a/drivers/video/rockchip/rga3/rga_drv.c b/drivers/video/rockchip/rga3/rga_drv.c new file mode 100644 index 000000000..351f5db27 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_drv.c @@ -0,0 +1,1612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga: " fmt + +#include "rga2_reg_info.h" +#include "rga3_reg_info.h" +#include "rga_dma_buf.h" +#include "rga_mm.h" + +#include "rga_job.h" +#include "rga_fence.h" +#include "rga_hw_config.h" + +#include "rga_iommu.h" +#include "rga_debugger.h" +#include "rga_common.h" + +struct rga_drvdata_t *rga_drvdata; + +/* set hrtimer */ +static struct hrtimer timer; +static ktime_t kt; + +static struct rga_session *rga_session_init(void); +static int rga_session_deinit(struct rga_session *session); + +static int rga_mpi_set_channel_buffer(struct dma_buf *dma_buf, + struct rga_img_info_t *channel_info, + struct rga_session *session) +{ + struct rga_external_buffer buffer; + + memset(&buffer, 0x0, sizeof(buffer)); + buffer.memory = (unsigned long)dma_buf; + buffer.type = RGA_DMA_BUFFER_PTR; + buffer.memory_parm.width = channel_info->vir_w; + buffer.memory_parm.height = channel_info->vir_h; + buffer.memory_parm.format = channel_info->format; + + buffer.handle = rga_mm_import_buffer(&buffer, session); + if (buffer.handle == 0) { + pr_err("can not import dma_buf %p\n", dma_buf); + return -EFAULT; + } + channel_info->yrgb_addr = buffer.handle; + + return 0; +} + +static void rga_mpi_set_channel_info(uint32_t flags_mask, uint32_t flags, + struct rga_video_frame_info *mpi_frame, + struct rga_img_info_t *channel_info, + struct rga_img_info_t *cache_info) +{ + uint32_t fix_enable_flag, cache_info_flag; + + switch (flags_mask) { + case RGA_CONTEXT_SRC_MASK: + fix_enable_flag = RGA_CONTEXT_SRC_FIX_ENABLE; + cache_info_flag = RGA_CONTEXT_SRC_CACHE_INFO; + break; + case RGA_CONTEXT_PAT_MASK: + fix_enable_flag = RGA_CONTEXT_PAT_FIX_ENABLE; + cache_info_flag = RGA_CONTEXT_PAT_CACHE_INFO; + break; + case RGA_CONTEXT_DST_MASK: + fix_enable_flag = RGA_CONTEXT_DST_FIX_ENABLE; + cache_info_flag = RGA_CONTEXT_DST_CACHE_INFO; + break; + default: + return; + } + + if (flags & fix_enable_flag) { + channel_info->x_offset = mpi_frame->x_offset; + channel_info->y_offset = mpi_frame->y_offset; + channel_info->act_w = mpi_frame->width; + channel_info->act_h = mpi_frame->height; + channel_info->vir_w = mpi_frame->vir_w; + channel_info->vir_h = mpi_frame->vir_h; + channel_info->rd_mode = mpi_frame->rd_mode; + channel_info->format = mpi_frame->format; + + if (flags & cache_info_flag) { + /* Replace the config of src in ctx with the config of mpi src. */ + cache_info->x_offset = mpi_frame->x_offset; + cache_info->y_offset = mpi_frame->y_offset; + cache_info->act_w = mpi_frame->width; + cache_info->act_h = mpi_frame->height; + cache_info->vir_w = mpi_frame->vir_w; + cache_info->vir_h = mpi_frame->vir_h; + cache_info->rd_mode = mpi_frame->rd_mode; + cache_info->format = mpi_frame->format; + + } + } +} + +int rga_mpi_commit(struct rga_mpi_job_t *mpi_job) +{ + int ret = 0; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + struct rga_req *cached_cmd; + struct rga_req mpi_cmd; + unsigned long flags; + + request_manager = rga_drvdata->pend_request_manager; + + mutex_lock(&request_manager->lock); + request = rga_request_lookup(request_manager, mpi_job->ctx_id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", mpi_job->ctx_id); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + if (request->task_count > 1) { + /* TODO */ + pr_err("Currently request does not support multiple tasks!"); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + /* + * The mpi commit will use the request repeatedly, so an additional + * get() is added here. + */ + rga_request_get(request); + mutex_unlock(&request_manager->lock); + + spin_lock_irqsave(&request->lock, flags); + + /* TODO: batch mode need mpi async mode */ + request->sync_mode = RGA_BLIT_SYNC; + + cached_cmd = request->task_list; + memcpy(&mpi_cmd, cached_cmd, sizeof(mpi_cmd)); + + spin_unlock_irqrestore(&request->lock, flags); + + /* set channel info */ + if ((mpi_job->src != NULL) && (request->flags & RGA_CONTEXT_SRC_MASK)) + rga_mpi_set_channel_info(RGA_CONTEXT_SRC_MASK, + request->flags, + mpi_job->src, + &mpi_cmd.src, + &cached_cmd->src); + + if ((mpi_job->pat != NULL) && (request->flags & RGA_CONTEXT_PAT_MASK)) + rga_mpi_set_channel_info(RGA_CONTEXT_PAT_MASK, + request->flags, + mpi_job->pat, + &mpi_cmd.pat, + &cached_cmd->pat); + + if ((mpi_job->dst != NULL) && (request->flags & RGA_CONTEXT_DST_MASK)) + rga_mpi_set_channel_info(RGA_CONTEXT_DST_MASK, + request->flags, + mpi_job->dst, + &mpi_cmd.dst, + &cached_cmd->dst); + + /* set buffer handle */ + if (mpi_job->dma_buf_src0 != NULL) { + ret = rga_mpi_set_channel_buffer(mpi_job->dma_buf_src0, + &mpi_cmd.src, + request->session); + if (ret < 0) { + pr_err("src channel set buffer handle failed!\n"); + goto err_put_request; + } + } + + if (mpi_job->dma_buf_src1 != NULL) { + ret = rga_mpi_set_channel_buffer(mpi_job->dma_buf_src1, + &mpi_cmd.pat, + request->session); + if (ret < 0) { + pr_err("src1 channel set buffer handle failed!\n"); + goto err_put_request; + } + } + + if (mpi_job->dma_buf_dst != NULL) { + ret = rga_mpi_set_channel_buffer(mpi_job->dma_buf_dst, + &mpi_cmd.dst, + request->session); + if (ret < 0) { + pr_err("dst channel set buffer handle failed!\n"); + goto err_put_request; + } + } + + mpi_cmd.handle_flag = 1; + mpi_cmd.mmu_info.mmu_en = 0; + mpi_cmd.mmu_info.mmu_flag = 0; + + if (DEBUGGER_EN(MSG)) + rga_cmd_print_debug_info(&mpi_cmd); + + ret = rga_request_mpi_submit(&mpi_cmd, request); + if (ret < 0) { + if (ret == -ERESTARTSYS) { + if (DEBUGGER_EN(MSG)) + pr_err("%s, commit mpi job failed, by a software interrupt.\n", + __func__); + } else { + pr_err("%s, commit mpi job failed\n", __func__); + } + + goto err_put_request; + } + + if ((mpi_job->dma_buf_src0 != NULL) && (mpi_cmd.src.yrgb_addr > 0)) + rga_mm_release_buffer(mpi_cmd.src.yrgb_addr); + if ((mpi_job->dma_buf_src1 != NULL) && (mpi_cmd.pat.yrgb_addr > 0)) + rga_mm_release_buffer(mpi_cmd.pat.yrgb_addr); + if ((mpi_job->dma_buf_dst != NULL) && (mpi_cmd.dst.yrgb_addr > 0)) + rga_mm_release_buffer(mpi_cmd.dst.yrgb_addr); + + /* copy dst info to mpi job for next node */ + if (mpi_job->output != NULL) { + mpi_job->output->x_offset = mpi_cmd.dst.x_offset; + mpi_job->output->y_offset = mpi_cmd.dst.y_offset; + mpi_job->output->width = mpi_cmd.dst.act_w; + mpi_job->output->height = mpi_cmd.dst.act_h; + mpi_job->output->vir_w = mpi_cmd.dst.vir_w; + mpi_job->output->vir_h = mpi_cmd.dst.vir_h; + mpi_job->output->rd_mode = mpi_cmd.dst.rd_mode; + mpi_job->output->format = mpi_cmd.dst.format; + } + + return 0; + +err_put_request: + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(rga_mpi_commit); + +int rga_kernel_commit(struct rga_req *cmd) +{ + int ret = 0; + int request_id; + struct rga_user_request kernel_request; + struct rga_request *request = NULL; + struct rga_session *session = NULL; + struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; + + session = rga_session_init(); + if (IS_ERR(session)) + return PTR_ERR(session); + + request_id = rga_request_alloc(0, session); + if (request_id < 0) { + pr_err("request alloc error!\n"); + ret = request_id; + return ret; + } + + memset(&kernel_request, 0, sizeof(kernel_request)); + kernel_request.id = request_id; + kernel_request.task_ptr = (uint64_t)(unsigned long)cmd; + kernel_request.task_num = 1; + kernel_request.sync_mode = RGA_BLIT_SYNC; + + ret = rga_request_check(&kernel_request); + if (ret < 0) { + pr_err("user request check error!\n"); + goto err_free_request_by_id; + } + + request = rga_request_kernel_config(&kernel_request); + if (IS_ERR(request)) { + pr_err("request[%d] config failed!\n", kernel_request.id); + ret = -EFAULT; + goto err_free_request_by_id; + } + + if (DEBUGGER_EN(MSG)) { + pr_info("kernel blit mode: request id = %d", kernel_request.id); + rga_cmd_print_debug_info(cmd); + } + + ret = rga_request_submit(request); + if (ret < 0) { + pr_err("request[%d] submit failed!\n", kernel_request.id); + goto err_put_request; + } + +err_put_request: + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + rga_session_deinit(session); + + return ret; + +err_free_request_by_id: + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, request_id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", request_id); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + rga_request_free(request); + + mutex_unlock(&request_manager->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(rga_kernel_commit); + +static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer) +{ + struct rga_drvdata_t *rga = rga_drvdata; + struct rga_scheduler_t *scheduler = NULL; + struct rga_job *job = NULL; + unsigned long flags; + int i; + + ktime_t now = ktime_get(); + + for (i = 0; i < rga->num_of_scheduler; i++) { + scheduler = rga->scheduler[i]; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + /* if timer action on job running */ + job = scheduler->running_job; + if (job) { + scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time); + job->hw_recoder_time = now; + } + + scheduler->timer.busy_time_record = scheduler->timer.busy_time; + scheduler->timer.busy_time = 0; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } + + hrtimer_forward_now(timer, kt); + return HRTIMER_RESTART; +} + +static void rga_init_timer(void) +{ + kt = ktime_set(0, RGA_TIMER_INTERVAL_NS); + hrtimer_setup(&timer, hrtimer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + + hrtimer_start(&timer, kt, HRTIMER_MODE_REL); +} + +static void rga_cancel_timer(void) +{ + hrtimer_cancel(&timer); +} + +#ifndef RGA_DISABLE_PM +int rga_power_enable(struct rga_scheduler_t *scheduler) +{ + int ret = -EINVAL; + int i; + unsigned long flags; + + pm_runtime_get_sync(scheduler->dev); + pm_stay_awake(scheduler->dev); + + for (i = 0; i < scheduler->num_clks; i++) { + if (!IS_ERR(scheduler->clks[i])) { + ret = clk_prepare_enable(scheduler->clks[i]); + if (ret < 0) + goto err_enable_clk; + } + } + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + scheduler->pd_refcount++; + if (scheduler->status == RGA_SCHEDULER_IDLE) + scheduler->status = RGA_SCHEDULER_WORKING; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + return 0; + +err_enable_clk: + for (--i; i >= 0; --i) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_relax(scheduler->dev); + pm_runtime_put_sync_suspend(scheduler->dev); + + return ret; +} + +int rga_power_disable(struct rga_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + if (scheduler->status == RGA_SCHEDULER_IDLE || + scheduler->pd_refcount == 0) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + WARN(true, "%s already idle!\n", dev_driver_string(scheduler->dev)); + return -1; + } + + scheduler->pd_refcount--; + if (scheduler->pd_refcount == 0) + scheduler->status = RGA_SCHEDULER_IDLE; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + for (i = scheduler->num_clks - 1; i >= 0; i--) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_relax(scheduler->dev); + pm_runtime_put_sync_suspend(scheduler->dev); + + return 0; +} + +static void rga_power_enable_all(void) +{ + struct rga_scheduler_t *scheduler = NULL; + int ret = 0; + int i; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + ret = rga_power_enable(scheduler); + if (ret < 0) + pr_err("power enable failed"); + } +} + +static void rga_power_disable_all(void) +{ + struct rga_scheduler_t *scheduler = NULL; + int i; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + rga_power_disable(scheduler); + } +} + +#else +int rga_power_enable(struct rga_scheduler_t *scheduler) +{ + return 0; +} + +int rga_power_disable(struct rga_scheduler_t *scheduler) +{ + return 0; +} + +static inline void rga_power_enable_all(void) {} +static inline void rga_power_disable_all(void) {} +#endif /* #ifndef RGA_DISABLE_PM */ + +static int rga_session_manager_init(struct rga_session_manager **session_manager_ptr) +{ + struct rga_session_manager *session_manager = NULL; + + *session_manager_ptr = kzalloc(sizeof(struct rga_session_manager), GFP_KERNEL); + if (*session_manager_ptr == NULL) { + pr_err("can not kzalloc for rga_session_manager\n"); + return -ENOMEM; + } + + session_manager = *session_manager_ptr; + + mutex_init(&session_manager->lock); + + idr_init_base(&session_manager->ctx_id_idr, 1); + + return 0; +} + +/* + * Called at driver close to release the rga session's id references. + */ +static int rga_session_free_remove_idr_cb(int id, void *ptr, void *data) +{ + struct rga_session *session = ptr; + + idr_remove(&rga_drvdata->session_manager->ctx_id_idr, session->id); + kfree(session); + + return 0; +} + +static int rga_session_free_remove_idr(struct rga_session *session) +{ + struct rga_session_manager *session_manager; + + session_manager = rga_drvdata->session_manager; + + mutex_lock(&session_manager->lock); + + session_manager->session_cnt--; + idr_remove(&session_manager->ctx_id_idr, session->id); + + mutex_unlock(&session_manager->lock); + + return 0; +} + +static int rga_session_manager_remove(struct rga_session_manager **session_manager_ptr) +{ + struct rga_session_manager *session_manager = *session_manager_ptr; + + mutex_lock(&session_manager->lock); + + idr_for_each(&session_manager->ctx_id_idr, &rga_session_free_remove_idr_cb, session_manager); + idr_destroy(&session_manager->ctx_id_idr); + + mutex_unlock(&session_manager->lock); + + kfree(*session_manager_ptr); + + *session_manager_ptr = NULL; + + return 0; +} + +static struct rga_session *rga_session_init(void) +{ + int new_id; + + struct rga_session_manager *session_manager = NULL; + struct rga_session *session = NULL; + + session_manager = rga_drvdata->session_manager; + if (session_manager == NULL) { + pr_err("rga_session_manager is null!\n"); + return ERR_PTR(-EFAULT); + } + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) { + pr_err("rga_session alloc failed\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_lock(&session_manager->lock); + + idr_preload(GFP_KERNEL); + new_id = idr_alloc_cyclic(&session_manager->ctx_id_idr, session, 1, 0, GFP_NOWAIT); + idr_preload_end(); + if (new_id < 0) { + mutex_unlock(&session_manager->lock); + + pr_err("rga_session alloc id failed!\n"); + kfree(session); + return ERR_PTR(new_id); + } + + session->id = new_id; + session_manager->session_cnt++; + + mutex_unlock(&session_manager->lock); + + session->tgid = current->tgid; + session->pname = kstrdup_quotable_cmdline(current, GFP_KERNEL); + + return session; +} + +static int rga_session_deinit(struct rga_session *session) +{ + rga_request_session_destroy_abort(session); + rga_mm_session_release_buffer(session); + + rga_session_free_remove_idr(session); + + kfree(session->pname); + kfree(session); + + return 0; +} + +static long rga_ioctl_import_buffer(unsigned long arg, struct rga_session *session) +{ + int i; + int ret = 0; + struct rga_buffer_pool buffer_pool; + struct rga_external_buffer *external_buffer = NULL; + + if (unlikely(copy_from_user(&buffer_pool, + (struct rga_buffer_pool *)arg, + sizeof(buffer_pool)))) { + pr_err("rga_buffer_pool copy_from_user failed!\n"); + return -EFAULT; + } + + if (buffer_pool.size > RGA_BUFFER_POOL_SIZE_MAX) { + pr_err("Cannot import more than %d buffers at a time!\n", + RGA_BUFFER_POOL_SIZE_MAX); + return -EFBIG; + } + + if (buffer_pool.buffers_ptr == 0) { + pr_err("Import buffers is NULL!\n"); + return -EFAULT; + } + + external_buffer = kmalloc(sizeof(struct rga_external_buffer) * buffer_pool.size, + GFP_KERNEL); + if (external_buffer == NULL) { + pr_err("external buffer list alloc error!\n"); + return -ENOMEM; + } + + if (unlikely(copy_from_user(external_buffer, + u64_to_user_ptr(buffer_pool.buffers_ptr), + sizeof(struct rga_external_buffer) * buffer_pool.size))) { + pr_err("rga_buffer_pool external_buffer list copy_from_user failed\n"); + ret = -EFAULT; + + goto err_free_external_buffer; + } + + for (i = 0; i < buffer_pool.size; i++) { + if (DEBUGGER_EN(MSG)) { + pr_info("import buffer info:\n"); + rga_dump_external_buffer(&external_buffer[i]); + } + + ret = rga_mm_import_buffer(&external_buffer[i], session); + if (ret <= 0) { + pr_err("buffer[%d] mm import buffer failed! memory = 0x%lx, type = %s(0x%x)\n", + i, (unsigned long)external_buffer[i].memory, + rga_get_memory_type_str(external_buffer[i].type), + external_buffer[i].type); + + goto err_free_external_buffer; + } + + external_buffer[i].handle = ret; + } + + if (unlikely(copy_to_user(u64_to_user_ptr(buffer_pool.buffers_ptr), + external_buffer, + sizeof(struct rga_external_buffer) * buffer_pool.size))) { + pr_err("rga_buffer_pool external_buffer list copy_to_user failed\n"); + ret = -EFAULT; + + goto err_free_external_buffer; + } + +err_free_external_buffer: + kfree(external_buffer); + return ret; +} + +static long rga_ioctl_release_buffer(unsigned long arg) +{ + int i; + int ret = 0; + struct rga_buffer_pool buffer_pool; + struct rga_external_buffer *external_buffer = NULL; + + if (unlikely(copy_from_user(&buffer_pool, + (struct rga_buffer_pool *)arg, + sizeof(buffer_pool)))) { + pr_err("rga_buffer_pool copy_from_user failed!\n"); + return -EFAULT; + } + + if (buffer_pool.size > RGA_BUFFER_POOL_SIZE_MAX) { + pr_err("Cannot release more than %d buffers at a time!\n", + RGA_BUFFER_POOL_SIZE_MAX); + return -EFBIG; + } + + if (buffer_pool.buffers_ptr == 0) { + pr_err("Release buffers is NULL!\n"); + return -EFAULT; + } + + external_buffer = kmalloc(sizeof(struct rga_external_buffer) * buffer_pool.size, + GFP_KERNEL); + if (external_buffer == NULL) { + pr_err("external buffer list alloc error!\n"); + return -ENOMEM; + } + + if (unlikely(copy_from_user(external_buffer, + u64_to_user_ptr(buffer_pool.buffers_ptr), + sizeof(struct rga_external_buffer) * buffer_pool.size))) { + pr_err("rga_buffer_pool external_buffer list copy_from_user failed\n"); + ret = -EFAULT; + + goto err_free_external_buffer; + } + + for (i = 0; i < buffer_pool.size; i++) { + if (DEBUGGER_EN(MSG)) + pr_info("release buffer handle[%d]\n", external_buffer[i].handle); + + ret = rga_mm_release_buffer(external_buffer[i].handle); + if (ret < 0) { + pr_err("buffer[%d] mm release buffer failed! handle = %d\n", + i, external_buffer[i].handle); + + goto err_free_external_buffer; + } + } + +err_free_external_buffer: + kfree(external_buffer); + return ret; +} + +static long rga_ioctl_request_create(unsigned long arg, struct rga_session *session) +{ + uint32_t id; + uint32_t flags; + + if (copy_from_user(&flags, (void *)arg, sizeof(uint32_t))) { + pr_err("%s failed to copy from usrer!\n", __func__); + return -EFAULT; + } + + id = rga_request_alloc(flags, session); + + if (copy_to_user((void *)arg, &id, sizeof(uint32_t))) { + pr_err("%s failed to copy to usrer!\n", __func__); + return -EFAULT; + } + + return 0; +} + +static long rga_ioctl_request_submit(unsigned long arg, bool run_enbale) +{ + int ret = 0; + struct rga_pending_request_manager *request_manager = NULL; + struct rga_user_request user_request; + struct rga_request *request = NULL; + + request_manager = rga_drvdata->pend_request_manager; + + if (unlikely(copy_from_user(&user_request, + (struct rga_user_request *)arg, + sizeof(user_request)))) { + pr_err("%s copy_from_user failed!\n", __func__); + return -EFAULT; + } + + ret = rga_request_check(&user_request); + if (ret < 0) { + pr_err("user request check error!\n"); + return ret; + } + + if (DEBUGGER_EN(MSG)) + pr_info("config request id = %d", user_request.id); + + request = rga_request_config(&user_request); + if (IS_ERR_OR_NULL(request)) { + pr_err("request[%d] config failed!\n", user_request.id); + return -EFAULT; + } + + if (run_enbale) { + ret = rga_request_submit(request); + if (ret < 0) { + pr_err("request[%d] submit failed!\n", user_request.id); + return -EFAULT; + } + + if (request->sync_mode == RGA_BLIT_ASYNC) { + user_request.release_fence_fd = request->release_fence_fd; + if (copy_to_user((struct rga_req *)arg, + &user_request, sizeof(user_request))) { + pr_err("copy_to_user failed\n"); + return -EFAULT; + } + } + } + + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + return 0; +} + +static long rga_ioctl_request_cancel(unsigned long arg) +{ + uint32_t id; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return -EFAULT; + } + + if (unlikely(copy_from_user(&id, (uint32_t *)arg, sizeof(uint32_t)))) { + pr_err("request id copy_from_user failed!\n"); + return -EFAULT; + } + + if (DEBUGGER_EN(MSG)) + pr_info("config cancel request id = %d", id); + + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", id); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + rga_request_put(request); + + mutex_unlock(&request_manager->lock); + + return 0; +} + +static long rga_ioctl_blit(unsigned long arg, uint32_t cmd, struct rga_session *session) +{ + int ret = 0; + int request_id; + struct rga_user_request user_request; + struct rga_req *rga_req; + struct rga_request *request = NULL; + struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; + + request_id = rga_request_alloc(0, session); + if (request_id < 0) { + pr_err("request alloc error!\n"); + ret = request_id; + return ret; + } + + memset(&user_request, 0, sizeof(user_request)); + user_request.id = request_id; + user_request.task_ptr = arg; + user_request.task_num = 1; + user_request.sync_mode = cmd; + + ret = rga_request_check(&user_request); + if (ret < 0) { + pr_err("user request check error!\n"); + goto err_free_request_by_id; + } + + request = rga_request_config(&user_request); + if (IS_ERR(request)) { + pr_err("request[%d] config failed!\n", user_request.id); + ret = -EFAULT; + goto err_free_request_by_id; + } + + rga_req = request->task_list; + /* In the BLIT_SYNC/BLIT_ASYNC command, in_fence_fd needs to be set. */ + request->acquire_fence_fd = rga_req->in_fence_fd; + + ret = rga_request_submit(request); + if (ret < 0) { + pr_err("request[%d] submit failed!\n", user_request.id); + goto err_put_request; + } + + if (request->sync_mode == RGA_BLIT_ASYNC) { + rga_req->out_fence_fd = request->release_fence_fd; + if (copy_to_user((struct rga_req *)arg, rga_req, sizeof(struct rga_req))) { + pr_err("copy_to_user failed\n"); + ret = -EFAULT; + goto err_put_request; + } + } + +err_put_request: + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + return ret; + +err_free_request_by_id: + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, request_id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", request_id); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + rga_request_free(request); + + mutex_unlock(&request_manager->lock); + + return ret; +} + +static long rga_ioctl(struct file *file, uint32_t cmd, unsigned long arg) +{ + int ret = 0; + int i = 0; + int major_version = 0, minor_version = 0; + char version[16] = { 0 }; + struct rga_version_t driver_version; + struct rga_hw_versions_t hw_versions; + struct rga_drvdata_t *rga = rga_drvdata; + struct rga_session *session = file->private_data; + + if (!rga) { + pr_err("rga_drvdata is null, rga is not init\n"); + return -ENODEV; + } + + if (DEBUGGER_EN(NONUSE)) + return 0; + + switch (cmd) { + case RGA_BLIT_SYNC: + case RGA_BLIT_ASYNC: + ret = rga_ioctl_blit(arg, cmd, session); + + break; + case RGA_CACHE_FLUSH: + case RGA_FLUSH: + case RGA_GET_RESULT: + break; + case RGA_GET_VERSION: + sscanf(rga->scheduler[i]->version.str, "%x.%x.%*x", + &major_version, &minor_version); + snprintf(version, 5, "%x.%02x", major_version, minor_version); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) + /* TODO: userspcae to get version */ + if (copy_to_user((void *)arg, version, sizeof(version))) + ret = -EFAULT; +#else + if (copy_to_user((void *)arg, RGA3_VERSION, + sizeof(RGA3_VERSION))) + ret = -EFAULT; +#endif + break; + case RGA2_GET_VERSION: + for (i = 0; i < rga->num_of_scheduler; i++) { + if (rga->scheduler[i]->ops == &rga2_ops) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) + if (copy_to_user((void *)arg, rga->scheduler[i]->version.str, + sizeof(rga->scheduler[i]->version.str))) + ret = -EFAULT; +#else + if (copy_to_user((void *)arg, RGA3_VERSION, + sizeof(RGA3_VERSION))) + ret = -EFAULT; +#endif + else + ret = true; + + break; + } + } + + /* This will indicate that the RGA2 version number cannot be obtained. */ + if (ret != true) + ret = -EFAULT; + + break; + + case RGA_IOC_GET_HW_VERSION: + /* RGA hardware version */ + hw_versions.size = rga->num_of_scheduler > RGA_HW_SIZE ? + RGA_HW_SIZE : rga->num_of_scheduler; + + for (i = 0; i < hw_versions.size; i++) { + memcpy(&hw_versions.version[i], &rga->scheduler[i]->version, + sizeof(rga->scheduler[i]->version)); + } + + if (copy_to_user((void *)arg, &hw_versions, sizeof(hw_versions))) + ret = -EFAULT; + else + ret = true; + + break; + + case RGA_IOC_GET_DRVIER_VERSION: + /* Driver version */ + driver_version.major = DRIVER_MAJOR_VERISON; + driver_version.minor = DRIVER_MINOR_VERSION; + driver_version.revision = DRIVER_REVISION_VERSION; + strncpy((char *)driver_version.str, DRIVER_VERSION, sizeof(driver_version.str)); + + if (copy_to_user((void *)arg, &driver_version, sizeof(driver_version))) + ret = -EFAULT; + else + ret = true; + + break; + + case RGA_IOC_IMPORT_BUFFER: + rga_power_enable_all(); + + ret = rga_ioctl_import_buffer(arg, session); + + rga_power_disable_all(); + + break; + + case RGA_IOC_RELEASE_BUFFER: + rga_power_enable_all(); + + ret = rga_ioctl_release_buffer(arg); + + rga_power_disable_all(); + + break; + + case RGA_IOC_REQUEST_CREATE: + ret = rga_ioctl_request_create(arg, session); + + break; + + case RGA_IOC_REQUEST_SUBMIT: + ret = rga_ioctl_request_submit(arg, true); + + break; + + case RGA_IOC_REQUEST_CONFIG: + ret = rga_ioctl_request_submit(arg, false); + + break; + + case RGA_IOC_REQUEST_CANCEL: + ret = rga_ioctl_request_cancel(arg); + + break; + + case RGA_IMPORT_DMA: + case RGA_RELEASE_DMA: + default: + pr_err("unknown ioctl cmd!\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER +static int rga_debugger_init(struct rga_debugger **debugger_p) +{ + struct rga_debugger *debugger; + + *debugger_p = kzalloc(sizeof(struct rga_debugger), GFP_KERNEL); + if (*debugger_p == NULL) { + pr_err("can not alloc for rga debugger\n"); + return -ENOMEM; + } + + debugger = *debugger_p; + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUG_FS + mutex_init(&debugger->debugfs_lock); + INIT_LIST_HEAD(&debugger->debugfs_entry_list); +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_PROC_FS + mutex_init(&debugger->procfs_lock); + INIT_LIST_HEAD(&debugger->procfs_entry_list); +#endif + + rga_debugfs_init(); + rga_procfs_init(); + + return 0; +} + +static int rga_debugger_remove(struct rga_debugger **debugger_p) +{ + rga_debugfs_remove(); + rga_procfs_remove(); + + kfree(*debugger_p); + *debugger_p = NULL; + + return 0; +} +#endif + +static int rga_open(struct inode *inode, struct file *file) +{ + struct rga_session *session = NULL; + + session = rga_session_init(); + if (IS_ERR(session)) + return PTR_ERR(session); + + file->private_data = (void *)session; + + return nonseekable_open(inode, file); +} + +static int rga_release(struct inode *inode, struct file *file) +{ + struct rga_session *session = file->private_data; + + rga_session_deinit(session); + + return 0; +} + +static irqreturn_t rga_irq_handler(int irq, void *data) +{ + irqreturn_t irq_ret = IRQ_NONE; + struct rga_scheduler_t *scheduler = data; + + if (scheduler->ops->irq) + irq_ret = scheduler->ops->irq(scheduler); + + return irq_ret; +} + +static irqreturn_t rga_isr_thread(int irq, void *data) +{ + irqreturn_t irq_ret = IRQ_NONE; + struct rga_scheduler_t *scheduler = data; + struct rga_job *job; + + job = rga_job_done(scheduler); + if (job == NULL) { + pr_err("isr thread invalid job!\n"); + return IRQ_HANDLED; + } + + if (scheduler->ops->isr_thread) + irq_ret = scheduler->ops->isr_thread(job, scheduler); + + rga_request_release_signal(scheduler, job); + + rga_job_next(scheduler); + + rga_power_disable(scheduler); + + return irq_ret; +} + +const struct file_operations rga_fops = { + .owner = THIS_MODULE, + .open = rga_open, + .release = rga_release, + .unlocked_ioctl = rga_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rga_ioctl, +#endif +}; + +static struct miscdevice rga_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rga", + .fops = &rga_fops, +}; + +static const char *const old_rga2_clks[] = { + "aclk_rga", + "hclk_rga", + "clk_rga", +}; + +static const char *const rk3588_rga2_clks[] = { + "aclk_rga2", + "hclk_rga2", + "clk_rga2", +}; + +static const char *const rga3_core_0_clks[] = { + "aclk_rga3_0", + "hclk_rga3_0", + "clk_rga3_0", +}; + +static const char *const rga3_core_1_clks[] = { + "aclk_rga3_1", + "hclk_rga3_1", + "clk_rga3_1", +}; + +static const struct rga_match_data_t old_rga2_match_data = { + .clks = old_rga2_clks, + .num_clks = ARRAY_SIZE(old_rga2_clks), +}; + +static const struct rga_match_data_t rk3588_rga2_match_data = { + .clks = rk3588_rga2_clks, + .num_clks = ARRAY_SIZE(rk3588_rga2_clks), +}; + +static const struct rga_match_data_t rga3_core0_match_data = { + .clks = rga3_core_0_clks, + .num_clks = ARRAY_SIZE(rga3_core_0_clks), +}; + +static const struct rga_match_data_t rga3_core1_match_data = { + .clks = rga3_core_1_clks, + .num_clks = ARRAY_SIZE(rga3_core_1_clks), +}; + +static const struct of_device_id rga3_core0_dt_ids[] = { + { + .compatible = "rockchip,rga3_core0", + .data = &rga3_core0_match_data, + }, + {}, +}; + +static const struct of_device_id rga3_core1_dt_ids[] = { + { + .compatible = "rockchip,rga3_core1", + .data = &rga3_core1_match_data, + }, + {}, +}; + +static const struct of_device_id rga2_dt_ids[] = { + { + .compatible = "rockchip,rga2_core0", + .data = &rk3588_rga2_match_data, + }, + { + .compatible = "rockchip,rga2", + .data = &old_rga2_match_data, + }, + {}, +}; + +static void init_scheduler(struct rga_scheduler_t *scheduler, + const char *name) +{ + spin_lock_init(&scheduler->irq_lock); + INIT_LIST_HEAD(&scheduler->todo_list); + init_waitqueue_head(&scheduler->job_done_wq); + + if (!strcmp(name, "rga3_core0")) { + scheduler->ops = &rga3_ops; + /* TODO: get by hw version */ + scheduler->core = RGA3_SCHEDULER_CORE0; + } else if (!strcmp(name, "rga3_core1")) { + scheduler->ops = &rga3_ops; + scheduler->core = RGA3_SCHEDULER_CORE1; + } else if (!strcmp(name, "rga2")) { + scheduler->ops = &rga2_ops; + scheduler->core = RGA2_SCHEDULER_CORE0; + } +} + +static int rga_drv_probe(struct platform_device *pdev) +{ +#ifndef RGA_DISABLE_PM + int i; +#endif + int ret = 0; + int irq; + struct resource *res; + const struct rga_match_data_t *match_data; + const struct of_device_id *match; + struct rga_scheduler_t *scheduler; + struct device *dev = &pdev->dev; + struct rga_drvdata_t *data = rga_drvdata; + + if (!dev->of_node) + return -EINVAL; + + if (!strcmp(dev_driver_string(dev), "rga3_core0")) + match = of_match_device(rga3_core0_dt_ids, dev); + else if (!strcmp(dev_driver_string(dev), "rga3_core1")) + match = of_match_device(rga3_core1_dt_ids, dev); + else if (!strcmp(dev_driver_string(dev), "rga2")) + match = of_match_device(rga2_dt_ids, dev); + else + match = NULL; + + if (!match) { + dev_err(dev, "%s missing DT entry!\n", dev_driver_string(dev)); + return -EINVAL; + } + + scheduler = devm_kzalloc(dev, sizeof(struct rga_scheduler_t), GFP_KERNEL); + if (scheduler == NULL) { + pr_err("failed to allocate scheduler. dev name = %s\n", dev_driver_string(dev)); + return -ENOMEM; + } + + init_scheduler(scheduler, dev_driver_string(dev)); + + scheduler->dev = dev; + + /* map the registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("get memory resource failed.\n"); + return -ENXIO; + } + + scheduler->rga_base = devm_ioremap(dev, res->start, resource_size(res)); + if (!scheduler->rga_base) { + pr_err("ioremap failed\n"); + ret = -ENOENT; + return ret; + } + + /* get the IRQ */ + match_data = match->data; + + /* there are irq names in dts */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq %s in dts\n", dev_driver_string(dev)); + return irq; + } + + scheduler->irq = irq; + + pr_info("%s, irq = %d, match scheduler\n", dev_driver_string(dev), irq); + + ret = devm_request_threaded_irq(dev, irq, + rga_irq_handler, + rga_isr_thread, + IRQF_SHARED, + dev_driver_string(dev), scheduler); + if (ret < 0) { + pr_err("request irq name: %s failed: %d\n", dev_driver_string(dev), ret); + return ret; + } + + +#ifndef RGA_DISABLE_PM + /* clk init */ + for (i = 0; i < match_data->num_clks; i++) { + struct clk *clk = devm_clk_get(dev, match_data->clks[i]); + + if (IS_ERR(clk)) + pr_err("failed to get %s\n", match_data->clks[i]); + + scheduler->clks[i] = clk; + } + scheduler->num_clks = match_data->num_clks; + + /* PM init */ + device_init_wakeup(dev, true); + pm_runtime_enable(scheduler->dev); + + ret = pm_runtime_get_sync(scheduler->dev); + if (ret < 0) { + pr_err("failed to get pm runtime, ret = %d\n", ret); + goto pm_disable; + } + + for (i = 0; i < scheduler->num_clks; i++) { + if (!IS_ERR(scheduler->clks[i])) { + ret = clk_prepare_enable(scheduler->clks[i]); + if (ret < 0) { + pr_err("failed to enable clk\n"); + goto pm_disable; + } + } + } +#endif /* #ifndef RGA_DISABLE_PM */ + + scheduler->ops->get_version(scheduler); + pr_info("%s hardware loaded successfully, hw_version:%s.\n", + dev_driver_string(dev), scheduler->version.str); + + /* TODO: get by hw version, Currently only supports judgment 1106. */ + if (scheduler->core == RGA3_SCHEDULER_CORE0 || + scheduler->core == RGA3_SCHEDULER_CORE1) { + scheduler->data = &rga3_data; + } else if (scheduler->core == RGA2_SCHEDULER_CORE0) { + if (!strcmp(scheduler->version.str, "3.3.87975")) + scheduler->data = &rga2e_1106_data; + else if (!strcmp(scheduler->version.str, "3.6.92812") || + !strcmp(scheduler->version.str, "3.7.93215")) + scheduler->data = &rga2e_iommu_data; + else + scheduler->data = &rga2e_data; + } + + data->scheduler[data->num_of_scheduler] = scheduler; + + data->num_of_scheduler++; + +#ifndef RGA_DISABLE_PM + for (i = scheduler->num_clks - 1; i >= 0; i--) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_runtime_put_sync(dev); +#endif /* #ifndef RGA_DISABLE_PM */ + + if (scheduler->data->mmu == RGA_IOMMU) { + scheduler->iommu_info = rga_iommu_probe(dev); + if (IS_ERR(scheduler->iommu_info)) { + dev_err(dev, "failed to attach iommu\n"); + scheduler->iommu_info = NULL; + } + } + + platform_set_drvdata(pdev, scheduler); + + pr_info("%s probe successfully\n", dev_driver_string(dev)); + + return 0; + +#ifndef RGA_DISABLE_PM +pm_disable: + device_init_wakeup(dev, false); + pm_runtime_disable(dev); +#endif /* #ifndef RGA_DISABLE_PM */ + + return ret; +} + +static void rga_drv_remove(struct platform_device *pdev) +{ +#ifndef RGA_DISABLE_PM + device_init_wakeup(&pdev->dev, false); + pm_runtime_disable(&pdev->dev); +#endif /* #ifndef RGA_DISABLE_PM */ + + return; +} + +static struct platform_driver rga3_core0_driver = { + .probe = rga_drv_probe, + .remove = rga_drv_remove, + .driver = { + .name = "rga3_core0", + .of_match_table = of_match_ptr(rga3_core0_dt_ids), + }, +}; + +static struct platform_driver rga3_core1_driver = { + .probe = rga_drv_probe, + .remove = rga_drv_remove, + .driver = { + .name = "rga3_core1", + .of_match_table = of_match_ptr(rga3_core1_dt_ids), + }, +}; + +static struct platform_driver rga2_driver = { + .probe = rga_drv_probe, + .remove = rga_drv_remove, + .driver = { + .name = "rga2", + .of_match_table = of_match_ptr(rga2_dt_ids), + }, +}; + +static int __init rga_init(void) +{ + int ret; + + rga_drvdata = kzalloc(sizeof(struct rga_drvdata_t), GFP_KERNEL); + if (rga_drvdata == NULL) { + pr_err("failed to allocate driver data.\n"); + return -ENOMEM; + } + + mutex_init(&rga_drvdata->lock); + + ret = platform_driver_register(&rga3_core0_driver); + if (ret != 0) { + pr_err("Platform device rga3_core0_driver register failed (%d).\n", ret); + goto err_free_drvdata; + } + + ret = platform_driver_register(&rga3_core1_driver); + if (ret != 0) { + pr_err("Platform device rga3_core1_driver register failed (%d).\n", ret); + goto err_unregister_rga3_core0; + } + + ret = platform_driver_register(&rga2_driver); + if (ret != 0) { + pr_err("Platform device rga2_driver register failed (%d).\n", ret); + goto err_unregister_rga3_core1; + } + + ret = rga_iommu_bind(); + if (ret < 0) { + pr_err("rga iommu bind failed!\n"); + goto err_unregister_rga2; + } + + ret = misc_register(&rga_dev); + if (ret) { + pr_err("cannot register miscdev (%d)\n", ret); + goto err_unbind_iommu; + } + + rga_init_timer(); + + rga_mm_init(&rga_drvdata->mm); + + rga_request_manager_init(&rga_drvdata->pend_request_manager); + + rga_session_manager_init(&rga_drvdata->session_manager); + +#ifdef CONFIG_ROCKCHIP_RGA_ASYNC + rga_fence_context_init(&rga_drvdata->fence_ctx); +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER + rga_debugger_init(&rga_drvdata->debugger); +#endif + + pr_info("Module initialized. v%s\n", DRIVER_VERSION); + + return 0; + +err_unbind_iommu: + rga_iommu_unbind(); + +err_unregister_rga2: + platform_driver_unregister(&rga2_driver); + +err_unregister_rga3_core1: + platform_driver_unregister(&rga3_core1_driver); + +err_unregister_rga3_core0: + platform_driver_unregister(&rga3_core0_driver); + +err_free_drvdata: + kfree(rga_drvdata); + + return ret; +} + +static void __exit rga_exit(void) +{ +#ifdef CONFIG_ROCKCHIP_RGA_DEBUGGER + rga_debugger_remove(&rga_drvdata->debugger); +#endif + +#ifdef CONFIG_ROCKCHIP_RGA_ASYNC + rga_fence_context_remove(&rga_drvdata->fence_ctx); +#endif + + rga_mm_remove(&rga_drvdata->mm); + + rga_request_manager_remove(&rga_drvdata->pend_request_manager); + + rga_session_manager_remove(&rga_drvdata->session_manager); + + rga_cancel_timer(); + + rga_iommu_unbind(); + + platform_driver_unregister(&rga3_core0_driver); + platform_driver_unregister(&rga3_core1_driver); + platform_driver_unregister(&rga2_driver); + + misc_deregister(&rga_dev); + + kfree(rga_drvdata); + + pr_info("Module exited. v%s\n", DRIVER_VERSION); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT +module_init(rga_init); +#elif defined CONFIG_VIDEO_REVERSE_IMAGE +fs_initcall(rga_init); +#else +late_initcall(rga_init); +#endif +#else +fs_initcall(rga_init); +#endif +module_exit(rga_exit); + +/* Module information */ +MODULE_AUTHOR("putin.li@rock-chips.com"); +MODULE_DESCRIPTION("Driver for rga device"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_IMPORT_NS +MODULE_IMPORT_NS("DMA_BUF"); +#endif diff --git a/drivers/video/rockchip/rga3/rga_fence.c b/drivers/video/rockchip/rga3/rga_fence.c new file mode 100644 index 000000000..7d831d55d --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_fence.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga_fence: " fmt + +#include +#include +#include + +#include "rga_drv.h" +#include "rga_fence.h" + +static const char *rga_fence_get_name(struct dma_fence *fence) +{ + return DRIVER_NAME; +} + +static const struct dma_fence_ops rga_fence_ops = { + .get_driver_name = rga_fence_get_name, + .get_timeline_name = rga_fence_get_name, +}; + +int rga_fence_context_init(struct rga_fence_context **ctx) +{ + struct rga_fence_context *fence_ctx = NULL; + + fence_ctx = kzalloc(sizeof(struct rga_fence_context), GFP_KERNEL); + if (!fence_ctx) { + pr_err("can not kzalloc for rga_fence_context!\n"); + return -ENOMEM; + } + + fence_ctx->context = dma_fence_context_alloc(1); + spin_lock_init(&fence_ctx->spinlock); + + *ctx = fence_ctx; + + return 0; +} + +void rga_fence_context_remove(struct rga_fence_context **ctx) +{ + if (*ctx == NULL) + return; + + kfree(*ctx); + *ctx = NULL; +} + +struct dma_fence *rga_dma_fence_alloc(void) +{ + struct rga_fence_context *fence_ctx = rga_drvdata->fence_ctx; + struct dma_fence *fence = NULL; + + if (fence_ctx == NULL) { + pr_err("fence_context is NULL!\n"); + return ERR_PTR(-EINVAL); + } + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return ERR_PTR(-ENOMEM); + + dma_fence_init(fence, &rga_fence_ops, &fence_ctx->spinlock, + fence_ctx->context, ++fence_ctx->seqno); + + return fence; +} + +int rga_dma_fence_get_fd(struct dma_fence *fence) +{ + struct sync_file *sync_file = NULL; + int fence_fd = -1; + + if (!fence) + return -EINVAL; + + fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (fence_fd < 0) + return fence_fd; + + sync_file = sync_file_create(fence); + if (!sync_file) { + put_unused_fd(fence_fd); + return -ENOMEM; + } + + fd_install(fence_fd, sync_file->file); + + return fence_fd; +} + +struct dma_fence *rga_get_dma_fence_from_fd(int fence_fd) +{ + struct dma_fence *fence; + + fence = sync_file_get_fence(fence_fd); + if (!fence) + pr_err("can not get fence from fd\n"); + + return fence; +} + +int rga_dma_fence_wait(struct dma_fence *fence) +{ + int ret = 0; + + ret = dma_fence_wait(fence, true); + + dma_fence_put(fence); + + return ret; +} + +int rga_dma_fence_add_callback(struct dma_fence *fence, dma_fence_func_t func, void *private) +{ + int ret; + struct rga_fence_waiter *waiter = NULL; + + waiter = kmalloc(sizeof(*waiter), GFP_KERNEL); + if (!waiter) { + pr_err("%s: Failed to allocate waiter\n", __func__); + return -ENOMEM; + } + + waiter->private = private; + + ret = dma_fence_add_callback(fence, &waiter->waiter, func); + if (ret == -ENOENT) { + pr_err("'input fence' has been already signaled."); + goto err_free_waiter; + } else if (ret == -EINVAL) { + pr_err("%s: failed to add callback to dma_fence, err: %d\n", __func__, ret); + goto err_free_waiter; + } + + return ret; + +err_free_waiter: + kfree(waiter); + return ret; +} diff --git a/drivers/video/rockchip/rga3/rga_hw_config.c b/drivers/video/rockchip/rga3/rga_hw_config.c new file mode 100644 index 000000000..0cf2599b9 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_hw_config.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Huang Lee + */ + +#include "rga_hw_config.h" + +/* RGA 1Word = 4Byte */ +#define WORD_TO_BYTE(w) ((w) * 4) + +const uint32_t rga3_input_raster_format[] = { + RGA_FORMAT_RGBA_8888, + RGA_FORMAT_BGRA_8888, + RGA_FORMAT_RGB_888, + RGA_FORMAT_BGR_888, + RGA_FORMAT_RGB_565, + RGA_FORMAT_BGR_565, + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YVYU_422, + RGA_FORMAT_VYUY_422, + RGA_FORMAT_YUYV_422, + RGA_FORMAT_UYVY_422, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, + RGA_FORMAT_ARGB_8888, + RGA_FORMAT_ABGR_8888, +}; + +const uint32_t rga3_output_raster_format[] = { + RGA_FORMAT_RGBA_8888, + RGA_FORMAT_BGRA_8888, + RGA_FORMAT_RGB_888, + RGA_FORMAT_BGR_888, + RGA_FORMAT_RGB_565, + RGA_FORMAT_BGR_565, + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YVYU_422, + RGA_FORMAT_VYUY_422, + RGA_FORMAT_YUYV_422, + RGA_FORMAT_UYVY_422, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, +}; + +const uint32_t rga3_fbcd_format[] = { + RGA_FORMAT_RGBA_8888, + RGA_FORMAT_BGRA_8888, + RGA_FORMAT_RGB_888, + RGA_FORMAT_BGR_888, + RGA_FORMAT_RGB_565, + RGA_FORMAT_BGR_565, + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, +}; + +const uint32_t rga3_tile_format[] = { + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, +}; + +const uint32_t rga2e_input_raster_format[] = { + RGA_FORMAT_RGBA_8888, + RGA_FORMAT_RGBX_8888, + RGA_FORMAT_BGRA_8888, + RGA_FORMAT_BGRX_8888, + RGA_FORMAT_RGB_888, + RGA_FORMAT_BGR_888, + RGA_FORMAT_RGB_565, + RGA_FORMAT_BGR_565, + RGA_FORMAT_YCbCr_422_P, + RGA_FORMAT_YCbCr_420_P, + RGA_FORMAT_YCrCb_422_P, + RGA_FORMAT_YCrCb_420_P, + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YVYU_422, + RGA_FORMAT_VYUY_422, + RGA_FORMAT_YUYV_422, + RGA_FORMAT_UYVY_422, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, + RGA_FORMAT_YCbCr_400, + RGA_FORMAT_RGBA_5551, + RGA_FORMAT_BGRA_5551, + RGA_FORMAT_RGBA_4444, + RGA_FORMAT_BGRA_4444, + RGA_FORMAT_XRGB_8888, + RGA_FORMAT_XBGR_8888, + RGA_FORMAT_BPP1, + RGA_FORMAT_BPP2, + RGA_FORMAT_BPP4, + RGA_FORMAT_BPP8, + RGA_FORMAT_ARGB_8888, + RGA_FORMAT_ARGB_5551, + RGA_FORMAT_ARGB_4444, + RGA_FORMAT_ABGR_8888, + RGA_FORMAT_ABGR_5551, + RGA_FORMAT_ABGR_4444, +}; + +const uint32_t rga2e_output_raster_format[] = { + RGA_FORMAT_RGBA_8888, + RGA_FORMAT_RGBX_8888, + RGA_FORMAT_BGRA_8888, + RGA_FORMAT_BGRX_8888, + RGA_FORMAT_RGB_888, + RGA_FORMAT_BGR_888, + RGA_FORMAT_RGB_565, + RGA_FORMAT_BGR_565, + RGA_FORMAT_YCbCr_422_P, + RGA_FORMAT_YCbCr_420_P, + RGA_FORMAT_YCrCb_422_P, + RGA_FORMAT_YCrCb_420_P, + RGA_FORMAT_YCbCr_422_SP, + RGA_FORMAT_YCbCr_420_SP, + RGA_FORMAT_YCrCb_422_SP, + RGA_FORMAT_YCrCb_420_SP, + RGA_FORMAT_YVYU_420, + RGA_FORMAT_VYUY_420, + RGA_FORMAT_YUYV_420, + RGA_FORMAT_UYVY_420, + RGA_FORMAT_YVYU_422, + RGA_FORMAT_VYUY_422, + RGA_FORMAT_YUYV_422, + RGA_FORMAT_UYVY_422, + RGA_FORMAT_YCbCr_420_SP_10B, + RGA_FORMAT_YCrCb_420_SP_10B, + RGA_FORMAT_YCbCr_422_SP_10B, + RGA_FORMAT_YCrCb_422_SP_10B, + RGA_FORMAT_Y4, + RGA_FORMAT_YCbCr_400, + RGA_FORMAT_RGBA_5551, + RGA_FORMAT_BGRA_5551, + RGA_FORMAT_RGBA_4444, + RGA_FORMAT_BGRA_4444, + RGA_FORMAT_XRGB_8888, + RGA_FORMAT_XBGR_8888, + RGA_FORMAT_ARGB_8888, + RGA_FORMAT_ARGB_5551, + RGA_FORMAT_ARGB_4444, + RGA_FORMAT_ABGR_8888, + RGA_FORMAT_ABGR_5551, + RGA_FORMAT_ABGR_4444, +}; + +const struct rga_win_data rga3_win_data[] = { + { + .name = "rga3-win0", + .formats[RGA_RASTER_INDEX] = rga3_input_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga3_input_raster_format), + .formats[RGA_AFBC16x16_INDEX] = rga3_fbcd_format, + .formats_count[RGA_AFBC16x16_INDEX] = ARRAY_SIZE(rga3_fbcd_format), + .formats[RGA_TILE8x8_INDEX] = rga3_tile_format, + .formats_count[RGA_TILE8x8_INDEX] = ARRAY_SIZE(rga3_tile_format), + .supported_rotations = RGA_MODE_ROTATE_MASK, + .scale_up_mode = RGA_SCALE_UP_BIC, + .scale_down_mode = RGA_SCALE_DOWN_AVG, + .rd_mode = RGA_RASTER_MODE | RGA_FBC_MODE | RGA_TILE_MODE, + + }, + + { + .name = "rga3-win1", + .formats[RGA_RASTER_INDEX] = rga3_input_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga3_input_raster_format), + .formats[RGA_AFBC16x16_INDEX] = rga3_fbcd_format, + .formats_count[RGA_AFBC16x16_INDEX] = ARRAY_SIZE(rga3_fbcd_format), + .formats[RGA_TILE8x8_INDEX] = rga3_tile_format, + .formats_count[RGA_TILE8x8_INDEX] = ARRAY_SIZE(rga3_tile_format), + .supported_rotations = RGA_MODE_ROTATE_MASK, + .scale_up_mode = RGA_SCALE_UP_BIC, + .scale_down_mode = RGA_SCALE_DOWN_AVG, + .rd_mode = RGA_RASTER_MODE | RGA_FBC_MODE | RGA_TILE_MODE, + + }, + + { + .name = "rga3-wr", + .formats[RGA_RASTER_INDEX] = rga3_output_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga3_output_raster_format), + .formats[RGA_AFBC16x16_INDEX] = rga3_fbcd_format, + .formats_count[RGA_AFBC16x16_INDEX] = ARRAY_SIZE(rga3_fbcd_format), + .formats[RGA_TILE8x8_INDEX] = rga3_tile_format, + .formats_count[RGA_TILE8x8_INDEX] = ARRAY_SIZE(rga3_tile_format), + .supported_rotations = 0, + .scale_up_mode = RGA_SCALE_UP_NONE, + .scale_down_mode = RGA_SCALE_DOWN_NONE, + .rd_mode = RGA_RASTER_MODE | RGA_FBC_MODE | RGA_TILE_MODE, + + }, +}; + +const struct rga_win_data rga2e_win_data[] = { + { + .name = "rga2e-src0", + .formats[RGA_RASTER_INDEX] = rga2e_input_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga2e_input_raster_format), + .supported_rotations = RGA_MODE_ROTATE_MASK, + .scale_up_mode = RGA_SCALE_UP_BIC, + .scale_down_mode = RGA_SCALE_DOWN_AVG, + .rd_mode = RGA_RASTER_MODE, + + }, + + { + .name = "rga2e-src1", + .formats[RGA_RASTER_INDEX] = rga2e_input_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga2e_input_raster_format), + .supported_rotations = RGA_MODE_ROTATE_MASK, + .scale_up_mode = RGA_SCALE_UP_BIC, + .scale_down_mode = RGA_SCALE_DOWN_AVG, + .rd_mode = RGA_RASTER_MODE, + + }, + + { + .name = "rga2-dst", + .formats[RGA_RASTER_INDEX] = rga2e_output_raster_format, + .formats_count[RGA_RASTER_INDEX] = ARRAY_SIZE(rga2e_output_raster_format), + .supported_rotations = 0, + .scale_up_mode = RGA_SCALE_UP_NONE, + .scale_down_mode = RGA_SCALE_DOWN_NONE, + .rd_mode = RGA_RASTER_MODE, + + }, +}; + +const struct rga_hw_data rga3_data = { + .version = 0, + .input_range = {{68, 2}, {8176, 8176}}, + .output_range = {{68, 2}, {8128, 8128}}, + + .win = rga3_win_data, + .win_size = ARRAY_SIZE(rga3_win_data), + /* 1 << factor mean real factor */ + .max_upscale_factor = 3, + .max_downscale_factor = 3, + + .byte_stride_align = 16, + .max_byte_stride = WORD_TO_BYTE(8192), + + .feature = RGA_COLOR_KEY, + .csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709 | RGA_MODE_CSC_BT2020, + .csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709 | RGA_MODE_CSC_BT2020, + .mmu = RGA_IOMMU, +}; + +const struct rga_hw_data rga2e_data = { + .version = 0, + .input_range = {{2, 2}, {8192, 8192}}, + .output_range = {{2, 2}, {4096, 4096}}, + + .win = rga2e_win_data, + .win_size = ARRAY_SIZE(rga2e_win_data), + /* 1 << factor mean real factor */ + .max_upscale_factor = 4, + .max_downscale_factor = 4, + + .byte_stride_align = 4, + .max_byte_stride = WORD_TO_BYTE(8192), + + .feature = RGA_COLOR_FILL | RGA_COLOR_PALETTE | + RGA_COLOR_KEY | RGA_ROP_CALCULATE | + RGA_NN_QUANTIZE | RGA_DITHER | RGA_FULL_CSC, + .csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F, + .csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709, + .mmu = RGA_MMU, +}; + +const struct rga_hw_data rga2e_1106_data = { + .version = 0, + .input_range = {{2, 2}, {8192, 8192}}, + .output_range = {{2, 2}, {4096, 4096}}, + + .win = rga2e_win_data, + .win_size = ARRAY_SIZE(rga2e_win_data), + /* 1 << factor mean real factor */ + .max_upscale_factor = 4, + .max_downscale_factor = 4, + + .byte_stride_align = 4, + .max_byte_stride = WORD_TO_BYTE(8192), + + .feature = RGA_COLOR_FILL | RGA_COLOR_PALETTE | + RGA_COLOR_KEY | RGA_ROP_CALCULATE | + RGA_NN_QUANTIZE | RGA_DITHER | RGA_MOSAIC | + RGA_YIN_YOUT | RGA_YUV_HDS | RGA_YUV_VDS | + RGA_OSD | RGA_PRE_INTR | RGA_FULL_CSC, + .csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709, + .csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709, + .mmu = RGA_NONE_MMU, +}; + +const struct rga_hw_data rga2e_iommu_data = { + .version = 0, + .input_range = {{2, 2}, {8192, 8192}}, + .output_range = {{2, 2}, {4096, 4096}}, + + .win = rga2e_win_data, + .win_size = ARRAY_SIZE(rga2e_win_data), + /* 1 << factor mean real factor */ + .max_upscale_factor = 4, + .max_downscale_factor = 4, + + .byte_stride_align = 4, + .max_byte_stride = WORD_TO_BYTE(8192), + + .feature = RGA_COLOR_FILL | RGA_COLOR_PALETTE | + RGA_COLOR_KEY | RGA_ROP_CALCULATE | + RGA_NN_QUANTIZE | RGA_DITHER | RGA_MOSAIC | + RGA_YIN_YOUT | RGA_YUV_HDS | RGA_YUV_VDS | + RGA_OSD | RGA_PRE_INTR | RGA_FULL_CSC, + .csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709, + .csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F | + RGA_MODE_CSC_BT709, + .mmu = RGA_IOMMU, +}; diff --git a/drivers/video/rockchip/rga3/rga_iommu.c b/drivers/video/rockchip/rga3/rga_iommu.c new file mode 100644 index 000000000..6ef9cbc0d --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_iommu.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga_iommu: " fmt + +#include "rga_iommu.h" +#include "rga_dma_buf.h" +#include "rga_mm.h" +#include "rga_job.h" +#include "rga_common.h" +#include "rga_hw_config.h" + +int rga_user_memory_check(struct page **pages, u32 w, u32 h, u32 format, int flag) +{ + int bits; + void *vaddr = NULL; + int taipage_num; + int taidata_num; + int *tai_vaddr = NULL; + + bits = rga_get_format_bits(format); + if (bits < 0) + return -1; + + taipage_num = w * h * bits / 8 / (1024 * 4); + taidata_num = w * h * bits / 8 % (1024 * 4); + if (taidata_num == 0) { + vaddr = kmap(pages[taipage_num - 1]); + tai_vaddr = (int *)vaddr + 1023; + } else { + vaddr = kmap(pages[taipage_num]); + tai_vaddr = (int *)vaddr + taidata_num / 4 - 1; + } + + if (flag == 1) { + pr_info("src user memory check\n"); + pr_info("tai data is %d\n", *tai_vaddr); + } else { + pr_info("dst user memory check\n"); + pr_info("tai data is %d\n", *tai_vaddr); + } + + if (taidata_num == 0) + kunmap(pages[taipage_num - 1]); + else + kunmap(pages[taipage_num]); + + return 0; +} + +int rga_set_mmu_base(struct rga_job *job, struct rga2_req *req) +{ + if (job->src_buffer.page_table) { + rga_dma_sync_flush_range(job->src_buffer.page_table, + (job->src_buffer.page_table + + job->src_buffer.page_count), + job->scheduler); + req->mmu_info.src0_base_addr = virt_to_phys(job->src_buffer.page_table); + } + + if (job->src1_buffer.page_table) { + rga_dma_sync_flush_range(job->src1_buffer.page_table, + (job->src1_buffer.page_table + + job->src1_buffer.page_count), + job->scheduler); + req->mmu_info.src1_base_addr = virt_to_phys(job->src1_buffer.page_table); + } + + if (job->dst_buffer.page_table) { + rga_dma_sync_flush_range(job->dst_buffer.page_table, + (job->dst_buffer.page_table + + job->dst_buffer.page_count), + job->scheduler); + req->mmu_info.dst_base_addr = virt_to_phys(job->dst_buffer.page_table); + + if (((req->alpha_rop_flag & 1) == 1) && (req->bitblt_mode == 0)) { + req->mmu_info.src1_base_addr = req->mmu_info.dst_base_addr; + req->mmu_info.src1_mmu_flag = req->mmu_info.dst_mmu_flag; + } + } + + if (job->els_buffer.page_table) { + rga_dma_sync_flush_range(job->els_buffer.page_table, + (job->els_buffer.page_table + + job->els_buffer.page_count), + job->scheduler); + req->mmu_info.els_base_addr = virt_to_phys(job->els_buffer.page_table); + } + + return 0; +} + +static int rga_mmu_buf_get_try(struct rga_mmu_base *t, uint32_t size) +{ + int ret = 0; + + if ((t->back - t->front) > t->size) { + if (t->front + size > t->back - t->size) { + pr_info("front %d, back %d dsize %d size %d", + t->front, t->back, t->size, size); + ret = -ENOMEM; + goto out; + } + } else { + if ((t->front + size) > t->back) { + pr_info("front %d, back %d dsize %d size %d", + t->front, t->back, t->size, size); + ret = -ENOMEM; + goto out; + } + + if (t->front + size > t->size) { + if (size > (t->back - t->size)) { + pr_info("front %d, back %d dsize %d size %d", + t->front, t->back, t->size, size); + ret = -ENOMEM; + goto out; + } + t->front = 0; + } + } +out: + return ret; +} + +unsigned int *rga_mmu_buf_get(struct rga_mmu_base *mmu_base, uint32_t size) +{ + int ret; + unsigned int *buf = NULL; + + WARN_ON(!mutex_is_locked(&rga_drvdata->lock)); + + size = ALIGN(size, 16); + + ret = rga_mmu_buf_get_try(mmu_base, size); + if (ret < 0) { + pr_err("Get MMU mem failed\n"); + return NULL; + } + + buf = mmu_base->buf_virtual + mmu_base->front; + + mmu_base->front += size; + + if (mmu_base->back + size > 2 * mmu_base->size) + mmu_base->back = size + mmu_base->size; + else + mmu_base->back += size; + + return buf; +} + +struct rga_mmu_base *rga_mmu_base_init(size_t size) +{ + int order = 0; + struct rga_mmu_base *mmu_base; + + mmu_base = kzalloc(sizeof(*mmu_base), GFP_KERNEL); + if (mmu_base == NULL) { + pr_err("Cannot alloc mmu_base!\n"); + return ERR_PTR(-ENOMEM); + } + + /* + * malloc pre scale mid buf mmu table: + * size * channel_num * address_size + */ + order = get_order(size * 3 * sizeof(*mmu_base->buf_virtual)); + if (order >= MAX_ORDER) { + pr_err("Can not alloc pages with order[%d] for mmu_page_table, max_order = %d\n", + order, MAX_ORDER); + goto err_free_mmu_base; + } + + mmu_base->buf_virtual = (uint32_t *) __get_free_pages(GFP_KERNEL | GFP_DMA32, order); + if (mmu_base->buf_virtual == NULL) { + pr_err("Can not alloc pages for mmu_page_table\n"); + goto err_free_mmu_base; + } + mmu_base->buf_order = order; + + order = get_order(size * sizeof(*mmu_base->pages)); + if (order >= MAX_ORDER) { + pr_err("Can not alloc pages with order[%d] for mmu_base->pages, max_order = %d\n", + order, MAX_ORDER); + goto err_free_buf_virtual; + } + + mmu_base->pages = (struct page **)__get_free_pages(GFP_KERNEL | GFP_DMA32, order); + if (mmu_base->pages == NULL) { + pr_err("Can not alloc pages for mmu_base->pages\n"); + goto err_free_buf_virtual; + } + mmu_base->pages_order = order; + + mmu_base->front = 0; + mmu_base->back = RGA2_PHY_PAGE_SIZE * 3; + mmu_base->size = RGA2_PHY_PAGE_SIZE * 3; + + return mmu_base; + +err_free_buf_virtual: + free_pages((unsigned long)mmu_base->buf_virtual, mmu_base->buf_order); + mmu_base->buf_order = 0; + +err_free_mmu_base: + kfree(mmu_base); + + return ERR_PTR(-ENOMEM); +} + +void rga_mmu_base_free(struct rga_mmu_base **mmu_base) +{ + struct rga_mmu_base *base = *mmu_base; + + if (base->buf_virtual != NULL) { + free_pages((unsigned long)base->buf_virtual, base->buf_order); + base->buf_virtual = NULL; + base->buf_order = 0; + } + + if (base->pages != NULL) { + free_pages((unsigned long)base->pages, base->pages_order); + base->pages = NULL; + base->pages_order = 0; + } + + kfree(base); + *mmu_base = NULL; +} + +static int rga_iommu_intr_fault_handler(struct iommu_domain *iommu, struct device *iommu_dev, + unsigned long iova, int status, void *arg) +{ + struct rga_scheduler_t *scheduler = (struct rga_scheduler_t *)arg; + struct rga_job *job = scheduler->running_job; + + if (job == NULL) + return 0; + + pr_err("IOMMU intr fault, IOVA[0x%lx], STATUS[0x%x]\n", iova, status); + if (scheduler->ops->irq) + scheduler->ops->irq(scheduler); + + /* iommu interrupts on rga2 do not affect rga2 itself. */ + if (!test_bit(RGA_JOB_STATE_INTR_ERR, &job->state)) { + set_bit(RGA_JOB_STATE_INTR_ERR, &job->state); + scheduler->ops->soft_reset(scheduler); + } + + if (status & RGA_IOMMU_IRQ_PAGE_FAULT) { + pr_err("RGA IOMMU: page fault! Please check the memory size.\n"); + job->ret = -EACCES; + } else if (status & RGA_IOMMU_IRQ_BUS_ERROR) { + pr_err("RGA IOMMU: bus error! Please check if the memory is invalid or has been freed.\n"); + job->ret = -EACCES; + } else { + pr_err("RGA IOMMU: Wrong IOMMU interrupt signal!\n"); + } + + return 0; +} + +int rga_iommu_detach(struct rga_iommu_info *info) +{ + if (!info) + return 0; + + iommu_detach_group(info->domain, info->group); + return 0; +} + +int rga_iommu_attach(struct rga_iommu_info *info) +{ + if (!info) + return 0; + + return iommu_attach_group(info->domain, info->group); +} + +struct rga_iommu_info *rga_iommu_probe(struct device *dev) +{ + int ret = 0; + struct rga_iommu_info *info = NULL; + struct iommu_domain *domain = NULL; + struct iommu_group *group = NULL; + + group = iommu_group_get(dev); + if (!group) + return ERR_PTR(-EINVAL); + + domain = iommu_get_domain_for_dev(dev); + if (!domain) { + ret = -EINVAL; + goto err_put_group; + } + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto err_put_group; + } + + info->dev = dev; + info->default_dev = info->dev; + info->group = group; + info->domain = domain; + + return info; + +err_put_group: + if (group) + iommu_group_put(group); + + return ERR_PTR(ret); +} + +int rga_iommu_remove(struct rga_iommu_info *info) +{ + if (!info) + return 0; + + iommu_group_put(info->group); + + return 0; +} + +int rga_iommu_bind(void) +{ + int i; + int ret; + struct rga_scheduler_t *scheduler = NULL; + struct rga_iommu_info *main_iommu = NULL; + int main_iommu_index = -1; + int main_mmu_index = -1; + int another_index = -1; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + + switch (scheduler->data->mmu) { + case RGA_IOMMU: + if (scheduler->iommu_info == NULL) + continue; + + if (main_iommu == NULL) { + main_iommu = scheduler->iommu_info; + main_iommu_index = i; + iommu_set_fault_handler(main_iommu->domain, + rga_iommu_intr_fault_handler, + (void *)scheduler); + } else { + scheduler->iommu_info->domain = main_iommu->domain; + scheduler->iommu_info->default_dev = main_iommu->default_dev; + rga_iommu_attach(scheduler->iommu_info); + } + + break; + + case RGA_MMU: + if (rga_drvdata->mmu_base != NULL) + continue; + + rga_drvdata->mmu_base = rga_mmu_base_init(RGA2_PHY_PAGE_SIZE); + if (IS_ERR(rga_drvdata->mmu_base)) { + dev_err(scheduler->dev, "rga mmu base init failed!\n"); + ret = PTR_ERR(rga_drvdata->mmu_base); + rga_drvdata->mmu_base = NULL; + + return ret; + } + + main_mmu_index = i; + + break; + default: + if (another_index != RGA_NONE_CORE) + another_index = i; + + break; + } + } + + /* + * priority order: iommu > mmu > another + * The scheduler core with IOMMU will be used preferentially as the + * default memory-mapped core. This ensures that all cores can obtain + * the required memory data when they are equipped with different + * versions of cores. + */ + if (main_iommu_index >= 0) { + rga_drvdata->map_scheduler_index = main_iommu_index; + } else if (main_mmu_index >= 0) { + rga_drvdata->map_scheduler_index = main_mmu_index; + } else if (another_index >= 0) { + rga_drvdata->map_scheduler_index = another_index; + } else { + rga_drvdata->map_scheduler_index = -1; + pr_err("%s, binding map scheduler failed!\n", __func__); + return -EFAULT; + } + + pr_info("IOMMU binding successfully, default mapping core[0x%x]\n", + rga_drvdata->scheduler[rga_drvdata->map_scheduler_index]->core); + + return 0; +} + +void rga_iommu_unbind(void) +{ + int i; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) + if (rga_drvdata->scheduler[i]->iommu_info != NULL) + rga_iommu_detach(rga_drvdata->scheduler[i]->iommu_info); + + if (rga_drvdata->mmu_base) + rga_mmu_base_free(&rga_drvdata->mmu_base); + + rga_drvdata->map_scheduler_index = -1; +} diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c new file mode 100644 index 000000000..fae613bbf --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -0,0 +1,1423 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga_job: " fmt + +#include "rga_job.h" +#include "rga_fence.h" +#include "rga_dma_buf.h" +#include "rga_mm.h" +#include "rga_iommu.h" +#include "rga_debugger.h" +#include "rga_common.h" + +static void rga_job_free(struct rga_job *job) +{ + free_page((unsigned long)job); +} + +static void rga_job_kref_release(struct kref *ref) +{ + struct rga_job *job; + + job = container_of(ref, struct rga_job, refcount); + + rga_job_free(job); +} + +static int rga_job_put(struct rga_job *job) +{ + return kref_put(&job->refcount, rga_job_kref_release); +} + +static void rga_job_get(struct rga_job *job) +{ + kref_get(&job->refcount); +} + +static int rga_job_cleanup(struct rga_job *job) +{ + rga_job_put(job); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], job cleanup total cost time %lld us\n", + job->request_id, + ktime_us_delta(ktime_get(), job->timestamp)); + + return 0; +} + +static int rga_job_judgment_support_core(struct rga_job *job) +{ + int ret = 0; + uint32_t mm_flag; + struct rga_req *req; + struct rga_mm *mm; + + req = &job->rga_command_base; + mm = rga_drvdata->mm; + if (mm == NULL) { + pr_err("rga mm is null!\n"); + return -EFAULT; + } + + mutex_lock(&mm->lock); + + if (likely(req->src.yrgb_addr > 0)) { + ret = rga_mm_lookup_flag(mm, req->src.yrgb_addr); + if (ret < 0) + goto out_finish; + else + mm_flag = (uint32_t)ret; + + if (~mm_flag & RGA_MEM_UNDER_4G) { + job->flags |= RGA_JOB_UNSUPPORT_RGA_MMU; + goto out_finish; + } + } + + if (likely(req->dst.yrgb_addr > 0)) { + ret = rga_mm_lookup_flag(mm, req->dst.yrgb_addr); + if (ret < 0) + goto out_finish; + else + mm_flag = (uint32_t)ret; + + if (~mm_flag & RGA_MEM_UNDER_4G) { + job->flags |= RGA_JOB_UNSUPPORT_RGA_MMU; + goto out_finish; + } + } + + if (req->pat.yrgb_addr > 0) { + ret = rga_mm_lookup_flag(mm, req->pat.yrgb_addr); + if (ret < 0) + goto out_finish; + else + mm_flag = (uint32_t)ret; + + if (~mm_flag & RGA_MEM_UNDER_4G) { + job->flags |= RGA_JOB_UNSUPPORT_RGA_MMU; + goto out_finish; + } + } + +out_finish: + mutex_unlock(&mm->lock); + + return ret; +} + +static struct rga_job *rga_job_alloc(struct rga_req *rga_command_base) +{ + struct rga_job *job = NULL; + + job = (struct rga_job *)get_zeroed_page(GFP_KERNEL | GFP_DMA32); + if (!job) + return NULL; + + INIT_LIST_HEAD(&job->head); + kref_init(&job->refcount); + + job->timestamp = ktime_get(); + job->pid = current->pid; + + job->rga_command_base = *rga_command_base; + + if (rga_command_base->priority > 0) { + if (rga_command_base->priority > RGA_SCHED_PRIORITY_MAX) + job->priority = RGA_SCHED_PRIORITY_MAX; + else + job->priority = rga_command_base->priority; + } + + if (job->rga_command_base.handle_flag & 1) { + job->flags |= RGA_JOB_USE_HANDLE; + + rga_job_judgment_support_core(job); + } + + return job; +} + +static void rga_job_dump_info(struct rga_job *job) +{ + pr_info("job: reqeust_id = %d, priority = %d, core = %d\n", + job->request_id, job->priority, job->core); +} + +void rga_job_scheduler_dump_info(struct rga_scheduler_t *scheduler) +{ + struct rga_job *job_pos; + + lockdep_assert_held(&scheduler->irq_lock); + + pr_info("===============================================================\n"); + pr_info("%s core = %d job_count = %d status = %d\n", + dev_driver_string(scheduler->dev), + scheduler->core, scheduler->job_count, scheduler->status); + + if (scheduler->running_job) + rga_job_dump_info(scheduler->running_job); + + list_for_each_entry(job_pos, &scheduler->todo_list, head) { + rga_job_dump_info(job_pos); + } + + pr_info("===============================================================\n"); +} + +static int rga_job_run(struct rga_job *job, struct rga_scheduler_t *scheduler) +{ + int ret = 0; + + /* enable power */ + ret = rga_power_enable(scheduler); + if (ret < 0) { + pr_err("power enable failed"); + return ret; + } + + ret = scheduler->ops->set_reg(job, scheduler); + if (ret < 0) { + pr_err("set reg failed"); + rga_power_disable(scheduler); + return ret; + } + + set_bit(RGA_JOB_STATE_RUNNING, &job->state); + + /* for debug */ + if (DEBUGGER_EN(MSG)) + rga_job_dump_info(job); + + return ret; +} + +void rga_job_next(struct rga_scheduler_t *scheduler) +{ + int ret; + struct rga_job *job = NULL; + unsigned long flags; + +next_job: + spin_lock_irqsave(&scheduler->irq_lock, flags); + + if (scheduler->running_job || + list_empty(&scheduler->todo_list)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + return; + } + + job = list_first_entry(&scheduler->todo_list, struct rga_job, head); + + list_del_init(&job->head); + + scheduler->job_count--; + + scheduler->running_job = job; + set_bit(RGA_JOB_STATE_PREPARE, &job->state); + rga_job_get(job); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + ret = rga_job_run(job, scheduler); + /* If some error before hw run */ + if (ret < 0) { + pr_err("some error on rga_job_run before hw start, %s(%d)\n", __func__, __LINE__); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + scheduler->running_job = NULL; + rga_job_put(job); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + job->ret = ret; + rga_request_release_signal(scheduler, job); + + goto next_job; + } + + rga_job_put(job); +} + +struct rga_job *rga_job_done(struct rga_scheduler_t *scheduler) +{ + struct rga_job *job; + unsigned long flags; + ktime_t now = ktime_get(); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = scheduler->running_job; + if (job == NULL) { + pr_err("core[0x%x] running job has been cleanup.\n", scheduler->core); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + return NULL; + } + scheduler->running_job = NULL; + + scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time); + set_bit(RGA_JOB_STATE_DONE, &job->state); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + if (scheduler->ops->read_back_reg) + scheduler->ops->read_back_reg(job, scheduler); + + if (DEBUGGER_EN(DUMP_IMAGE)) + rga_dump_job_image(job); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], hardware[%s] cost time %lld us\n", + job->request_id, + rga_get_core_name(scheduler->core), + ktime_us_delta(now, job->hw_running_time)); + + rga_mm_unmap_job_info(job); + + return job; +} + +static void rga_job_scheduler_timeout_clean(struct rga_scheduler_t *scheduler) +{ + unsigned long flags; + struct rga_job *job = NULL; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + if (scheduler->running_job == NULL || scheduler->running_job->hw_running_time == 0) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + return; + } + + job = scheduler->running_job; + if (ktime_ms_delta(ktime_get(), job->hw_running_time) >= RGA_JOB_TIMEOUT_DELAY) { + scheduler->running_job = NULL; + scheduler->status = RGA_SCHEDULER_ABORT; + scheduler->ops->soft_reset(scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + rga_mm_unmap_job_info(job); + + job->ret = -EBUSY; + rga_request_release_signal(scheduler, job); + + rga_power_disable(scheduler); + } else { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } +} + +static void rga_job_insert_todo_list(struct rga_job *job) +{ + bool first_match = 0; + unsigned long flags; + struct rga_job *job_pos; + struct rga_scheduler_t *scheduler = job->scheduler; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + /* priority policy set by userspace */ + if (list_empty(&scheduler->todo_list) + || (job->priority == RGA_SCHED_PRIORITY_DEFAULT)) { + list_add_tail(&job->head, &scheduler->todo_list); + } else { + list_for_each_entry(job_pos, &scheduler->todo_list, head) { + if (job->priority > job_pos->priority && + (!first_match)) { + list_add(&job->head, &job_pos->head); + first_match = true; + } + + /* + * Increase the priority of subsequent tasks + * after inserting into the list + */ + if (first_match) + job_pos->priority++; + } + + if (!first_match) + list_add_tail(&job->head, &scheduler->todo_list); + } + + scheduler->job_count++; + set_bit(RGA_JOB_STATE_PENDING, &job->state); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); +} + +static struct rga_scheduler_t *rga_job_schedule(struct rga_job *job) +{ + int i; + struct rga_scheduler_t *scheduler = NULL; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + rga_job_scheduler_timeout_clean(scheduler); + } + + if (rga_drvdata->num_of_scheduler > 1) { + job->core = rga_job_assign(job); + if (job->core <= 0) { + pr_err("job assign failed"); + job->ret = -EINVAL; + return NULL; + } + } else { + job->core = rga_drvdata->scheduler[0]->core; + job->scheduler = rga_drvdata->scheduler[0]; + } + + scheduler = job->scheduler; + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + job->ret = -EFAULT; + return NULL; + } + + return scheduler; +} + +struct rga_job *rga_job_commit(struct rga_req *rga_command_base, struct rga_request *request) +{ + int ret; + struct rga_job *job = NULL; + struct rga_scheduler_t *scheduler = NULL; + + job = rga_job_alloc(rga_command_base); + if (!job) { + pr_err("failed to alloc rga job!\n"); + return ERR_PTR(-ENOMEM); + } + + job->use_batch_mode = request->use_batch_mode; + job->request_id = request->id; + job->session = request->session; + job->mm = request->current_mm; + + scheduler = rga_job_schedule(job); + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + goto err_free_job; + } + + /* Memory mapping needs to keep pd enabled. */ + if (rga_power_enable(scheduler) < 0) { + pr_err("power enable failed"); + job->ret = -EFAULT; + goto err_free_job; + } + + ret = rga_mm_map_job_info(job); + if (ret < 0) { + pr_err("%s: failed to map job info\n", __func__); + job->ret = ret; + goto err_power_disable; + } + + ret = scheduler->ops->init_reg(job); + if (ret < 0) { + pr_err("%s: init reg failed", __func__); + job->ret = ret; + goto err_unmap_job_info; + } + + rga_job_insert_todo_list(job); + + rga_job_next(scheduler); + + rga_power_disable(scheduler); + + return job; + +err_unmap_job_info: + rga_mm_unmap_job_info(job); + +err_power_disable: + rga_power_disable(scheduler); + +err_free_job: + ret = job->ret; + rga_request_release_signal(scheduler, job); + + return ERR_PTR(ret); +} + +static bool rga_is_need_current_mm(struct rga_req *req) +{ + int mmu_flag; + struct rga_img_info_t *src0 = NULL; + struct rga_img_info_t *src1 = NULL; + struct rga_img_info_t *dst = NULL; + struct rga_img_info_t *els = NULL; + + src0 = &req->src; + dst = &req->dst; + if (req->render_mode != UPDATE_PALETTE_TABLE_MODE) + src1 = &req->pat; + else + els = &req->pat; + + if (likely(src0 != NULL)) { + mmu_flag = ((req->mmu_info.mmu_flag >> 8) & 1); + if (mmu_flag && src0->uv_addr) + return true; + } + + if (likely(dst != NULL)) { + mmu_flag = ((req->mmu_info.mmu_flag >> 10) & 1); + if (mmu_flag && dst->uv_addr) + return true; + } + + if (src1 != NULL) { + mmu_flag = ((req->mmu_info.mmu_flag >> 9) & 1); + if (mmu_flag && src1->uv_addr) + return true; + } + + if (els != NULL) { + mmu_flag = ((req->mmu_info.mmu_flag >> 11) & 1); + if (mmu_flag && els->uv_addr) + return true; + } + + return false; +} + +static struct mm_struct *rga_request_get_current_mm(struct rga_request *request) +{ + int i; + + for (i = 0; i < request->task_count; i++) { + if (rga_is_need_current_mm(&(request->task_list[i]))) { + mmgrab(current->mm); + mmget(current->mm); + + return current->mm; + } + } + + return NULL; +} + +static void rga_request_put_current_mm(struct mm_struct *mm) +{ + if (mm == NULL) + return; + + mmput(mm); + mmdrop(mm); +} + +static int rga_request_add_acquire_fence_callback(int acquire_fence_fd, + struct rga_request *request, + dma_fence_func_t cb_func) +{ + int ret; + struct dma_fence *acquire_fence = NULL; + struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; + + if (DEBUGGER_EN(MSG)) + pr_info("acquire_fence_fd = %d", acquire_fence_fd); + + acquire_fence = rga_get_dma_fence_from_fd(acquire_fence_fd); + if (IS_ERR_OR_NULL(acquire_fence)) { + pr_err("%s: failed to get acquire dma_fence from[%d]\n", + __func__, acquire_fence_fd); + return -EINVAL; + } + + if (!request->feature.user_close_fence) { + /* close acquire fence fd */ +#ifdef CONFIG_NO_GKI +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + close_fd(acquire_fence_fd); +#else + ksys_close(acquire_fence_fd); +#endif +#else + pr_err("Please update the driver to v1.2.28 to prevent acquire_fence_fd leaks."); + return -EFAULT; +#endif + } + + + ret = rga_dma_fence_get_status(acquire_fence); + if (ret < 0) { + pr_err("%s: Current acquire fence unexpectedly has error status before signal\n", + __func__); + return ret; + } else if (ret > 0) { + /* has been signaled */ + return ret; + } + + /* + * Ensure that the request will not be free early when + * the callback is called. + */ + mutex_lock(&request_manager->lock); + rga_request_get(request); + mutex_unlock(&request_manager->lock); + + ret = rga_dma_fence_add_callback(acquire_fence, cb_func, (void *)request); + if (ret < 0) { + if (ret != -ENOENT) + pr_err("%s: failed to add fence callback\n", __func__); + + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + return ret; + } + + return 0; +} + +int rga_request_check(struct rga_user_request *req) +{ + if (req->id <= 0) { + pr_err("user request id[%d] is invalid", req->id); + return -EINVAL; + } + + if (req->task_num <= 0) { + pr_err("invalied user request!\n"); + return -EINVAL; + } + + if (req->task_ptr == 0) { + pr_err("task_ptr is NULL!\n"); + return -EINVAL; + } + + if (req->task_num > RGA_TASK_NUM_MAX) { + pr_err("Only supports running %d tasks, now %d\n", + RGA_TASK_NUM_MAX, req->task_num); + return -EFBIG; + } + + return 0; +} + +struct rga_request *rga_request_lookup(struct rga_pending_request_manager *manager, uint32_t id) +{ + struct rga_request *request = NULL; + + WARN_ON(!mutex_is_locked(&manager->lock)); + + request = idr_find(&manager->request_idr, id); + + return request; +} + +static int rga_request_scheduler_job_abort(struct rga_request *request) +{ + int i; + unsigned long flags; + enum rga_scheduler_status scheduler_status; + int running_abort_count = 0, todo_abort_count = 0; + struct rga_scheduler_t *scheduler = NULL; + struct rga_job *job, *job_q; + LIST_HEAD(list_to_free); + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + spin_lock_irqsave(&scheduler->irq_lock, flags); + + list_for_each_entry_safe(job, job_q, &scheduler->todo_list, head) { + if (request->id == job->request_id) { + list_move(&job->head, &list_to_free); + scheduler->job_count--; + + todo_abort_count++; + } + } + + job = NULL; + if (scheduler->running_job) { + if (request->id == scheduler->running_job->request_id) { + job = scheduler->running_job; + scheduler_status = scheduler->status; + scheduler->running_job = NULL; + scheduler->status = RGA_SCHEDULER_ABORT; + list_add_tail(&job->head, &list_to_free); + + if (job->hw_running_time != 0) { + scheduler->timer.busy_time += + ktime_us_delta(ktime_get(), job->hw_recoder_time); + scheduler->ops->soft_reset(scheduler); + } + + pr_err("reset core[%d] by request[%d] abort", + scheduler->core, request->id); + running_abort_count++; + } + } + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + if (job && scheduler_status == RGA_SCHEDULER_WORKING) + rga_power_disable(scheduler); + } + + /* Clean up the jobs in the todo list that need to be free. */ + list_for_each_entry_safe(job, job_q, &list_to_free, head) { + rga_mm_unmap_job_info(job); + + job->ret = -EBUSY; + rga_job_cleanup(job); + } + + /* This means it has been cleaned up. */ + if (running_abort_count + todo_abort_count == 0) + return 1; + + pr_err("request[%d] abort! finished %d failed %d running_abort %d todo_abort %d\n", + request->id, request->finished_task_count, request->failed_task_count, + running_abort_count, todo_abort_count); + + return 0; +} + +static void rga_request_release_abort(struct rga_request *request, int err_code) +{ + unsigned long flags; + struct mm_struct *current_mm; + struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; + + if (rga_request_scheduler_job_abort(request) > 0) + return; + + spin_lock_irqsave(&request->lock, flags); + + if (request->is_done) { + spin_unlock_irqrestore(&request->lock, flags); + return; + } + + request->is_running = false; + request->is_done = false; + current_mm = request->current_mm; + request->current_mm = NULL; + + spin_unlock_irqrestore(&request->lock, flags); + + rga_request_put_current_mm(current_mm); + + rga_dma_fence_signal(request->release_fence, err_code); + + mutex_lock(&request_manager->lock); + /* current submit request put */ + rga_request_put(request); + mutex_unlock(&request_manager->lock); +} + +void rga_request_session_destroy_abort(struct rga_session *session) +{ + int request_id; + struct rga_request *request; + struct rga_pending_request_manager *request_manager; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return; + } + + mutex_lock(&request_manager->lock); + + idr_for_each_entry(&request_manager->request_idr, request, request_id) { + if (session == request->session) { + pr_err("[tgid:%d pid:%d] destroy request[%d] when the user exits", + session->tgid, current->pid, request->id); + rga_request_put(request); + } + } + + mutex_unlock(&request_manager->lock); +} + +static int rga_request_timeout_query_state(struct rga_request *request) +{ + int i; + unsigned long flags; + struct rga_scheduler_t *scheduler = NULL; + struct rga_job *job = NULL; + + for (i = 0; i < rga_drvdata->num_of_scheduler; i++) { + scheduler = rga_drvdata->scheduler[i]; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + if (scheduler->running_job) { + job = scheduler->running_job; + if (request->id == job->request_id) { + if (test_bit(RGA_JOB_STATE_DONE, &job->state) && + test_bit(RGA_JOB_STATE_FINISH, &job->state)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + return request->ret; + } else if (!test_bit(RGA_JOB_STATE_DONE, &job->state) && + test_bit(RGA_JOB_STATE_FINISH, &job->state)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + pr_err("request[%d] hardware has finished, but the software has timeout!\n", + request->id); + return -EBUSY; + } else if (!test_bit(RGA_JOB_STATE_DONE, &job->state) && + !test_bit(RGA_JOB_STATE_FINISH, &job->state)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + pr_err("request[%d] hardware has timeout.\n", request->id); + return -EBUSY; + } + } + } + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } + + return request->ret; +} + +static int rga_request_wait(struct rga_request *request) +{ + int left_time; + int ret; + + left_time = wait_event_timeout(request->finished_wq, request->is_done, + RGA_JOB_TIMEOUT_DELAY * request->task_count); + + switch (left_time) { + case 0: + ret = rga_request_timeout_query_state(request); + goto err_request_abort; + case -ERESTARTSYS: + ret = -ERESTARTSYS; + goto err_request_abort; + default: + ret = request->ret; + break; + } + + return ret; + +err_request_abort: + rga_request_release_abort(request, ret); + + return ret; +} + +int rga_request_commit(struct rga_request *request) +{ + int ret; + int i = 0; + struct rga_job *job; + + for (i = 0; i < request->task_count; i++) { + struct rga_req *req = &(request->task_list[i]); + + if (DEBUGGER_EN(MSG)) { + pr_info("commit request[%d] task[%d]:\n", request->id, i); + rga_cmd_print_debug_info(req); + } + + job = rga_job_commit(req, request); + if (IS_ERR(job)) { + pr_err("request[%d] task[%d] job_commit failed.\n", request->id, i); + rga_request_release_abort(request, PTR_ERR(job)); + + return PTR_ERR(job); + } + } + + if (request->sync_mode == RGA_BLIT_SYNC) { + ret = rga_request_wait(request); + if (ret < 0) + return ret; + } + + return 0; +} + +static void rga_request_acquire_fence_signaled_cb(struct dma_fence *fence, + struct dma_fence_cb *_waiter) +{ + int ret; + unsigned long flags; + struct mm_struct *current_mm; + struct rga_fence_waiter *waiter = (struct rga_fence_waiter *)_waiter; + struct rga_request *request = (struct rga_request *)waiter->private; + struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; + + ret = rga_request_commit(request); + if (ret < 0) { + pr_err("acquire_fence callback: rga request[%d] commit failed!\n", request->id); + + spin_lock_irqsave(&request->lock, flags); + + request->is_running = false; + current_mm = request->current_mm; + request->current_mm = NULL; + + spin_unlock_irqrestore(&request->lock, flags); + + rga_request_put_current_mm(current_mm); + + /* + * Since the callback is called while holding &dma_fence.lock, + * the _locked API is used here. + */ + if (dma_fence_get_status_locked(request->release_fence) == 0) + dma_fence_signal_locked(request->release_fence); + } + + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + kfree(waiter); +} + +int rga_request_release_signal(struct rga_scheduler_t *scheduler, struct rga_job *job) +{ + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + struct mm_struct *current_mm; + int finished_count, failed_count; + bool is_finished = false; + unsigned long flags; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return -EFAULT; + } + + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, job->request_id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find internal request from id[%d]", job->request_id); + mutex_unlock(&request_manager->lock); + return -EINVAL; + } + + rga_request_get(request); + mutex_unlock(&request_manager->lock); + + spin_lock_irqsave(&request->lock, flags); + + if (job->ret < 0) { + request->failed_task_count++; + request->ret = job->ret; + } else { + request->finished_task_count++; + } + + failed_count = request->failed_task_count; + finished_count = request->finished_task_count; + + spin_unlock_irqrestore(&request->lock, flags); + + if ((failed_count + finished_count) >= request->task_count) { + spin_lock_irqsave(&request->lock, flags); + + request->is_running = false; + request->is_done = true; + current_mm = request->current_mm; + request->current_mm = NULL; + + spin_unlock_irqrestore(&request->lock, flags); + + rga_request_put_current_mm(current_mm); + + rga_dma_fence_signal(request->release_fence, request->ret); + + is_finished = true; + + if (DEBUGGER_EN(MSG)) + pr_info("request[%d] finished %d failed %d\n", + request->id, finished_count, failed_count); + + /* current submit request put */ + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + } + + mutex_lock(&request_manager->lock); + + if (is_finished) + wake_up(&request->finished_wq); + + rga_request_put(request); + + mutex_unlock(&request_manager->lock); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], job done total cost time %lld us\n", + job->request_id, + ktime_us_delta(ktime_get(), job->timestamp)); + + rga_job_cleanup(job); + + return 0; +} + +struct rga_request *rga_request_config(struct rga_user_request *user_request) +{ + int ret; + unsigned long flags; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + struct rga_req *task_list; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return ERR_PTR(-EFAULT); + } + + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, user_request->id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", user_request->id); + mutex_unlock(&request_manager->lock); + return ERR_PTR(-EINVAL); + } + + rga_request_get(request); + mutex_unlock(&request_manager->lock); + + task_list = kmalloc_array(user_request->task_num, sizeof(struct rga_req), GFP_KERNEL); + if (task_list == NULL) { + pr_err("task_req list alloc error!\n"); + ret = -ENOMEM; + goto err_put_request; + } + + if (unlikely(copy_from_user(task_list, u64_to_user_ptr(user_request->task_ptr), + sizeof(struct rga_req) * user_request->task_num))) { + pr_err("rga_user_request task list copy_from_user failed\n"); + ret = -EFAULT; + goto err_free_task_list; + } + + spin_lock_irqsave(&request->lock, flags); + + request->use_batch_mode = true; + request->task_list = task_list; + request->task_count = user_request->task_num; + request->sync_mode = user_request->sync_mode; + request->mpi_config_flags = user_request->mpi_config_flags; + request->acquire_fence_fd = user_request->acquire_fence_fd; + request->feature = task_list[0].feature; + + spin_unlock_irqrestore(&request->lock, flags); + + return request; + +err_free_task_list: + kfree(task_list); +err_put_request: + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + return ERR_PTR(ret); +} + +struct rga_request *rga_request_kernel_config(struct rga_user_request *user_request) +{ + int ret = 0; + unsigned long flags; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + struct rga_req *task_list; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return ERR_PTR(-EFAULT); + } + + mutex_lock(&request_manager->lock); + + request = rga_request_lookup(request_manager, user_request->id); + if (IS_ERR_OR_NULL(request)) { + pr_err("can not find request from id[%d]", user_request->id); + mutex_unlock(&request_manager->lock); + return ERR_PTR(-EINVAL); + } + + rga_request_get(request); + mutex_unlock(&request_manager->lock); + + task_list = kmalloc_array(user_request->task_num, sizeof(struct rga_req), GFP_KERNEL); + if (task_list == NULL) { + pr_err("task_req list alloc error!\n"); + ret = -ENOMEM; + goto err_put_request; + } + + memcpy(task_list, u64_to_user_ptr(user_request->task_ptr), + sizeof(struct rga_req) * user_request->task_num); + + spin_lock_irqsave(&request->lock, flags); + + request->use_batch_mode = true; + request->task_list = task_list; + request->task_count = user_request->task_num; + request->sync_mode = user_request->sync_mode; + request->mpi_config_flags = user_request->mpi_config_flags; + request->acquire_fence_fd = user_request->acquire_fence_fd; + + spin_unlock_irqrestore(&request->lock, flags); + + return request; + +err_put_request: + mutex_lock(&request_manager->lock); + rga_request_put(request); + mutex_unlock(&request_manager->lock); + + return ERR_PTR(ret); +} + +int rga_request_submit(struct rga_request *request) +{ + int ret = 0; + unsigned long flags; + struct dma_fence *release_fence; + struct mm_struct *current_mm; + + current_mm = rga_request_get_current_mm(request); + + spin_lock_irqsave(&request->lock, flags); + + if (request->is_running) { + spin_unlock_irqrestore(&request->lock, flags); + + pr_err("can not re-config when request is running\n"); + ret = -EFAULT; + goto err_put_current_mm; + } + + if (request->task_list == NULL) { + spin_unlock_irqrestore(&request->lock, flags); + + pr_err("can not find task list from id[%d]\n", request->id); + ret = -EINVAL; + goto err_put_current_mm; + } + + /* Reset */ + request->is_running = true; + request->is_done = false; + request->finished_task_count = 0; + request->failed_task_count = 0; + request->current_mm = current_mm; + + /* Unlock after ensuring that the current request will not be resubmitted. */ + spin_unlock_irqrestore(&request->lock, flags); + + if (request->sync_mode == RGA_BLIT_ASYNC) { + release_fence = rga_dma_fence_alloc(); + if (IS_ERR(release_fence)) { + pr_err("Can not alloc release fence!\n"); + ret = IS_ERR(release_fence); + goto err_reset_request; + } + request->release_fence = release_fence; + + if (request->acquire_fence_fd > 0) { + ret = rga_request_add_acquire_fence_callback( + request->acquire_fence_fd, request, + rga_request_acquire_fence_signaled_cb); + if (ret == 0) { + /* acquire fence active */ + goto export_release_fence_fd; + } else if (ret > 0) { + /* acquire fence has been signaled */ + goto request_commit; + } else { + pr_err("Failed to add callback with acquire fence fd[%d]!\n", + request->acquire_fence_fd); + goto err_put_release_fence; + } + } + } + +request_commit: + ret = rga_request_commit(request); + if (ret < 0) { + pr_err("rga request[%d] commit failed!\n", request->id); + goto err_put_release_fence; + } + +export_release_fence_fd: + if (request->release_fence != NULL) { + ret = rga_dma_fence_get_fd(request->release_fence); + if (ret < 0) { + pr_err("Failed to alloc release fence fd!\n"); + rga_request_release_abort(request, ret); + return ret; + } + + request->release_fence_fd = ret; + } + + return 0; + +err_put_release_fence: + if (request->release_fence != NULL) { + rga_dma_fence_put(request->release_fence); + request->release_fence = NULL; + } + +err_reset_request: + spin_lock_irqsave(&request->lock, flags); + + request->current_mm = NULL; + request->is_running = false; + + spin_unlock_irqrestore(&request->lock, flags); + +err_put_current_mm: + rga_request_put_current_mm(current_mm); + + return ret; +} + +int rga_request_mpi_submit(struct rga_req *req, struct rga_request *request) +{ + int ret = 0; + struct rga_job *job = NULL; + unsigned long flags; + + if (request->sync_mode == RGA_BLIT_ASYNC) { + pr_err("mpi unsupported async mode!\n"); + return -EINVAL; + } + + spin_lock_irqsave(&request->lock, flags); + + if (request->is_running) { + pr_err("can not re-config when request is running"); + spin_unlock_irqrestore(&request->lock, flags); + return -EFAULT; + } + + if (request->task_list == NULL) { + pr_err("can not find task list from id[%d]", request->id); + spin_unlock_irqrestore(&request->lock, flags); + return -EINVAL; + } + + /* Reset */ + request->is_running = true; + request->is_done = false; + request->finished_task_count = 0; + request->failed_task_count = 0; + + spin_unlock_irqrestore(&request->lock, flags); + + job = rga_job_commit(req, request); + if (IS_ERR_OR_NULL(job)) { + pr_err("failed to commit job!\n"); + return job ? PTR_ERR(job) : -EFAULT; + } + + ret = rga_request_wait(request); + if (ret < 0) + return ret; + + return 0; +} + +int rga_request_free(struct rga_request *request) +{ + struct rga_pending_request_manager *request_manager; + struct rga_req *task_list; + unsigned long flags; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return -EFAULT; + } + + WARN_ON(!mutex_is_locked(&request_manager->lock)); + + if (IS_ERR_OR_NULL(request)) { + pr_err("request already freed"); + return -EFAULT; + } + + request_manager->request_count--; + idr_remove(&request_manager->request_idr, request->id); + + spin_lock_irqsave(&request->lock, flags); + + task_list = request->task_list; + + spin_unlock_irqrestore(&request->lock, flags); + + if (task_list != NULL) + kfree(task_list); + + kfree(request); + + return 0; +} + +static void rga_request_kref_release(struct kref *ref) +{ + struct rga_request *request; + struct mm_struct *current_mm; + unsigned long flags; + + request = container_of(ref, struct rga_request, refcount); + + if (rga_dma_fence_get_status(request->release_fence) == 0) + rga_dma_fence_signal(request->release_fence, -EFAULT); + + spin_lock_irqsave(&request->lock, flags); + + rga_dma_fence_put(request->release_fence); + current_mm = request->current_mm; + request->current_mm = NULL; + + if (!request->is_running || request->is_done) { + spin_unlock_irqrestore(&request->lock, flags); + + rga_request_put_current_mm(current_mm); + + goto free_request; + } + + spin_unlock_irqrestore(&request->lock, flags); + + rga_request_put_current_mm(current_mm); + + rga_request_scheduler_job_abort(request); + +free_request: + rga_request_free(request); +} + +/* + * Called at driver close to release the request's id references. + */ +static int rga_request_free_cb(int id, void *ptr, void *data) +{ + return rga_request_free((struct rga_request *)ptr); +} + +int rga_request_alloc(uint32_t flags, struct rga_session *session) +{ + int new_id; + struct rga_pending_request_manager *request_manager; + struct rga_request *request; + + request_manager = rga_drvdata->pend_request_manager; + if (request_manager == NULL) { + pr_err("rga_pending_request_manager is null!\n"); + return -EFAULT; + } + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) { + pr_err("can not kzalloc for rga_request\n"); + return -ENOMEM; + } + + spin_lock_init(&request->lock); + init_waitqueue_head(&request->finished_wq); + + request->pid = current->pid; + request->flags = flags; + request->session = session; + kref_init(&request->refcount); + + /* + * Get the user-visible handle using idr. Preload and perform + * allocation under our spinlock. + */ + mutex_lock(&request_manager->lock); + + idr_preload(GFP_KERNEL); + new_id = idr_alloc_cyclic(&request_manager->request_idr, request, 1, 0, GFP_NOWAIT); + idr_preload_end(); + if (new_id < 0) { + pr_err("request alloc id failed!\n"); + + mutex_unlock(&request_manager->lock); + kfree(request); + return new_id; + } + + request->id = new_id; + request_manager->request_count++; + + mutex_unlock(&request_manager->lock); + + return request->id; +} + +int rga_request_put(struct rga_request *request) +{ + return kref_put(&request->refcount, rga_request_kref_release); +} + +void rga_request_get(struct rga_request *request) +{ + kref_get(&request->refcount); +} + +int rga_request_manager_init(struct rga_pending_request_manager **request_manager_session) +{ + struct rga_pending_request_manager *request_manager = NULL; + + *request_manager_session = kzalloc(sizeof(struct rga_pending_request_manager), GFP_KERNEL); + if (*request_manager_session == NULL) { + pr_err("can not kzalloc for rga_pending_request_manager\n"); + return -ENOMEM; + } + + request_manager = *request_manager_session; + + mutex_init(&request_manager->lock); + + idr_init_base(&request_manager->request_idr, 1); + + return 0; +} + +int rga_request_manager_remove(struct rga_pending_request_manager **request_manager_session) +{ + struct rga_pending_request_manager *request_manager = *request_manager_session; + + mutex_lock(&request_manager->lock); + + idr_for_each(&request_manager->request_idr, &rga_request_free_cb, request_manager); + idr_destroy(&request_manager->request_idr); + + mutex_unlock(&request_manager->lock); + + kfree(*request_manager_session); + + *request_manager_session = NULL; + + return 0; +} diff --git a/drivers/video/rockchip/rga3/rga_mm.c b/drivers/video/rockchip/rga3/rga_mm.c new file mode 100644 index 000000000..51a31f450 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_mm.c @@ -0,0 +1,2229 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Cerf Yu + */ + +#define pr_fmt(fmt) "rga_mm: " fmt + +#include "rga.h" +#include "rga_job.h" +#include "rga_mm.h" +#include "rga_dma_buf.h" +#include "rga_common.h" +#include "rga_iommu.h" +#include "rga_hw_config.h" +#include "rga_debugger.h" + +static void rga_current_mm_read_lock(struct mm_struct *mm) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + mmap_read_lock(mm); +#else + down_read(&mm->mmap_sem); +#endif +} + +static void rga_current_mm_read_unlock(struct mm_struct *mm) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + mmap_read_unlock(mm); +#else + up_read(&mm->mmap_sem); +#endif +} + +static int rga_get_user_pages_from_vma(struct page **pages, unsigned long Memory, + uint32_t pageCount, struct mm_struct *current_mm) +{ + int ret = 0; + int i; + struct vm_area_struct *vma; + spinlock_t *ptl; + pte_t *pte; + pgd_t *pgd; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + p4d_t *p4d; +#endif + pud_t *pud; + pmd_t *pmd; + unsigned long pfn; + + for (i = 0; i < pageCount; i++) { + vma = find_vma(current_mm, (Memory + i) << PAGE_SHIFT); + if (!vma) { + pr_err("page[%d] failed to get vma\n", i); + ret = RGA_OUT_OF_RESOURCES; + break; + } + + pgd = pgd_offset(current_mm, (Memory + i) << PAGE_SHIFT); + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) { + pr_err("page[%d] failed to get pgd\n", i); + ret = RGA_OUT_OF_RESOURCES; + break; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + /* + * In the four-level page table, + * it will do nothing and return pgd. + */ + p4d = p4d_offset(pgd, (Memory + i) << PAGE_SHIFT); + if (p4d_none(*p4d) || unlikely(p4d_bad(*p4d))) { + pr_err("page[%d] failed to get p4d\n", i); + ret = RGA_OUT_OF_RESOURCES; + break; + } + + pud = pud_offset(p4d, (Memory + i) << PAGE_SHIFT); +#else + pud = pud_offset(pgd, (Memory + i) << PAGE_SHIFT); +#endif + + if (pud_none(*pud) || unlikely(pud_bad(*pud))) { + pr_err("page[%d] failed to get pud\n", i); + ret = RGA_OUT_OF_RESOURCES; + break; + } + pmd = pmd_offset(pud, (Memory + i) << PAGE_SHIFT); + if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) { + pr_err("page[%d] failed to get pmd\n", i); + ret = RGA_OUT_OF_RESOURCES; + break; + } + pte = pte_offset_map_lock(current_mm, pmd, + (Memory + i) << PAGE_SHIFT, &ptl); + if (pte_none(*pte)) { + pr_err("page[%d] failed to get pte\n", i); + pte_unmap_unlock(pte, ptl); + ret = RGA_OUT_OF_RESOURCES; + break; + } + + pfn = pte_pfn(*pte); + pages[i] = pfn_to_page(pfn); + pte_unmap_unlock(pte, ptl); + } + + if (ret == RGA_OUT_OF_RESOURCES && i > 0) + pr_err("Only get buffer %d byte from vma, but current image required %d byte", + (int)(i * PAGE_SIZE), (int)(pageCount * PAGE_SIZE)); + + return ret; +} + +static int rga_get_user_pages(struct page **pages, unsigned long Memory, + uint32_t pageCount, int writeFlag, + struct mm_struct *current_mm) +{ + uint32_t i; + int32_t ret = 0; + int32_t result; + + rga_current_mm_read_lock(current_mm); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 168) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + result = get_user_pages(current, current_mm, Memory << PAGE_SHIFT, + pageCount, writeFlag ? FOLL_WRITE : 0, + pages, NULL); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) + result = get_user_pages(current, current_mm, Memory << PAGE_SHIFT, + pageCount, writeFlag ? FOLL_WRITE : 0, 0, pages, NULL); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + result = get_user_pages_remote(current, current_mm, + Memory << PAGE_SHIFT, + pageCount, writeFlag ? FOLL_WRITE : 0, pages, NULL); +#else + result = get_user_pages_remote(current_mm, Memory << PAGE_SHIFT, + pageCount, writeFlag ? FOLL_WRITE : 0, pages, NULL); +#endif + + if (result > 0 && result >= pageCount) { + ret = result; + } else { + if (result > 0) + for (i = 0; i < result; i++) + put_page(pages[i]); + + ret = rga_get_user_pages_from_vma(pages, Memory, pageCount, current_mm); + if (ret < 0 && result > 0) { + pr_err("Only get buffer %d byte from user pages, but current image required %d byte\n", + (int)(result * PAGE_SIZE), (int)(pageCount * PAGE_SIZE)); + } + } + + rga_current_mm_read_unlock(current_mm); + + return ret; +} + +static void rga_free_sgt(struct sg_table **sgt_ptr) +{ + if (sgt_ptr == NULL || *sgt_ptr == NULL) + return; + + sg_free_table(*sgt_ptr); + kfree(*sgt_ptr); + *sgt_ptr = NULL; +} + +static struct sg_table *rga_alloc_sgt(struct rga_virt_addr *virt_addr) +{ + int ret; + struct sg_table *sgt = NULL; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (sgt == NULL) { + pr_err("%s alloc sgt error!\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* get sg form pages. */ + /* iova requires minimum page alignment, so sgt cannot have offset */ + ret = sg_alloc_table_from_pages(sgt, + virt_addr->pages, + virt_addr->page_count, + 0, + virt_addr->size, + GFP_KERNEL); + if (ret) { + pr_err("sg_alloc_table_from_pages failed"); + goto out_free_sgt; + } + + return sgt; + +out_free_sgt: + kfree(sgt); + + return ERR_PTR(ret); +} + +static void rga_free_virt_addr(struct rga_virt_addr **virt_addr_p) +{ + int i; + struct rga_virt_addr *virt_addr = NULL; + + if (virt_addr_p == NULL) + return; + + virt_addr = *virt_addr_p; + if (virt_addr == NULL) + return; + + for (i = 0; i < virt_addr->result; i++) + put_page(virt_addr->pages[i]); + + free_pages((unsigned long)virt_addr->pages, virt_addr->pages_order); + kfree(virt_addr); + *virt_addr_p = NULL; +} + +static int rga_alloc_virt_addr(struct rga_virt_addr **virt_addr_p, + uint64_t viraddr, + struct rga_memory_parm *memory_parm, + int writeFlag, + struct mm_struct *mm) +{ + int i; + int ret; + int result = 0; + int order; + unsigned int count; + int img_size; + size_t offset; + unsigned long size; + struct page **pages = NULL; + struct rga_virt_addr *virt_addr = NULL; + + if (memory_parm->size) + img_size = memory_parm->size; + else + img_size = rga_image_size_cal(memory_parm->width, + memory_parm->height, + memory_parm->format, + NULL, NULL, NULL); + + offset = viraddr & (~PAGE_MASK); + count = RGA_GET_PAGE_COUNT(img_size + offset); + size = count * PAGE_SIZE; + if (!size) { + pr_err("failed to calculating buffer size! size = %ld, count = %d, offset = %ld\n", + size, count, (unsigned long)offset); + rga_dump_memory_parm(memory_parm); + return -EFAULT; + } + + /* alloc pages and page_table */ + order = get_order(count * sizeof(struct page *)); + if (order >= MAX_ORDER) { + pr_err("Can not alloc pages with order[%d] for viraddr pages, max_order = %d\n", + order, MAX_ORDER); + return -ENOMEM; + } + + pages = (struct page **)__get_free_pages(GFP_KERNEL, order); + if (pages == NULL) { + pr_err("%s can not alloc pages for viraddr pages\n", __func__); + return -ENOMEM; + } + + /* get pages from virtual address. */ + ret = rga_get_user_pages(pages, viraddr >> PAGE_SHIFT, count, writeFlag, mm); + if (ret < 0) { + pr_err("failed to get pages from virtual adrees: 0x%lx\n", + (unsigned long)viraddr); + ret = -EINVAL; + goto out_free_pages; + } else if (ret > 0) { + /* For put pages */ + result = ret; + } + + *virt_addr_p = kzalloc(sizeof(struct rga_virt_addr), GFP_KERNEL); + if (*virt_addr_p == NULL) { + pr_err("%s alloc virt_addr error!\n", __func__); + ret = -ENOMEM; + goto out_put_and_free_pages; + } + virt_addr = *virt_addr_p; + + virt_addr->addr = viraddr; + virt_addr->pages = pages; + virt_addr->pages_order = order; + virt_addr->page_count = count; + virt_addr->size = size; + virt_addr->offset = offset; + virt_addr->result = result; + + return 0; + +out_put_and_free_pages: + for (i = 0; i < result; i++) + put_page(pages[i]); +out_free_pages: + free_pages((unsigned long)pages, order); + + return ret; +} + +static inline bool rga_mm_check_memory_limit(struct rga_scheduler_t *scheduler, int mm_flag) +{ + if (!scheduler) + return false; + + if (scheduler->data->mmu == RGA_MMU && + !(mm_flag & RGA_MEM_UNDER_4G)) { + pr_err("%s unsupported memory larger than 4G!\n", + rga_get_mmu_type_str(scheduler->data->mmu)); + return false; + } + + return true; +} + +/* If it is within 0~4G, return 1 (true). */ +static int rga_mm_check_range_sgt(struct sg_table *sgt) +{ + int i; + struct scatterlist *sg; + phys_addr_t s_phys = 0; + + for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { + s_phys = sg_phys(sg); + if ((s_phys > 0xffffffff) || (s_phys + sg->length > 0xffffffff)) + return 0; + } + + return 1; +} + +static inline int rga_mm_check_range_phys_addr(phys_addr_t paddr, size_t size) +{ + return ((paddr + size) <= 0xffffffff); +} + +static inline bool rga_mm_check_contiguous_sgt(struct sg_table *sgt) +{ + if (sgt->orig_nents == 1) + return true; + + return false; +} + +static void rga_mm_unmap_dma_buffer(struct rga_internal_buffer *internal_buffer) +{ + if (rga_mm_is_invalid_dma_buffer(internal_buffer->dma_buffer)) + return; + + rga_dma_unmap_buf(internal_buffer->dma_buffer); + + if (internal_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS && + internal_buffer->phys_addr > 0) + internal_buffer->phys_addr = 0; + + kfree(internal_buffer->dma_buffer); + internal_buffer->dma_buffer = NULL; +} + +static int rga_mm_map_dma_buffer(struct rga_external_buffer *external_buffer, + struct rga_internal_buffer *internal_buffer, + struct rga_job *job) +{ + int ret; + int ex_buffer_size; + uint32_t mm_flag = 0; + phys_addr_t phys_addr = 0; + struct rga_dma_buffer *buffer; + struct device *map_dev; + struct rga_scheduler_t *scheduler; + + scheduler = job ? job->scheduler : + rga_drvdata->scheduler[rga_drvdata->map_scheduler_index]; + if (scheduler == NULL) { + pr_err("Invalid scheduler device!\n"); + return -EINVAL; + } + + if (external_buffer->memory_parm.size) + ex_buffer_size = external_buffer->memory_parm.size; + else + ex_buffer_size = rga_image_size_cal(external_buffer->memory_parm.width, + external_buffer->memory_parm.height, + external_buffer->memory_parm.format, + NULL, NULL, NULL); + if (ex_buffer_size <= 0) { + pr_err("failed to calculating buffer size!\n"); + rga_dump_memory_parm(&external_buffer->memory_parm); + return ex_buffer_size == 0 ? -EINVAL : ex_buffer_size; + } + + /* + * dma-buf api needs to use default_domain of main dev, + * and not IOMMU for devices without iommu_info ptr. + */ + map_dev = scheduler->iommu_info ? scheduler->iommu_info->default_dev : scheduler->dev; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s alloc internal_buffer error!\n", __func__); + return -ENOMEM; + } + + switch (external_buffer->type) { + case RGA_DMA_BUFFER: + ret = rga_dma_map_fd((int)external_buffer->memory, + buffer, DMA_BIDIRECTIONAL, + map_dev); + break; + case RGA_DMA_BUFFER_PTR: + ret = rga_dma_map_buf((struct dma_buf *)u64_to_user_ptr(external_buffer->memory), + buffer, DMA_BIDIRECTIONAL, + map_dev); + break; + default: + ret = -EFAULT; + break; + } + if (ret < 0) { + pr_err("%s core[%d] map dma buffer error!\n", + __func__, scheduler->core); + goto free_buffer; + } + + if (buffer->size < ex_buffer_size) { + pr_err("Only get buffer %ld byte from %s = 0x%lx, but current image required %d byte\n", + buffer->size, rga_get_memory_type_str(external_buffer->type), + (unsigned long)external_buffer->memory, ex_buffer_size); + rga_dump_memory_parm(&external_buffer->memory_parm); + ret = -EINVAL; + goto unmap_buffer; + } + + buffer->scheduler = scheduler; + + if (rga_mm_check_range_sgt(buffer->sgt)) + mm_flag |= RGA_MEM_UNDER_4G; + + /* + * If it's physically contiguous, then the RGA_MMU can + * directly use the physical address. + */ + if (rga_mm_check_contiguous_sgt(buffer->sgt)) { + phys_addr = sg_phys(buffer->sgt->sgl); + if (phys_addr == 0) { + pr_err("%s get physical address error!", __func__); + ret = -EFAULT; + goto unmap_buffer; + } + + mm_flag |= RGA_MEM_PHYSICAL_CONTIGUOUS; + } + + if (!rga_mm_check_memory_limit(scheduler, mm_flag)) { + pr_err("scheduler core[%d] unsupported mm_flag[0x%x]!\n", + scheduler->core, mm_flag); + ret = -EINVAL; + goto unmap_buffer; + } + + internal_buffer->dma_buffer = buffer; + internal_buffer->mm_flag = mm_flag; + internal_buffer->phys_addr = phys_addr ? phys_addr : 0; + + return 0; + +unmap_buffer: + rga_dma_unmap_buf(buffer); + +free_buffer: + kfree(buffer); + + return ret; +} + +static void rga_mm_unmap_virt_addr(struct rga_internal_buffer *internal_buffer) +{ + WARN_ON(internal_buffer->dma_buffer == NULL || internal_buffer->virt_addr == NULL); + + if (rga_mm_is_invalid_dma_buffer(internal_buffer->dma_buffer)) + return; + + switch (internal_buffer->dma_buffer->scheduler->data->mmu) { + case RGA_IOMMU: + rga_iommu_unmap(internal_buffer->dma_buffer); + break; + case RGA_MMU: + dma_unmap_sg(internal_buffer->dma_buffer->scheduler->dev, + internal_buffer->dma_buffer->sgt->sgl, + internal_buffer->dma_buffer->sgt->orig_nents, + DMA_BIDIRECTIONAL); + break; + default: + break; + } + + if (internal_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS && + internal_buffer->phys_addr > 0) + internal_buffer->phys_addr = 0; + + rga_free_sgt(&internal_buffer->dma_buffer->sgt); + + kfree(internal_buffer->dma_buffer); + internal_buffer->dma_buffer = NULL; + + rga_free_virt_addr(&internal_buffer->virt_addr); + + mmput(internal_buffer->current_mm); + mmdrop(internal_buffer->current_mm); + internal_buffer->current_mm = NULL; +} + +static int rga_mm_map_virt_addr(struct rga_external_buffer *external_buffer, + struct rga_internal_buffer *internal_buffer, + struct rga_job *job, int write_flag) +{ + int ret; + uint32_t mm_flag = 0; + phys_addr_t phys_addr = 0; + struct sg_table *sgt; + struct rga_virt_addr *virt_addr; + struct rga_dma_buffer *buffer; + struct rga_scheduler_t *scheduler; + + scheduler = job ? job->scheduler : + rga_drvdata->scheduler[rga_drvdata->map_scheduler_index]; + if (scheduler == NULL) { + pr_err("Invalid scheduler device!\n"); + return -EINVAL; + } + + internal_buffer->current_mm = job ? job->mm : current->mm; + if (internal_buffer->current_mm == NULL) { + pr_err("%s, cannot get current mm!\n", __func__); + return -EFAULT; + } + mmgrab(internal_buffer->current_mm); + mmget(internal_buffer->current_mm); + + ret = rga_alloc_virt_addr(&virt_addr, + external_buffer->memory, + &internal_buffer->memory_parm, + write_flag, internal_buffer->current_mm); + if (ret < 0) { + pr_err("Can not alloc rga_virt_addr from 0x%lx\n", + (unsigned long)external_buffer->memory); + goto put_current_mm; + } + + sgt = rga_alloc_sgt(virt_addr); + if (IS_ERR(sgt)) { + pr_err("alloc sgt error!\n"); + ret = PTR_ERR(sgt); + goto free_virt_addr; + } + + if (rga_mm_check_range_sgt(sgt)) + mm_flag |= RGA_MEM_UNDER_4G; + + if (rga_mm_check_contiguous_sgt(sgt)) { + phys_addr = sg_phys(sgt->sgl); + if (phys_addr == 0) { + pr_err("%s get physical address error!", __func__); + ret = -EFAULT; + goto free_sgt; + } + + mm_flag |= RGA_MEM_PHYSICAL_CONTIGUOUS; + } + + /* + * Some userspace virtual addresses do not have an + * interface for flushing the cache, so it is mandatory + * to flush the cache when the virtual address is used. + */ + mm_flag |= RGA_MEM_FORCE_FLUSH_CACHE; + + if (!rga_mm_check_memory_limit(scheduler, mm_flag)) { + pr_err("scheduler core[%d] unsupported mm_flag[0x%x]!\n", + scheduler->core, mm_flag); + ret = -EINVAL; + goto free_sgt; + } + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s alloc internal dma_buffer error!\n", __func__); + ret = -ENOMEM; + goto free_sgt; + } + + switch (scheduler->data->mmu) { + case RGA_IOMMU: + ret = rga_iommu_map_sgt(sgt, virt_addr->size, buffer, scheduler->dev); + if (ret < 0) { + pr_err("%s core[%d] iommu_map virtual address error!\n", + __func__, scheduler->core); + goto free_dma_buffer; + } + break; + case RGA_MMU: + ret = dma_map_sg(scheduler->dev, sgt->sgl, sgt->orig_nents, DMA_BIDIRECTIONAL); + if (ret == 0) { + pr_err("%s core[%d] dma_map_sgt error! va = 0x%lx, nents = %d\n", + __func__, scheduler->core, + (unsigned long)virt_addr->addr, sgt->orig_nents); + ret = -EINVAL; + goto free_dma_buffer; + } + break; + default: + if (mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + break; + + pr_err("Current %s[%d] cannot support physically discontinuous virtual address!\n", + rga_get_mmu_type_str(scheduler->data->mmu), scheduler->data->mmu); + ret = -EOPNOTSUPP; + goto free_dma_buffer; + } + + buffer->sgt = sgt; + buffer->offset = virt_addr->offset; + buffer->size = virt_addr->size; + buffer->scheduler = scheduler; + + internal_buffer->virt_addr = virt_addr; + internal_buffer->dma_buffer = buffer; + internal_buffer->mm_flag = mm_flag; + internal_buffer->phys_addr = phys_addr ? phys_addr + virt_addr->offset : 0; + + return 0; + +free_dma_buffer: + kfree(buffer); +free_sgt: + rga_free_sgt(&sgt); +free_virt_addr: + rga_free_virt_addr(&virt_addr); +put_current_mm: + mmput(internal_buffer->current_mm); + mmdrop(internal_buffer->current_mm); + internal_buffer->current_mm = NULL; + + return ret; +} + +static void rga_mm_unmap_phys_addr(struct rga_internal_buffer *internal_buffer) +{ + WARN_ON(internal_buffer->dma_buffer == NULL); + + if (rga_mm_is_invalid_dma_buffer(internal_buffer->dma_buffer)) + return; + + if (internal_buffer->dma_buffer->scheduler->data->mmu == RGA_IOMMU) + rga_iommu_unmap(internal_buffer->dma_buffer); + + kfree(internal_buffer->dma_buffer); + internal_buffer->dma_buffer = NULL; + internal_buffer->phys_addr = 0; + internal_buffer->size = 0; +} + +static int rga_mm_map_phys_addr(struct rga_external_buffer *external_buffer, + struct rga_internal_buffer *internal_buffer, + struct rga_job *job) +{ + int ret; + phys_addr_t phys_addr; + int buffer_size; + uint32_t mm_flag = 0; + struct rga_dma_buffer *buffer; + struct rga_scheduler_t *scheduler; + + scheduler = job ? job->scheduler : + rga_drvdata->scheduler[rga_drvdata->map_scheduler_index]; + if (scheduler == NULL) { + pr_err("Invalid scheduler device!\n"); + return -EINVAL; + } + + if (internal_buffer->memory_parm.size) + buffer_size = internal_buffer->memory_parm.size; + else + buffer_size = rga_image_size_cal(internal_buffer->memory_parm.width, + internal_buffer->memory_parm.height, + internal_buffer->memory_parm.format, + NULL, NULL, NULL); + if (buffer_size <= 0) { + pr_err("Failed to get phys addr size!\n"); + rga_dump_memory_parm(&internal_buffer->memory_parm); + return buffer_size == 0 ? -EINVAL : buffer_size; + } + + phys_addr = external_buffer->memory; + mm_flag |= RGA_MEM_PHYSICAL_CONTIGUOUS; + if (rga_mm_check_range_phys_addr(phys_addr, buffer_size)) + mm_flag |= RGA_MEM_UNDER_4G; + + if (!rga_mm_check_memory_limit(scheduler, mm_flag)) { + pr_err("scheduler core[%d] unsupported mm_flag[0x%x]!\n", + scheduler->core, mm_flag); + return -EINVAL; + } + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s alloc internal dma buffer error!\n", __func__); + return -ENOMEM; + } + + if (scheduler->data->mmu == RGA_IOMMU) { + ret = rga_iommu_map(phys_addr, buffer_size, buffer, scheduler->dev); + if (ret < 0) { + pr_err("%s core[%d] map phys_addr error!\n", __func__, scheduler->core); + goto free_dma_buffer; + } + } + + buffer->scheduler = scheduler; + + internal_buffer->phys_addr = phys_addr; + internal_buffer->size = buffer_size; + internal_buffer->mm_flag = mm_flag; + internal_buffer->dma_buffer = buffer; + + return 0; + +free_dma_buffer: + kfree(buffer); + + return ret; +} + +static int rga_mm_unmap_buffer(struct rga_internal_buffer *internal_buffer) +{ + switch (internal_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + rga_mm_unmap_dma_buffer(internal_buffer); + break; + case RGA_VIRTUAL_ADDRESS: + rga_mm_unmap_virt_addr(internal_buffer); + break; + case RGA_PHYSICAL_ADDRESS: + rga_mm_unmap_phys_addr(internal_buffer); + break; + default: + pr_err("Illegal external buffer!\n"); + return -EFAULT; + } + + return 0; +} + +static int rga_mm_map_buffer(struct rga_external_buffer *external_buffer, + struct rga_internal_buffer *internal_buffer, + struct rga_job *job, int write_flag) +{ + int ret; + + memcpy(&internal_buffer->memory_parm, &external_buffer->memory_parm, + sizeof(internal_buffer->memory_parm)); + + switch (external_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + internal_buffer->type = external_buffer->type; + + ret = rga_mm_map_dma_buffer(external_buffer, internal_buffer, job); + if (ret < 0) { + pr_err("%s map dma_buf error!\n", __func__); + return ret; + } + + internal_buffer->size = internal_buffer->dma_buffer->size - + internal_buffer->dma_buffer->offset; + internal_buffer->mm_flag |= RGA_MEM_NEED_USE_IOMMU; + break; + case RGA_VIRTUAL_ADDRESS: + internal_buffer->type = RGA_VIRTUAL_ADDRESS; + + ret = rga_mm_map_virt_addr(external_buffer, internal_buffer, job, write_flag); + if (ret < 0) { + pr_err("%s map virtual address error!\n", __func__); + return ret; + } + + internal_buffer->size = internal_buffer->virt_addr->size - + internal_buffer->virt_addr->offset; + internal_buffer->mm_flag |= RGA_MEM_NEED_USE_IOMMU; + break; + case RGA_PHYSICAL_ADDRESS: + internal_buffer->type = RGA_PHYSICAL_ADDRESS; + + ret = rga_mm_map_phys_addr(external_buffer, internal_buffer, job); + if (ret < 0) { + pr_err("%s map physical address error!\n", __func__); + return ret; + } + + internal_buffer->mm_flag |= RGA_MEM_NEED_USE_IOMMU; + break; + default: + pr_err("Illegal external buffer!\n"); + return -EFAULT; + } + + return 0; +} + +static void rga_mm_kref_release_buffer(struct kref *ref) +{ + struct rga_internal_buffer *internal_buffer; + + internal_buffer = container_of(ref, struct rga_internal_buffer, refcount); + rga_mm_unmap_buffer(internal_buffer); + + idr_remove(&rga_drvdata->mm->memory_idr, internal_buffer->handle); + kfree(internal_buffer); + rga_drvdata->mm->buffer_count--; +} + +/* + * Called at driver close to release the memory's handle references. + */ +static int rga_mm_handle_remove(int id, void *ptr, void *data) +{ + struct rga_internal_buffer *internal_buffer = ptr; + + rga_mm_kref_release_buffer(&internal_buffer->refcount); + + return 0; +} + +static void rga_mm_buffer_destroy(struct rga_internal_buffer *buffer) +{ + rga_mm_kref_release_buffer(&buffer->refcount); +} + +static struct rga_internal_buffer * +rga_mm_lookup_external(struct rga_mm *mm_session, + struct rga_external_buffer *external_buffer, + struct mm_struct *current_mm) +{ + int id; + struct dma_buf *dma_buf = NULL; + struct rga_internal_buffer *temp_buffer = NULL; + struct rga_internal_buffer *output_buffer = NULL; + + WARN_ON(!mutex_is_locked(&mm_session->lock)); + + switch (external_buffer->type) { + case RGA_DMA_BUFFER: + dma_buf = dma_buf_get((int)external_buffer->memory); + if (IS_ERR(dma_buf)) + return (struct rga_internal_buffer *)dma_buf; + + idr_for_each_entry(&mm_session->memory_idr, temp_buffer, id) { + if (temp_buffer->dma_buffer == NULL) + continue; + + if (temp_buffer->dma_buffer[0].dma_buf == dma_buf) { + output_buffer = temp_buffer; + break; + } + } + + dma_buf_put(dma_buf); + break; + case RGA_VIRTUAL_ADDRESS: + idr_for_each_entry(&mm_session->memory_idr, temp_buffer, id) { + if (temp_buffer->virt_addr == NULL) + continue; + + if (temp_buffer->virt_addr->addr == external_buffer->memory) { + if (temp_buffer->current_mm == current_mm) { + output_buffer = temp_buffer; + break; + } + + continue; + } + } + + break; + case RGA_PHYSICAL_ADDRESS: + idr_for_each_entry(&mm_session->memory_idr, temp_buffer, id) { + if (temp_buffer->phys_addr == external_buffer->memory) { + output_buffer = temp_buffer; + break; + } + } + + break; + case RGA_DMA_BUFFER_PTR: + idr_for_each_entry(&mm_session->memory_idr, temp_buffer, id) { + if (temp_buffer->dma_buffer == NULL) + continue; + + if ((unsigned long)temp_buffer->dma_buffer[0].dma_buf == + external_buffer->memory) { + output_buffer = temp_buffer; + break; + } + } + + break; + + default: + pr_err("Illegal external buffer!\n"); + return NULL; + } + + return output_buffer; +} + +struct rga_internal_buffer *rga_mm_lookup_handle(struct rga_mm *mm_session, uint32_t handle) +{ + struct rga_internal_buffer *output_buffer; + + WARN_ON(!mutex_is_locked(&mm_session->lock)); + + output_buffer = idr_find(&mm_session->memory_idr, handle); + + return output_buffer; +} + +int rga_mm_lookup_flag(struct rga_mm *mm_session, uint64_t handle) +{ + struct rga_internal_buffer *output_buffer; + + output_buffer = rga_mm_lookup_handle(mm_session, handle); + if (output_buffer == NULL) { + pr_err("This handle[%ld] is illegal.\n", (unsigned long)handle); + return -EINVAL; + } + + return output_buffer->mm_flag; +} + +dma_addr_t rga_mm_lookup_iova(struct rga_internal_buffer *buffer) +{ + if (rga_mm_is_invalid_dma_buffer(buffer->dma_buffer)) + return 0; + + return buffer->dma_buffer->iova + buffer->dma_buffer->offset; +} + +struct sg_table *rga_mm_lookup_sgt(struct rga_internal_buffer *buffer) +{ + if (rga_mm_is_invalid_dma_buffer(buffer->dma_buffer)) + return NULL; + + return buffer->dma_buffer->sgt; +} + +void rga_mm_dump_buffer(struct rga_internal_buffer *dump_buffer) +{ + pr_info("handle = %d refcount = %d mm_flag = 0x%x\n", + dump_buffer->handle, kref_read(&dump_buffer->refcount), + dump_buffer->mm_flag); + + switch (dump_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + if (rga_mm_is_invalid_dma_buffer(dump_buffer->dma_buffer)) + break; + + pr_info("dma_buffer:\n"); + pr_info("dma_buf = %p, iova = 0x%lx, sgt = %p, size = %ld, map_core = 0x%x\n", + dump_buffer->dma_buffer->dma_buf, + (unsigned long)dump_buffer->dma_buffer->iova, + dump_buffer->dma_buffer->sgt, + dump_buffer->dma_buffer->size, + dump_buffer->dma_buffer->scheduler->core); + + if (dump_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + pr_info("is contiguous, pa = 0x%lx\n", + (unsigned long)dump_buffer->phys_addr); + break; + case RGA_VIRTUAL_ADDRESS: + if (dump_buffer->virt_addr == NULL) + break; + + pr_info("virtual address:\n"); + pr_info("va = 0x%lx, pages = %p, size = %ld\n", + (unsigned long)dump_buffer->virt_addr->addr, + dump_buffer->virt_addr->pages, + dump_buffer->virt_addr->size); + + if (rga_mm_is_invalid_dma_buffer(dump_buffer->dma_buffer)) + break; + + pr_info("iova = 0x%lx, offset = 0x%lx, sgt = %p, size = %ld, map_core = 0x%x\n", + (unsigned long)dump_buffer->dma_buffer->iova, + (unsigned long)dump_buffer->dma_buffer->offset, + dump_buffer->dma_buffer->sgt, + dump_buffer->dma_buffer->size, + dump_buffer->dma_buffer->scheduler->core); + + if (dump_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + pr_info("is contiguous, pa = 0x%lx\n", + (unsigned long)dump_buffer->phys_addr); + break; + case RGA_PHYSICAL_ADDRESS: + pr_info("physical address: pa = 0x%lx\n", (unsigned long)dump_buffer->phys_addr); + break; + default: + pr_err("Illegal external buffer!\n"); + break; + } +} + +void rga_mm_dump_info(struct rga_mm *mm_session) +{ + int id; + struct rga_internal_buffer *dump_buffer; + + WARN_ON(!mutex_is_locked(&mm_session->lock)); + + pr_info("rga mm info:\n"); + + pr_info("buffer count = %d\n", mm_session->buffer_count); + pr_info("===============================================================\n"); + + idr_for_each_entry(&mm_session->memory_idr, dump_buffer, id) { + rga_mm_dump_buffer(dump_buffer); + + pr_info("---------------------------------------------------------------\n"); + } +} + +static bool rga_mm_is_need_mmu(struct rga_job *job, struct rga_internal_buffer *buffer) +{ + if (buffer == NULL || job == NULL || job->scheduler == NULL) + return false; + + /* RK_IOMMU no need to configure enable or not in the driver. */ + if (job->scheduler->data->mmu == RGA_IOMMU) + return false; + + /* RK_MMU need to configure enable or not in the driver. */ + if (buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) + return false; + else if (buffer->mm_flag & RGA_MEM_NEED_USE_IOMMU) + return true; + + return false; +} + +static int rga_mm_set_mmu_flag(struct rga_job *job) +{ + struct rga_mmu_t *mmu_info; + int src_mmu_en; + int src1_mmu_en; + int dst_mmu_en; + int els_mmu_en; + + src_mmu_en = rga_mm_is_need_mmu(job, job->src_buffer.addr); + src1_mmu_en = rga_mm_is_need_mmu(job, job->src1_buffer.addr); + dst_mmu_en = rga_mm_is_need_mmu(job, job->dst_buffer.addr); + els_mmu_en = rga_mm_is_need_mmu(job, job->els_buffer.addr); + + mmu_info = &job->rga_command_base.mmu_info; + memset(mmu_info, 0x0, sizeof(*mmu_info)); + if (src_mmu_en) + mmu_info->mmu_flag |= (0x1 << 8); + if (src1_mmu_en) + mmu_info->mmu_flag |= (0x1 << 9); + if (dst_mmu_en) + mmu_info->mmu_flag |= (0x1 << 10); + if (els_mmu_en) + mmu_info->mmu_flag |= (0x1 << 11); + + if (mmu_info->mmu_flag & (0xf << 8)) { + mmu_info->mmu_flag |= 1; + mmu_info->mmu_flag |= 1 << 31; + mmu_info->mmu_en = 1; + } + + return 0; +} + +static int rga_mm_sgt_to_page_table(struct sg_table *sg, + uint32_t *page_table, + int32_t pageCount, + int32_t use_dma_address) +{ + uint32_t i; + unsigned long Address; + uint32_t mapped_size = 0; + uint32_t len; + struct scatterlist *sgl = sg->sgl; + uint32_t sg_num = 0; + uint32_t break_flag = 0; + + do { + /* + * The length of each sgl is expected to be obtained here, not + * the length of the entire dma_buf, so sg_dma_len() is not used. + */ + len = sgl->length >> PAGE_SHIFT; + + if (use_dma_address) + /* + * The fd passed by user space gets sg through + * dma_buf_map_attachment, so dma_address can + * be use here. + * When the mapped device does not have iommu, it will + * return the first address of the real physical page + * when it meets the requirements of the current device, + * and will trigger swiotlb when it does not meet the + * requirements to obtain a software-mapped physical + * address that is mapped to meet the device address + * requirements. + */ + Address = sg_dma_address(sgl); + else + Address = sg_phys(sgl); + + for (i = 0; i < len; i++) { + if (mapped_size + i >= pageCount) { + break_flag = 1; + break; + } + page_table[mapped_size + i] = (uint32_t)(Address + (i << PAGE_SHIFT)); + } + if (break_flag) + break; + mapped_size += len; + sg_num += 1; + } while ((sgl = sg_next(sgl)) && (mapped_size < pageCount) && (sg_num < sg->orig_nents)); + + return 0; +} + +static int rga_mm_set_mmu_base(struct rga_job *job, + struct rga_img_info_t *img, + struct rga_job_buffer *job_buf) +{ + int ret; + int yrgb_count = 0; + int uv_count = 0; + int v_count = 0; + int page_count = 0; + int order = 0; + uint32_t *page_table = NULL; + struct sg_table *sgt = NULL; + + int img_size, yrgb_size, uv_size, v_size; + int img_offset = 0; + int yrgb_offset = 0; + int uv_offset = 0; + int v_offset = 0; + + img_size = rga_image_size_cal(img->vir_w, img->vir_h, img->format, + &yrgb_size, &uv_size, &v_size); + if (img_size <= 0) { + pr_err("Image size cal error! width = %d, height = %d, format = %s\n", + img->vir_w, img->vir_h, rga_get_format_name(img->format)); + return -EINVAL; + } + + /* using third-address */ + if (job_buf->uv_addr) { + if (job_buf->y_addr->virt_addr != NULL) + yrgb_offset = job_buf->y_addr->virt_addr->offset; + if (job_buf->uv_addr->virt_addr != NULL) + uv_offset = job_buf->uv_addr->virt_addr->offset; + if (job_buf->v_addr->virt_addr != NULL) + v_offset = job_buf->v_addr->virt_addr->offset; + + yrgb_count = RGA_GET_PAGE_COUNT(yrgb_size + yrgb_offset); + uv_count = RGA_GET_PAGE_COUNT(uv_size + uv_offset); + v_count = RGA_GET_PAGE_COUNT(v_size + v_offset); + page_count = yrgb_count + uv_count + v_count; + + if (page_count <= 0) { + pr_err("page count cal error! yrba = %d, uv = %d, v = %d\n", + yrgb_count, uv_count, v_count); + return -EFAULT; + } + + if (job->flags & RGA_JOB_USE_HANDLE) { + order = get_order(page_count * sizeof(uint32_t *)); + if (order >= MAX_ORDER) { + pr_err("Can not alloc pages with order[%d] for page_table, max_order = %d\n", + order, MAX_ORDER); + return -ENOMEM; + } + + page_table = (uint32_t *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order); + if (page_table == NULL) { + pr_err("%s can not alloc pages for page_table, order = %d\n", + __func__, order); + return -ENOMEM; + } + } else { + mutex_lock(&rga_drvdata->lock); + + page_table = rga_mmu_buf_get(rga_drvdata->mmu_base, page_count); + if (page_table == NULL) { + pr_err("mmu_buf get error!\n"); + mutex_unlock(&rga_drvdata->lock); + return -EFAULT; + } + + mutex_unlock(&rga_drvdata->lock); + } + + sgt = rga_mm_lookup_sgt(job_buf->y_addr); + if (sgt == NULL) { + pr_err("rga2 cannot get sgt from internal buffer!\n"); + ret = -EINVAL; + goto err_free_page_table; + } + rga_mm_sgt_to_page_table(sgt, page_table, yrgb_count, false); + + sgt = rga_mm_lookup_sgt(job_buf->uv_addr); + if (sgt == NULL) { + pr_err("rga2 cannot get sgt from internal buffer!\n"); + ret = -EINVAL; + goto err_free_page_table; + } + rga_mm_sgt_to_page_table(sgt, page_table + yrgb_count, uv_count, false); + + sgt = rga_mm_lookup_sgt(job_buf->v_addr); + if (sgt == NULL) { + pr_err("rga2 cannot get sgt from internal buffer!\n"); + ret = -EINVAL; + goto err_free_page_table; + } + rga_mm_sgt_to_page_table(sgt, page_table + yrgb_count + uv_count, v_count, false); + + img->yrgb_addr = yrgb_offset; + img->uv_addr = (yrgb_count << PAGE_SHIFT) + uv_offset; + img->v_addr = ((yrgb_count + uv_count) << PAGE_SHIFT) + v_offset; + } else { + if (job_buf->addr->virt_addr != NULL) + img_offset = job_buf->addr->virt_addr->offset; + + page_count = RGA_GET_PAGE_COUNT(img_size + img_offset); + if (page_count < 0) { + pr_err("page count cal error! yrba = %d, uv = %d, v = %d\n", + yrgb_count, uv_count, v_count); + return -EFAULT; + } + + if (job->flags & RGA_JOB_USE_HANDLE) { + order = get_order(page_count * sizeof(uint32_t *)); + if (order >= MAX_ORDER) { + pr_err("Can not alloc pages with order[%d] for page_table, max_order = %d\n", + order, MAX_ORDER); + return -ENOMEM; + } + + page_table = (uint32_t *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order); + if (page_table == NULL) { + pr_err("%s can not alloc pages for page_table, order = %d\n", + __func__, order); + return -ENOMEM; + } + } else { + mutex_lock(&rga_drvdata->lock); + + page_table = rga_mmu_buf_get(rga_drvdata->mmu_base, page_count); + if (page_table == NULL) { + pr_err("mmu_buf get error!\n"); + mutex_unlock(&rga_drvdata->lock); + return -EFAULT; + } + + mutex_unlock(&rga_drvdata->lock); + } + + sgt = rga_mm_lookup_sgt(job_buf->addr); + if (sgt == NULL) { + pr_err("rga2 cannot get sgt from internal buffer!\n"); + ret = -EINVAL; + goto err_free_page_table; + } + rga_mm_sgt_to_page_table(sgt, page_table, page_count, false); + + img->yrgb_addr = img_offset; + rga_convert_addr(img, false); + } + + job_buf->page_table = page_table; + job_buf->order = order; + job_buf->page_count = page_count; + + return 0; + +err_free_page_table: + if (job->flags & RGA_JOB_USE_HANDLE) + free_pages((unsigned long)page_table, order); + return ret; +} + +static int rga_mm_sync_dma_sg_for_device(struct rga_internal_buffer *buffer, + struct rga_job *job, + enum dma_data_direction dir) +{ + struct sg_table *sgt; + struct rga_scheduler_t *scheduler; + + scheduler = buffer->dma_buffer->scheduler; + if (scheduler == NULL) { + pr_err("%s(%d), failed to get scheduler, core = 0x%x\n", + __func__, __LINE__, job->core); + return -EFAULT; + } + + if (buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS && + scheduler->data->mmu != RGA_IOMMU) { + dma_sync_single_for_device(scheduler->dev, buffer->phys_addr, buffer->size, dir); + } else { + sgt = rga_mm_lookup_sgt(buffer); + if (sgt == NULL) { + pr_err("%s(%d), failed to get sgt, core = 0x%x\n", + __func__, __LINE__, job->core); + return -EINVAL; + } + + dma_sync_sg_for_device(scheduler->dev, sgt->sgl, sgt->orig_nents, dir); + } + + return 0; +} + +static int rga_mm_sync_dma_sg_for_cpu(struct rga_internal_buffer *buffer, + struct rga_job *job, + enum dma_data_direction dir) +{ + struct sg_table *sgt; + struct rga_scheduler_t *scheduler; + + scheduler = buffer->dma_buffer->scheduler; + if (scheduler == NULL) { + pr_err("%s(%d), failed to get scheduler, core = 0x%x\n", + __func__, __LINE__, job->core); + return -EFAULT; + } + + if (buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS && + scheduler->data->mmu != RGA_IOMMU) { + dma_sync_single_for_cpu(scheduler->dev, buffer->phys_addr, buffer->size, dir); + } else { + sgt = rga_mm_lookup_sgt(buffer); + if (sgt == NULL) { + pr_err("%s(%d), failed to get sgt, core = 0x%x\n", + __func__, __LINE__, job->core); + return -EINVAL; + } + + dma_sync_sg_for_cpu(scheduler->dev, sgt->sgl, sgt->orig_nents, dir); + } + + return 0; +} + +static int rga_mm_get_buffer_info(struct rga_job *job, + struct rga_internal_buffer *internal_buffer, + uint64_t *channel_addr) +{ + uint64_t addr; + + switch (job->scheduler->data->mmu) { + case RGA_IOMMU: + addr = rga_mm_lookup_iova(internal_buffer); + if (addr == 0) { + pr_err("core[%d] lookup buffer_type[0x%x] iova error!\n", + job->core, internal_buffer->type); + return -EINVAL; + } + break; + case RGA_MMU: + default: + if (internal_buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) { + addr = internal_buffer->phys_addr; + break; + } + + switch (internal_buffer->type) { + case RGA_DMA_BUFFER: + case RGA_DMA_BUFFER_PTR: + addr = 0; + break; + case RGA_VIRTUAL_ADDRESS: + addr = internal_buffer->virt_addr->addr; + break; + case RGA_PHYSICAL_ADDRESS: + addr = internal_buffer->phys_addr; + break; + default: + pr_err("Illegal external buffer!\n"); + return -EFAULT; + } + break; + } + + *channel_addr = addr; + + return 0; +} + +static int rga_mm_get_buffer(struct rga_mm *mm, + struct rga_job *job, + uint64_t handle, + uint64_t *channel_addr, + struct rga_internal_buffer **buf, + int require_size, + enum dma_data_direction dir) +{ + int ret = 0; + struct rga_internal_buffer *internal_buffer = NULL; + + if (handle == 0) { + pr_err("No buffer handle can be used!\n"); + return -EFAULT; + } + + mutex_lock(&mm->lock); + *buf = rga_mm_lookup_handle(mm, handle); + if (*buf == NULL) { + pr_err("This handle[%ld] is illegal.\n", (unsigned long)handle); + + mutex_unlock(&mm->lock); + return -EFAULT; + } + + internal_buffer = *buf; + kref_get(&internal_buffer->refcount); + + if (DEBUGGER_EN(MM)) { + pr_info("handle[%d] get info:\n", (int)handle); + rga_mm_dump_buffer(internal_buffer); + } + + mutex_unlock(&mm->lock); + + ret = rga_mm_get_buffer_info(job, internal_buffer, channel_addr); + if (ret < 0) { + pr_err("handle[%ld] failed to get internal buffer info!\n", (unsigned long)handle); + return ret; + } + + if (internal_buffer->size < require_size) { + ret = -EINVAL; + pr_err("Only get buffer %ld byte from handle[%ld], but current required %d byte\n", + internal_buffer->size, (unsigned long)handle, require_size); + + goto put_internal_buffer; + } + + if (internal_buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE) { + /* + * Some userspace virtual addresses do not have an + * interface for flushing the cache, so it is mandatory + * to flush the cache when the virtual address is used. + */ + ret = rga_mm_sync_dma_sg_for_device(internal_buffer, job, dir); + if (ret < 0) { + pr_err("sync sgt for device error!\n"); + goto put_internal_buffer; + } + } + + return 0; + +put_internal_buffer: + mutex_lock(&mm->lock); + kref_put(&internal_buffer->refcount, rga_mm_kref_release_buffer); + mutex_unlock(&mm->lock); + + return ret; + +} + +static void rga_mm_put_buffer(struct rga_mm *mm, + struct rga_job *job, + struct rga_internal_buffer *internal_buffer, + enum dma_data_direction dir) +{ + if (internal_buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE && dir != DMA_NONE) + if (rga_mm_sync_dma_sg_for_cpu(internal_buffer, job, dir)) + pr_err("sync sgt for cpu error!\n"); + + if (DEBUGGER_EN(MM)) { + pr_info("handle[%d] put info:\n", (int)internal_buffer->handle); + rga_mm_dump_buffer(internal_buffer); + } + + mutex_lock(&mm->lock); + kref_put(&internal_buffer->refcount, rga_mm_kref_release_buffer); + mutex_unlock(&mm->lock); +} + +static void rga_mm_put_channel_handle_info(struct rga_mm *mm, + struct rga_job *job, + struct rga_job_buffer *job_buf, + enum dma_data_direction dir) +{ + if (job_buf->y_addr) + rga_mm_put_buffer(mm, job, job_buf->y_addr, dir); + if (job_buf->uv_addr) + rga_mm_put_buffer(mm, job, job_buf->uv_addr, dir); + if (job_buf->v_addr) + rga_mm_put_buffer(mm, job, job_buf->v_addr, dir); + + if (job_buf->page_table) + free_pages((unsigned long)job_buf->page_table, job_buf->order); +} + +static int rga_mm_get_channel_handle_info(struct rga_mm *mm, + struct rga_job *job, + struct rga_img_info_t *img, + struct rga_job_buffer *job_buf, + enum dma_data_direction dir) +{ + int ret = 0; + int handle = 0; + int img_size, yrgb_size, uv_size, v_size; + + img_size = rga_image_size_cal(img->vir_w, img->vir_h, img->format, + &yrgb_size, &uv_size, &v_size); + if (img_size <= 0) { + pr_err("Image size cal error! width = %d, height = %d, format = %s\n", + img->vir_w, img->vir_h, rga_get_format_name(img->format)); + return -EINVAL; + } + + /* using third-address */ + if (img->uv_addr > 0) { + handle = img->yrgb_addr; + if (handle > 0) { + ret = rga_mm_get_buffer(mm, job, handle, &img->yrgb_addr, + &job_buf->y_addr, yrgb_size, dir); + if (ret < 0) { + pr_err("handle[%d] Can't get y/rgb address info!\n", handle); + return ret; + } + } + + handle = img->uv_addr; + if (handle > 0) { + ret = rga_mm_get_buffer(mm, job, handle, &img->uv_addr, + &job_buf->uv_addr, uv_size, dir); + if (ret < 0) { + pr_err("handle[%d] Can't get uv address info!\n", handle); + return ret; + } + } + + handle = img->v_addr; + if (handle > 0) { + ret = rga_mm_get_buffer(mm, job, handle, &img->v_addr, + &job_buf->v_addr, v_size, dir); + if (ret < 0) { + pr_err("handle[%d] Can't get uv address info!\n", handle); + return ret; + } + } + } else { + handle = img->yrgb_addr; + if (handle > 0) { + ret = rga_mm_get_buffer(mm, job, handle, &img->yrgb_addr, + &job_buf->addr, img_size, dir); + if (ret < 0) { + pr_err("handle[%d] Can't get y/rgb address info!\n", handle); + return ret; + } + } + + rga_convert_addr(img, false); + } + + if (job->scheduler->data->mmu == RGA_MMU && + rga_mm_is_need_mmu(job, job_buf->addr)) { + ret = rga_mm_set_mmu_base(job, img, job_buf); + if (ret < 0) { + pr_err("Can't set RGA2 MMU_BASE from handle!\n"); + + rga_mm_put_channel_handle_info(mm, job, job_buf, dir); + return ret; + } + } + + return 0; +} + +static int rga_mm_get_handle_info(struct rga_job *job) +{ + int ret = 0; + struct rga_req *req = NULL; + struct rga_mm *mm = NULL; + enum dma_data_direction dir; + + req = &job->rga_command_base; + mm = rga_drvdata->mm; + + switch (req->render_mode) { + case BITBLT_MODE: + case COLOR_PALETTE_MODE: + if (unlikely(req->src.yrgb_addr <= 0)) { + pr_err("render_mode[0x%x] src0 channel handle[%ld] must is valid!", + req->render_mode, (unsigned long)req->src.yrgb_addr); + return -EINVAL; + } + + if (unlikely(req->dst.yrgb_addr <= 0)) { + pr_err("render_mode[0x%x] dst channel handle[%ld] must is valid!", + req->render_mode, (unsigned long)req->dst.yrgb_addr); + return -EINVAL; + } + + if (req->bsfilter_flag) { + if (unlikely(req->pat.yrgb_addr <= 0)) { + pr_err("render_mode[0x%x] src1/pat channel handle[%ld] must is valid!", + req->render_mode, (unsigned long)req->pat.yrgb_addr); + return -EINVAL; + } + } + + break; + case COLOR_FILL_MODE: + if (unlikely(req->dst.yrgb_addr <= 0)) { + pr_err("render_mode[0x%x] dst channel handle[%ld] must is valid!", + req->render_mode, (unsigned long)req->dst.yrgb_addr); + return -EINVAL; + } + + break; + + case UPDATE_PALETTE_TABLE_MODE: + case UPDATE_PATTEN_BUF_MODE: + if (unlikely(req->pat.yrgb_addr <= 0)) { + pr_err("render_mode[0x%x] lut/pat channel handle[%ld] must is valid!, req->render_mode", + req->render_mode, (unsigned long)req->pat.yrgb_addr); + return -EINVAL; + } + + break; + default: + pr_err("%s, unknown render mode!\n", __func__); + break; + } + + if (likely(req->src.yrgb_addr > 0)) { + ret = rga_mm_get_channel_handle_info(mm, job, &req->src, + &job->src_buffer, + DMA_TO_DEVICE); + if (ret < 0) { + pr_err("Can't get src buffer info from handle!\n"); + return ret; + } + } + + if (likely(req->dst.yrgb_addr > 0)) { + ret = rga_mm_get_channel_handle_info(mm, job, &req->dst, + &job->dst_buffer, + DMA_TO_DEVICE); + if (ret < 0) { + pr_err("Can't get dst buffer info from handle!\n"); + return ret; + } + } + + if (likely(req->pat.yrgb_addr > 0)) { + + if (req->render_mode != UPDATE_PALETTE_TABLE_MODE) { + if (req->bsfilter_flag) + dir = DMA_BIDIRECTIONAL; + else + dir = DMA_TO_DEVICE; + + ret = rga_mm_get_channel_handle_info(mm, job, &req->pat, + &job->src1_buffer, + dir); + } else { + ret = rga_mm_get_channel_handle_info(mm, job, &req->pat, + &job->els_buffer, + DMA_BIDIRECTIONAL); + } + if (ret < 0) { + pr_err("Can't get pat buffer info from handle!\n"); + return ret; + } + } + + rga_mm_set_mmu_flag(job); + + return 0; +} + +static void rga_mm_put_handle_info(struct rga_job *job) +{ + struct rga_mm *mm = rga_drvdata->mm; + + rga_mm_put_channel_handle_info(mm, job, &job->src_buffer, DMA_NONE); + rga_mm_put_channel_handle_info(mm, job, &job->dst_buffer, DMA_FROM_DEVICE); + rga_mm_put_channel_handle_info(mm, job, &job->src1_buffer, DMA_NONE); + rga_mm_put_channel_handle_info(mm, job, &job->els_buffer, DMA_NONE); +} + +static void rga_mm_put_channel_external_buffer(struct rga_job_buffer *job_buffer) +{ + if (job_buffer->ex_addr->type == RGA_DMA_BUFFER_PTR) + dma_buf_put((struct dma_buf *)(unsigned long)job_buffer->ex_addr->memory); + + kfree(job_buffer->ex_addr); + job_buffer->ex_addr = NULL; +} + +static int rga_mm_get_channel_external_buffer(int mmu_flag, + struct rga_img_info_t *img_info, + struct rga_job_buffer *job_buffer) +{ + struct dma_buf *dma_buf = NULL; + struct rga_external_buffer *external_buffer = NULL; + + /* Default unsupported multi-planar format */ + external_buffer = kzalloc(sizeof(*external_buffer), GFP_KERNEL); + if (external_buffer == NULL) { + pr_err("Cannot alloc job_buffer!\n"); + return -ENOMEM; + } + + if (img_info->yrgb_addr) { + dma_buf = dma_buf_get(img_info->yrgb_addr); + if (IS_ERR(dma_buf)) { + pr_err("%s dma_buf_get fail fd[%lu]\n", + __func__, (unsigned long)img_info->yrgb_addr); + kfree(external_buffer); + return -EINVAL; + } + + external_buffer->memory = (unsigned long)dma_buf; + external_buffer->type = RGA_DMA_BUFFER_PTR; + } else if (mmu_flag && img_info->uv_addr) { + external_buffer->memory = (uint64_t)img_info->uv_addr; + external_buffer->type = RGA_VIRTUAL_ADDRESS; + } else if (img_info->uv_addr) { + external_buffer->memory = (uint64_t)img_info->uv_addr; + external_buffer->type = RGA_PHYSICAL_ADDRESS; + } else { + kfree(external_buffer); + return -EINVAL; + } + + external_buffer->memory_parm.width = img_info->vir_w; + external_buffer->memory_parm.height = img_info->vir_h; + external_buffer->memory_parm.format = img_info->format; + + job_buffer->ex_addr = external_buffer; + + return 0; +} + +static void rga_mm_put_external_buffer(struct rga_job *job) +{ + if (job->src_buffer.ex_addr) + rga_mm_put_channel_external_buffer(&job->src_buffer); + if (job->src1_buffer.ex_addr) + rga_mm_put_channel_external_buffer(&job->src1_buffer); + if (job->dst_buffer.ex_addr) + rga_mm_put_channel_external_buffer(&job->dst_buffer); + if (job->els_buffer.ex_addr) + rga_mm_put_channel_external_buffer(&job->els_buffer); +} + +static int rga_mm_get_external_buffer(struct rga_job *job) +{ + int ret = -EINVAL; + int mmu_flag; + + struct rga_img_info_t *src0 = NULL; + struct rga_img_info_t *src1 = NULL; + struct rga_img_info_t *dst = NULL; + struct rga_img_info_t *els = NULL; + + if (job->rga_command_base.render_mode != COLOR_FILL_MODE) + src0 = &job->rga_command_base.src; + + if (job->rga_command_base.render_mode != UPDATE_PALETTE_TABLE_MODE) + src1 = job->rga_command_base.bsfilter_flag ? + &job->rga_command_base.pat : NULL; + else + els = &job->rga_command_base.pat; + + dst = &job->rga_command_base.dst; + + if (likely(src0)) { + mmu_flag = ((job->rga_command_base.mmu_info.mmu_flag >> 8) & 1); + ret = rga_mm_get_channel_external_buffer(mmu_flag, src0, &job->src_buffer); + if (ret < 0) { + pr_err("Cannot get src0 channel buffer!\n"); + return ret; + } + } + + if (likely(dst)) { + mmu_flag = ((job->rga_command_base.mmu_info.mmu_flag >> 10) & 1); + ret = rga_mm_get_channel_external_buffer(mmu_flag, dst, &job->dst_buffer); + if (ret < 0) { + pr_err("Cannot get dst channel buffer!\n"); + goto error_put_buffer; + } + } + + if (src1) { + mmu_flag = ((job->rga_command_base.mmu_info.mmu_flag >> 9) & 1); + ret = rga_mm_get_channel_external_buffer(mmu_flag, src1, &job->src1_buffer); + if (ret < 0) { + pr_err("Cannot get src1 channel buffer!\n"); + goto error_put_buffer; + } + } + + if (els) { + mmu_flag = ((job->rga_command_base.mmu_info.mmu_flag >> 11) & 1); + ret = rga_mm_get_channel_external_buffer(mmu_flag, els, &job->els_buffer); + if (ret < 0) { + pr_err("Cannot get els channel buffer!\n"); + goto error_put_buffer; + } + } + + return 0; +error_put_buffer: + rga_mm_put_external_buffer(job); + return ret; +} + +static void rga_mm_unmap_channel_job_buffer(struct rga_job *job, + struct rga_job_buffer *job_buffer, + enum dma_data_direction dir) +{ + if (job_buffer->addr->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE && dir != DMA_NONE) + if (rga_mm_sync_dma_sg_for_cpu(job_buffer->addr, job, dir)) + pr_err("sync sgt for cpu error!\n"); + + rga_mm_unmap_buffer(job_buffer->addr); + kfree(job_buffer->addr); + + job_buffer->page_table = NULL; +} + +static int rga_mm_map_channel_job_buffer(struct rga_job *job, + struct rga_img_info_t *img, + struct rga_job_buffer *job_buffer, + enum dma_data_direction dir, + int write_flag) +{ + int ret; + struct rga_internal_buffer *buffer = NULL; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s alloc internal_buffer error!\n", __func__); + return -ENOMEM; + } + + ret = rga_mm_map_buffer(job_buffer->ex_addr, buffer, job, write_flag); + if (ret < 0) { + pr_err("job buffer map failed!\n"); + goto error_free_buffer; + } + + ret = rga_mm_get_buffer_info(job, buffer, &img->yrgb_addr); + if (ret < 0) { + pr_err("Failed to get internal buffer info!\n"); + goto error_unmap_buffer; + } + + if (buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE) { + ret = rga_mm_sync_dma_sg_for_device(buffer, job, dir); + if (ret < 0) { + pr_err("sync sgt for device error!\n"); + goto error_unmap_buffer; + } + } + + rga_convert_addr(img, false); + + job_buffer->addr = buffer; + + if (job->scheduler->data->mmu == RGA_MMU && + rga_mm_is_need_mmu(job, job_buffer->addr)) { + ret = rga_mm_set_mmu_base(job, img, job_buffer); + if (ret < 0) { + pr_err("Can't set RGA2 MMU_BASE!\n"); + job_buffer->addr = NULL; + goto error_unmap_buffer; + } + } + + return 0; + +error_unmap_buffer: + rga_mm_unmap_buffer(buffer); +error_free_buffer: + kfree(buffer); + + return ret; +} + +static void rga_mm_unmap_buffer_info(struct rga_job *job) +{ + if (job->src_buffer.addr) + rga_mm_unmap_channel_job_buffer(job, &job->src_buffer, DMA_NONE); + if (job->dst_buffer.addr) + rga_mm_unmap_channel_job_buffer(job, &job->dst_buffer, DMA_FROM_DEVICE); + if (job->src1_buffer.addr) + rga_mm_unmap_channel_job_buffer(job, &job->src1_buffer, DMA_NONE); + if (job->els_buffer.addr) + rga_mm_unmap_channel_job_buffer(job, &job->els_buffer, DMA_NONE); + + rga_mm_put_external_buffer(job); +} + +static int rga_mm_map_buffer_info(struct rga_job *job) +{ + int ret = 0; + struct rga_req *req = NULL; + enum dma_data_direction dir; + + ret = rga_mm_get_external_buffer(job); + if (ret < 0) { + pr_err("failed to get external buffer from job_cmd!\n"); + return ret; + } + + req = &job->rga_command_base; + + if (likely(job->src_buffer.ex_addr)) { + ret = rga_mm_map_channel_job_buffer(job, &req->src, + &job->src_buffer, + DMA_TO_DEVICE, false); + if (ret < 0) { + pr_err("src channel map job buffer failed!"); + goto error_unmap_buffer; + } + } + + if (likely(job->dst_buffer.ex_addr)) { + ret = rga_mm_map_channel_job_buffer(job, &req->dst, + &job->dst_buffer, + DMA_TO_DEVICE, true); + if (ret < 0) { + pr_err("dst channel map job buffer failed!"); + goto error_unmap_buffer; + } + } + + if (job->src1_buffer.ex_addr) { + if (req->bsfilter_flag) + dir = DMA_BIDIRECTIONAL; + else + dir = DMA_TO_DEVICE; + + ret = rga_mm_map_channel_job_buffer(job, &req->pat, + &job->src1_buffer, + dir, false); + if (ret < 0) { + pr_err("src1 channel map job buffer failed!"); + goto error_unmap_buffer; + } + } + + if (job->els_buffer.ex_addr) { + ret = rga_mm_map_channel_job_buffer(job, &req->pat, + &job->els_buffer, + DMA_BIDIRECTIONAL, false); + if (ret < 0) { + pr_err("els channel map job buffer failed!"); + goto error_unmap_buffer; + } + } + + rga_mm_set_mmu_flag(job); + return 0; + +error_unmap_buffer: + rga_mm_unmap_buffer_info(job); + + return ret; +} + +int rga_mm_map_job_info(struct rga_job *job) +{ + int ret; + ktime_t timestamp = ktime_get(); + + if (job->flags & RGA_JOB_USE_HANDLE) { + ret = rga_mm_get_handle_info(job); + if (ret < 0) { + pr_err("failed to get buffer from handle\n"); + return ret; + } + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], get buffer_handle info cost %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + } else { + ret = rga_mm_map_buffer_info(job); + if (ret < 0) { + pr_err("failed to map buffer\n"); + return ret; + } + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], map buffer cost %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + } + + return 0; +} + +void rga_mm_unmap_job_info(struct rga_job *job) +{ + ktime_t timestamp = ktime_get(); + + if (job->flags & RGA_JOB_USE_HANDLE) { + rga_mm_put_handle_info(job); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], put buffer_handle info cost %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + } else { + rga_mm_unmap_buffer_info(job); + + if (DEBUGGER_EN(TIME)) + pr_info("request[%d], unmap buffer cost %lld us\n", + job->request_id, ktime_us_delta(ktime_get(), timestamp)); + } +} + +/* + * rga_mm_import_buffer - Importing external buffer into the RGA driver + * + * @external_buffer: [in] Parameters of external buffer + * @session: [in] Session of the current process + * + * returns: + * if return value > 0, the buffer import is successful and is the generated + * buffer-handle, negative error code on failure. + */ +int rga_mm_import_buffer(struct rga_external_buffer *external_buffer, + struct rga_session *session) +{ + int ret = 0, new_id; + struct rga_mm *mm; + struct rga_internal_buffer *internal_buffer; + + mm = rga_drvdata->mm; + if (mm == NULL) { + pr_err("rga mm is null!\n"); + return -EFAULT; + } + + mutex_lock(&mm->lock); + + /* first, Check whether to rga_mm */ + internal_buffer = rga_mm_lookup_external(mm, external_buffer, current->mm); + if (!IS_ERR_OR_NULL(internal_buffer)) { + kref_get(&internal_buffer->refcount); + + mutex_unlock(&mm->lock); + + if (DEBUGGER_EN(MM)) { + pr_info("import existing buffer:\n"); + rga_mm_dump_buffer(internal_buffer); + } + + return internal_buffer->handle; + } + + /* finally, map and cached external_buffer in rga_mm */ + internal_buffer = kzalloc(sizeof(struct rga_internal_buffer), GFP_KERNEL); + if (internal_buffer == NULL) { + pr_err("%s alloc internal_buffer error!\n", __func__); + + mutex_unlock(&mm->lock); + return -ENOMEM; + } + + ret = rga_mm_map_buffer(external_buffer, internal_buffer, NULL, true); + if (ret < 0) + goto FREE_INTERNAL_BUFFER; + + kref_init(&internal_buffer->refcount); + internal_buffer->session = session; + + /* + * Get the user-visible handle using idr. Preload and perform + * allocation under our spinlock. + */ + idr_preload(GFP_KERNEL); + new_id = idr_alloc_cyclic(&mm->memory_idr, internal_buffer, 1, 0, GFP_NOWAIT); + idr_preload_end(); + if (new_id < 0) { + pr_err("internal_buffer alloc id failed!\n"); + ret = new_id; + goto FREE_INTERNAL_BUFFER; + } + + internal_buffer->handle = new_id; + mm->buffer_count++; + + if (DEBUGGER_EN(MM)) { + pr_info("import buffer:\n"); + rga_mm_dump_buffer(internal_buffer); + } + + mutex_unlock(&mm->lock); + return internal_buffer->handle; + +FREE_INTERNAL_BUFFER: + mutex_unlock(&mm->lock); + kfree(internal_buffer); + + return ret; +} + +int rga_mm_release_buffer(uint32_t handle) +{ + struct rga_mm *mm; + struct rga_internal_buffer *internal_buffer; + + mm = rga_drvdata->mm; + if (mm == NULL) { + pr_err("rga mm is null!\n"); + return -EFAULT; + } + + mutex_lock(&mm->lock); + + /* Find the buffer that has been imported */ + internal_buffer = rga_mm_lookup_handle(mm, handle); + if (IS_ERR_OR_NULL(internal_buffer)) { + pr_err("This is not a buffer that has been imported, handle = %d\n", (int)handle); + + mutex_unlock(&mm->lock); + return -ENOENT; + } + + if (DEBUGGER_EN(MM)) { + pr_info("release buffer:\n"); + rga_mm_dump_buffer(internal_buffer); + } + + kref_put(&internal_buffer->refcount, rga_mm_kref_release_buffer); + + mutex_unlock(&mm->lock); + return 0; +} + +int rga_mm_session_release_buffer(struct rga_session *session) +{ + int i; + struct rga_mm *mm; + struct rga_internal_buffer *buffer; + + mm = rga_drvdata->mm; + if (mm == NULL) { + pr_err("rga mm is null!\n"); + return -EFAULT; + } + + mutex_lock(&mm->lock); + + idr_for_each_entry(&mm->memory_idr, buffer, i) { + if (session == buffer->session) { + pr_err("[tgid:%d] Destroy handle[%d] when the user exits\n", + session->tgid, buffer->handle); + rga_mm_buffer_destroy(buffer); + } + } + + mutex_unlock(&mm->lock); + return 0; +} + +int rga_mm_init(struct rga_mm **mm_session) +{ + struct rga_mm *mm = NULL; + + *mm_session = kzalloc(sizeof(struct rga_mm), GFP_KERNEL); + if (*mm_session == NULL) { + pr_err("can not kzalloc for rga buffer mm_session\n"); + return -ENOMEM; + } + + mm = *mm_session; + + mutex_init(&mm->lock); + idr_init_base(&mm->memory_idr, 1); + + return 0; +} + +int rga_mm_remove(struct rga_mm **mm_session) +{ + struct rga_mm *mm = *mm_session; + + mutex_lock(&mm->lock); + + idr_for_each(&mm->memory_idr, &rga_mm_handle_remove, mm); + idr_destroy(&mm->memory_idr); + + mutex_unlock(&mm->lock); + + kfree(*mm_session); + *mm_session = NULL; + + return 0; +} diff --git a/drivers/video/rockchip/rga3/rga_policy.c b/drivers/video/rockchip/rga3/rga_policy.c new file mode 100644 index 000000000..c87ce1881 --- /dev/null +++ b/drivers/video/rockchip/rga3/rga_policy.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rga_policy: " fmt + +#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; + 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(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; + pr_info("unsupported width stride %d, 0x%x should be %d aligned!", + w_stride, format, align); + } + + return false; +} + +static bool rga_check_src0(const struct rga_hw_data *data, + struct rga_img_info_t *src0) +{ + if (!rga_check_resolution(&data->input_range, src0->act_w, src0->act_h)) + return false; + + if (data == &rga3_data && + !rga_check_resolution(&data->input_range, + src0->act_w + src0->x_offset, + src0->act_h + src0->y_offset)) + return false; + + if (!rga_check_format(data, src0->rd_mode, src0->format, 0)) + return false; + + if (!rga_check_align(data->byte_stride_align, src0->format, src0->vir_w)) + return false; + + return true; +} + +static bool rga_check_src1(const struct rga_hw_data *data, + struct rga_img_info_t *src1) +{ + if (!rga_check_resolution(&data->input_range, src1->act_w, src1->act_h)) + return false; + + if (data == &rga3_data && + !rga_check_resolution(&data->input_range, + src1->act_w + src1->x_offset, + src1->act_h + src1->y_offset)) + return false; + + if (!rga_check_format(data, src1->rd_mode, src1->format, 1)) + return false; + + if (!rga_check_align(data->byte_stride_align, src1->format, src1->vir_w)) + return false; + + return true; +} + +static bool rga_check_dst(const struct rga_hw_data *data, + struct rga_img_info_t *dst) +{ + if (!rga_check_resolution(&data->output_range, dst->act_w, dst->act_h)) + return false; + + if (data == &rga3_data && + !rga_check_resolution(&data->output_range, + dst->act_w + dst->x_offset, + dst->act_h + dst->y_offset)) + return false; + + if (!rga_check_format(data, dst->rd_mode, dst->format, 2)) + return false; + + if (!rga_check_align(data->byte_stride_align, dst->format, dst->vir_w)) + return false; + + return true; +} + +static bool rga_check_scale(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; + + if ((rga_base->sina == 65536 && rga_base->cosa == 0) + || (rga_base->sina == -65536 && rga_base->cosa == 0)) { + dw = dst->act_h; + dh = dst->act_w; + } else { + dw = dst->act_w; + dh = dst->act_h; + } + + if (sw > dw) { + if ((sw >> data->max_downscale_factor) > dw) + return false; + } else if (sw < dw) { + if ((sw << data->max_upscale_factor) < dw) + return false; + } + + if (sh > dh) { + if ((sh >> data->max_downscale_factor) > dh) + return false; + } else if (sh < dh) { + if ((sh << data->max_upscale_factor) < dh) + return false; + } + + return true; +} + +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) { + pr_err("invalid setting core by user\n"); + goto finish; + } 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)) + pr_info("start policy on core = %d", scheduler->core); + + if (scheduler->data->mmu == RGA_MMU && + job->flags & RGA_JOB_UNSUPPORT_RGA_MMU) { + if (DEBUGGER_EN(MSG)) + pr_info("RGA2 only support under 4G memory!\n"); + continue; + } + + if (feature > 0) { + if (!(feature & data->feature)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on feature", + 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)) || + (!(src1->rd_mode & data->win[1].rd_mode)) || + (!(dst->rd_mode & data->win[2].rd_mode))) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, ABC break on rd_mode", + scheduler->core); + continue; + } + } else { + if ((!(src0->rd_mode & data->win[0].rd_mode)) || + (!(dst->rd_mode & data->win[2].rd_mode))) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, ABB break on rd_mode", + scheduler->core); + continue; + } + } + + if (!rga_check_scale(data, rga_base)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on rga_check_scale", + scheduler->core); + continue; + } + + if (!rga_check_src0(data, src0)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on rga_check_src0", + scheduler->core); + continue; + } + + if (src1->yrgb_addr > 0) { + if (!rga_check_src1(data, src1)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on rga_check_src1", + scheduler->core); + continue; + } + } + } + + if (!rga_check_dst(data, dst)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on rga_check_dst", + scheduler->core); + continue; + } + + if (!rga_check_csc(data, rga_base)) { + if (DEBUGGER_EN(MSG)) + pr_info("core = %d, break on rga_check_csc", + scheduler->core); + continue; + } + + optional_cores |= scheduler->core; + } + + if (DEBUGGER_EN(MSG)) + pr_info("optional_cores = %d\n", optional_cores); + + if (optional_cores == 0) { + core = -1; + pr_err("invalid function policy\n"); + goto finish; + } + + 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 */ +finish: + if (DEBUGGER_EN(MSG)) + pr_info("assign core: %d\n", core); + + return core; +}