diff options
Diffstat (limited to 'drivers/video/b2r2/b2r2_node_split.c')
-rw-r--r-- | drivers/video/b2r2/b2r2_node_split.c | 3513 |
1 files changed, 3513 insertions, 0 deletions
diff --git a/drivers/video/b2r2/b2r2_node_split.c b/drivers/video/b2r2/b2r2_node_split.c new file mode 100644 index 00000000000..556e5f69e07 --- /dev/null +++ b/drivers/video/b2r2/b2r2_node_split.c @@ -0,0 +1,3513 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * ST-Ericsson B2R2 node splitter + * + * Author: Fredrik Allansson <fredrik.allansson@stericsson.com> + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + +#include "b2r2_debug.h" +#include "b2r2_node_split.h" +#include "b2r2_internal.h" +#include "b2r2_hw.h" +#include "b2r2_filters.h" + +#include <linux/kernel.h> + +/* + * Macros and constants + */ +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define INSTANCES_DEFAULT_SIZE 10 +#define INSTANCES_GROW_SIZE 5 + +/* + * Internal types + */ + + +/* + * Global variables + */ + +/** + * VMX values for different color space conversions + */ +static const u32 vmx_rgb_to_yuv[] = { + B2R2_VMX0_RGB_TO_YUV_601_VIDEO, + B2R2_VMX1_RGB_TO_YUV_601_VIDEO, + B2R2_VMX2_RGB_TO_YUV_601_VIDEO, + B2R2_VMX3_RGB_TO_YUV_601_VIDEO, +}; + +static const u32 vmx_rgb_to_blt_yuv888[] = { + B2R2_VMX0_RGB_TO_BLT_YUV888_601_VIDEO, + B2R2_VMX1_RGB_TO_BLT_YUV888_601_VIDEO, + B2R2_VMX2_RGB_TO_BLT_YUV888_601_VIDEO, + B2R2_VMX3_RGB_TO_BLT_YUV888_601_VIDEO, +}; + +static const u32 vmx_yuv_to_rgb[] = { + B2R2_VMX0_YUV_TO_RGB_601_VIDEO, + B2R2_VMX1_YUV_TO_RGB_601_VIDEO, + B2R2_VMX2_YUV_TO_RGB_601_VIDEO, + B2R2_VMX3_YUV_TO_RGB_601_VIDEO, +}; + +static const u32 vmx_blt_yuv888_to_rgb[] = { + B2R2_VMX0_BLT_YUV888_TO_RGB_601_VIDEO, + B2R2_VMX1_BLT_YUV888_TO_RGB_601_VIDEO, + B2R2_VMX2_BLT_YUV888_TO_RGB_601_VIDEO, + B2R2_VMX3_BLT_YUV888_TO_RGB_601_VIDEO, +}; + +static const u32 vmx_yuv_to_blt_yuv888[] = { + B2R2_VMX0_YUV_TO_BLT_YUV888, + B2R2_VMX1_YUV_TO_BLT_YUV888, + B2R2_VMX2_YUV_TO_BLT_YUV888, + B2R2_VMX3_YUV_TO_BLT_YUV888, +}; + +static const u32 vmx_blt_yuv888_to_yuv[] = { + B2R2_VMX0_BLT_YUV888_TO_YUV, + B2R2_VMX1_BLT_YUV888_TO_YUV, + B2R2_VMX2_BLT_YUV888_TO_YUV, + B2R2_VMX3_BLT_YUV888_TO_YUV, +}; + +static const u32 vmx_yvu_to_blt_yuv888[] = { + B2R2_VMX0_YVU_TO_BLT_YUV888, + B2R2_VMX1_YVU_TO_BLT_YUV888, + B2R2_VMX2_YVU_TO_BLT_YUV888, + B2R2_VMX3_YVU_TO_BLT_YUV888, +}; + +static const u32 vmx_blt_yuv888_to_yvu[] = { + B2R2_VMX0_BLT_YUV888_TO_YVU, + B2R2_VMX1_BLT_YUV888_TO_YVU, + B2R2_VMX2_BLT_YUV888_TO_YVU, + B2R2_VMX3_BLT_YUV888_TO_YVU, +}; + +static const u32 vmx_yvu_to_rgb[] = { + B2R2_VMX0_YVU_TO_RGB_601_VIDEO, + B2R2_VMX1_YVU_TO_RGB_601_VIDEO, + B2R2_VMX2_YVU_TO_RGB_601_VIDEO, + B2R2_VMX3_YVU_TO_RGB_601_VIDEO, +}; + +static const u32 vmx_rgb_to_yvu[] = { + B2R2_VMX0_RGB_TO_YVU_601_VIDEO, + B2R2_VMX1_RGB_TO_YVU_601_VIDEO, + B2R2_VMX2_RGB_TO_YVU_601_VIDEO, + B2R2_VMX3_RGB_TO_YVU_601_VIDEO, +}; + +static const u32 vmx_rgb_to_bgr[] = { + B2R2_VMX0_RGB_TO_BGR, + B2R2_VMX1_RGB_TO_BGR, + B2R2_VMX2_RGB_TO_BGR, + B2R2_VMX3_RGB_TO_BGR, +}; + +static const u32 vmx_bgr_to_yuv[] = { + B2R2_VMX0_BGR_TO_YUV_601_VIDEO, + B2R2_VMX1_BGR_TO_YUV_601_VIDEO, + B2R2_VMX2_BGR_TO_YUV_601_VIDEO, + B2R2_VMX3_BGR_TO_YUV_601_VIDEO, +}; + +static const u32 vmx_yuv_to_bgr[] = { + B2R2_VMX0_YUV_TO_BGR_601_VIDEO, + B2R2_VMX1_YUV_TO_BGR_601_VIDEO, + B2R2_VMX2_YUV_TO_BGR_601_VIDEO, + B2R2_VMX3_YUV_TO_BGR_601_VIDEO, +}; + +static const u32 vmx_bgr_to_yvu[] = { + B2R2_VMX0_BGR_TO_YVU_601_VIDEO, + B2R2_VMX1_BGR_TO_YVU_601_VIDEO, + B2R2_VMX2_BGR_TO_YVU_601_VIDEO, + B2R2_VMX3_BGR_TO_YVU_601_VIDEO, +}; + +static const u32 vmx_yvu_to_bgr[] = { + B2R2_VMX0_YVU_TO_BGR_601_VIDEO, + B2R2_VMX1_YVU_TO_BGR_601_VIDEO, + B2R2_VMX2_YVU_TO_BGR_601_VIDEO, + B2R2_VMX3_YVU_TO_BGR_601_VIDEO, +}; + +static const u32 vmx_yvu_to_yuv[] = { + B2R2_VMX0_YVU_TO_YUV_601_VIDEO, + B2R2_VMX1_YVU_TO_YUV_601_VIDEO, + B2R2_VMX2_YVU_TO_YUV_601_VIDEO, + B2R2_VMX3_YVU_TO_YUV_601_VIDEO, +}; + +/* + * Forward declaration of private functions + */ + +static int analyze_fmt_conv(struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, + const u32 **vmx, u32 *node_count); +static int analyze_color_fill(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count); +static int analyze_copy(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count); +static int analyze_scaling(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count); +static int analyze_rotate(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count); +static int analyze_transform(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count); +static int analyze_rot_scale(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count); +static int analyze_scale_factors(struct b2r2_node_split_job *this); + +static void configure_src(struct b2r2_node *node, + struct b2r2_node_split_buf *src, const u32 *ivmx); +static int configure_dst(struct b2r2_node *node, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next); +static void configure_blend(struct b2r2_node *node, u32 flags, u32 global_alpha, + bool swap_fg_bg); +static void configure_clip(struct b2r2_node *node, + struct b2r2_blt_rect *clip_rect); + +static int configure_tile(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next); +static void configure_direct_fill(struct b2r2_node *node, u32 color, + struct b2r2_node_split_buf *dst, struct b2r2_node **next); +static int configure_fill(struct b2r2_node *node, u32 color, + enum b2r2_blt_fmt fmt, struct b2r2_node_split_buf *dst, + const u32 *ivmx, struct b2r2_node **next); +static void configure_direct_copy(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, struct b2r2_node **next); +static int configure_copy(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next, + struct b2r2_node_split_job *this); +static int configure_rotate(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next, + struct b2r2_node_split_job *this); +static int configure_scale(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, u16 h_rsf, u16 v_rsf, + const u32 *ivmx, struct b2r2_node **next, + struct b2r2_node_split_job *this); +static int configure_rot_scale(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next); + +static int check_rect(const struct b2r2_blt_img *img, + const struct b2r2_blt_rect *rect, + const struct b2r2_blt_rect *clip); +static void set_buf(struct b2r2_node_split_buf *buf, u32 addr, + const struct b2r2_blt_img *img, + const struct b2r2_blt_rect *rect, bool color_fill, u32 color); +static int setup_tmp_buf(struct b2r2_node_split_buf *this, u32 max_size, + enum b2r2_blt_fmt pref_fmt, u32 pref_width, u32 pref_height); + +static enum b2r2_ty get_alpha_range(enum b2r2_blt_fmt fmt); +static u32 set_alpha(enum b2r2_blt_fmt fmt, u8 alpha, u32 color); +static u8 get_alpha(enum b2r2_blt_fmt fmt, u32 pixel); +static bool fmt_has_alpha(enum b2r2_blt_fmt fmt); + +static bool is_rgb_fmt(enum b2r2_blt_fmt fmt); +static bool is_bgr_fmt(enum b2r2_blt_fmt fmt); +static bool is_yuv_fmt(enum b2r2_blt_fmt fmt); +static bool is_yvu_fmt(enum b2r2_blt_fmt fmt); +static bool is_yuv420_fmt(enum b2r2_blt_fmt fmt); +static bool is_yuv422_fmt(enum b2r2_blt_fmt fmt); +static bool is_yuv444_fmt(enum b2r2_blt_fmt fmt); +static bool is_yvu420_fmt(enum b2r2_blt_fmt fmt); +static bool is_yvu422_fmt(enum b2r2_blt_fmt fmt); + +static int fmt_byte_pitch(enum b2r2_blt_fmt fmt, u32 width); +static enum b2r2_native_fmt to_native_fmt(enum b2r2_blt_fmt fmt); +static u32 to_RGB888(u32 color, const enum b2r2_blt_fmt fmt); +static enum b2r2_fmt_type get_fmt_type(enum b2r2_blt_fmt fmt); + +static bool is_transform(const struct b2r2_blt_request *req); +static int calculate_scale_factor(u32 from, u32 to, u16 *sf_out); +static s32 rescale(s32 dim, u16 sf); +static s32 inv_rescale(s32 dim, u16 sf); + +static void set_target(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf); +static void set_src(struct b2r2_src_config *src, u32 addr, + struct b2r2_node_split_buf *buf); +static void set_src_1(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf); +static void set_src_2(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf); +static void set_src_3(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf); +static void set_ivmx(struct b2r2_node *node, const u32 *vmx_values); + +static void reset_nodes(struct b2r2_node *node); + +/* + * Public functions + */ + +/** + * b2r2_node_split_analyze() - analyzes the request + */ +int b2r2_node_split_analyze(const struct b2r2_blt_request *req, + u32 max_buf_size, u32 *node_count, struct b2r2_work_buf **bufs, + u32 *buf_count, struct b2r2_node_split_job *this) +{ + int ret; + bool color_fill; + + b2r2_log_info("%s\n", __func__); + + memset(this, 0, sizeof(*this)); + + /* Copy parameters */ + this->flags = req->user_req.flags; + this->transform = req->user_req.transform; + this->max_buf_size = max_buf_size; + this->global_alpha = req->user_req.global_alpha; + this->buf_count = 0; + this->node_count = 0; + + if (this->flags & B2R2_BLT_FLAG_BLUR) { + ret = -ENOSYS; + goto unsupported; + } + + /* Unsupported formats on src */ + switch (req->user_req.src_img.fmt) { + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + if (is_bgr_fmt(req->user_req.dst_img.fmt)) { + ret = -ENOSYS; + goto unsupported; + } + break; + default: + break; + } + + /* Unsupported formats on dst */ + switch (req->user_req.dst_img.fmt) { + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + if (is_bgr_fmt(req->user_req.src_img.fmt)) { + ret = -ENOSYS; + goto unsupported; + } + break; + default: + break; + } + + if ((this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) && + (is_yuv_fmt(req->user_req.src_img.fmt) || + req->user_req.src_img.fmt == B2R2_BLT_FMT_1_BIT_A1 || + req->user_req.src_img.fmt == B2R2_BLT_FMT_8_BIT_A8)) { + b2r2_log_warn("%s: Unsupported: source color keying with " + "YUV or pure alpha formats.\n", + __func__); + ret = -ENOSYS; + goto unsupported; + } + + if (this->flags & (B2R2_BLT_FLAG_DEST_COLOR_KEY | + B2R2_BLT_FLAG_SOURCE_MASK)) { + b2r2_log_warn("%s: Unsupported: source mask, " + "destination color keying.\n", + __func__); + ret = -ENOSYS; + goto unsupported; + } + + if ((req->user_req.flags & B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) && + req->user_req.clut == NULL) { + b2r2_log_warn("%s: Invalid request: no table specified " + "for CLUT color correction.\n", + __func__); + return -EINVAL; + } + + /* Check for color fill */ + color_fill = (this->flags & (B2R2_BLT_FLAG_SOURCE_FILL | + B2R2_BLT_FLAG_SOURCE_FILL_RAW)) != 0; + + /* Configure the source and destination buffers */ + set_buf(&this->src, req->src_resolved.physical_address, + &req->user_req.src_img, &req->user_req.src_rect, + color_fill, req->user_req.src_color); + + set_buf(&this->dst, req->dst_resolved.physical_address, + &req->user_req.dst_img, &req->user_req.dst_rect, false, + 0); + + b2r2_log_info("%s:\n" + "\t\tsrc.rect=(%4d, %4d, %4d, %4d)\t" + "dst.rect=(%4d, %4d, %4d, %4d)\n", __func__, this->src.rect.x, + this->src.rect.y, this->src.rect.width, this->src.rect.height, + this->dst.rect.x, this->dst.rect.y, this->dst.rect.width, + this->dst.rect.height); + + if (this->flags & B2R2_BLT_FLAG_DITHER) + this->dst.dither = B2R2_TTY_RGB_ROUND_DITHER; + + if (this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) + this->flag_param = req->user_req.src_color; + + /* Check for blending */ + if ((this->flags & B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND) && + (this->global_alpha != 255)) + this->blend = true; + else if (this->flags & B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND) + this->blend = (color_fill && fmt_has_alpha(this->dst.fmt)) || + fmt_has_alpha(this->src.fmt); + + if (this->blend && this->src.type == B2R2_FMT_TYPE_PLANAR) { + b2r2_log_warn("%s: Unsupported: blend with planar source\n", + __func__); + ret = -ENOSYS; + goto unsupported; + } + + /* Check for clipping */ + this->clip = (this->flags & + B2R2_BLT_FLAG_DESTINATION_CLIP) != 0; + if (this->clip) { + s32 l = req->user_req.dst_clip_rect.x; + s32 r = l + req->user_req.dst_clip_rect.width; + s32 t = req->user_req.dst_clip_rect.y; + s32 b = t + req->user_req.dst_clip_rect.height; + + /* Intersect the clip and buffer rects */ + if (l < 0) + l = 0; + if (r > req->user_req.dst_img.width) + r = req->user_req.dst_img.width; + if (t < 0) + t = 0; + if (b > req->user_req.dst_img.height) + b = req->user_req.dst_img.height; + + this->clip_rect.x = l; + this->clip_rect.y = t; + this->clip_rect.width = r - l; + this->clip_rect.height = b - t; + } else { + /* Set the clip rectangle to the buffer bounds */ + this->clip_rect.x = 0; + this->clip_rect.y = 0; + this->clip_rect.width = req->user_req.dst_img.width; + this->clip_rect.height = req->user_req.dst_img.height; + } + + /* Validate the destination */ + ret = check_rect(&req->user_req.dst_img, &req->user_req.dst_rect, + &this->clip_rect); + if (ret < 0) + goto error; + + /* Validate the source (if not color fill) */ + if (!color_fill) { + ret = check_rect(&req->user_req.src_img, + &req->user_req.src_rect, NULL); + if (ret < 0) + goto error; + } + + /* Do the analysis depending on the type of operation */ + if (color_fill) { + ret = analyze_color_fill(this, req, &this->node_count); + } else { + + bool upsample; + bool downsample; + + /* + * YUV formats that are non-raster, non-yuv444 needs to be + * up (or down) sampled using the resizer. + * + * NOTE: The resizer needs to be enabled for YUV444 as well, + * even though there is no upsampling. This is most + * likely a bug in the hardware. + */ + upsample = this->src.type != B2R2_FMT_TYPE_RASTER && + is_yuv_fmt(this->src.fmt); + downsample = this->dst.type != B2R2_FMT_TYPE_RASTER && + is_yuv_fmt(this->dst.fmt); + + if (is_transform(req) || upsample || downsample) + ret = analyze_transform(this, req, &this->node_count, + &this->buf_count); + else + ret = analyze_copy(this, req, &this->node_count, + &this->buf_count); + } + + if (ret == -ENOSYS) { + goto unsupported; + } else if (ret < 0) { + b2r2_log_warn("%s: Analysis failed!\n", __func__); + goto error; + } + + /* Setup the origin and movement of the destination window */ + if (this->dst.hso == B2R2_TY_HSO_RIGHT_TO_LEFT) { + this->dst.dx = -this->dst.win.width; + this->dst.win.x = this->dst.rect.x + this->dst.rect.width - 1; + } else { + this->dst.dx = this->dst.win.width; + this->dst.win.x = this->dst.rect.x; + } + if (this->dst.vso == B2R2_TY_VSO_BOTTOM_TO_TOP) { + this->dst.dy = -this->dst.win.height; + this->dst.win.y = this->dst.rect.y + this->dst.rect.height - 1; + } else { + this->dst.dy = this->dst.win.height; + this->dst.win.y = this->dst.rect.y; + } + + *buf_count = this->buf_count; + *node_count = this->node_count; + + if (this->buf_count > 0) + *bufs = &this->work_bufs[0]; + + b2r2_log_info("%s: dst.win=(%d, %d, %d, %d), dst.dx=%d, dst.dy=%d\n", + __func__, this->dst.win.x, this->dst.win.y, + this->dst.win.width, this->dst.win.height, this->dst.dx, + this->dst.dy); + if (this->buf_count > 0) + b2r2_log_info("%s: buf_count=%d, buf_size=%d, node_count=%d\n", + __func__, *buf_count, bufs[0]->size, *node_count); + else + b2r2_log_info("%s: buf_count=%d, node_count=%d\n", + __func__, *buf_count, *node_count); + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); +unsupported: + return ret; +} + +/** + * b2r2_node_split_configure() - configures the node list + */ +int b2r2_node_split_configure(struct b2r2_node_split_job *this, + struct b2r2_node *first) +{ + int ret; + + struct b2r2_node_split_buf *dst = &this->dst; + struct b2r2_node *node = first; + + u32 x_pixels = 0; + u32 y_pixels = 0; + + reset_nodes(node); + + while (y_pixels < dst->rect.height) { + s32 dst_x = dst->win.x; + s32 dst_w = dst->win.width; + + /* Clamp window height */ + if (dst->win.height > dst->rect.height - y_pixels) + dst->win.height = dst->rect.height - y_pixels; + + while (x_pixels < dst->rect.width) { + + /* Clamp window width */ + if (dst_w > dst->rect.width - x_pixels) + dst->win.width = dst->rect.width - x_pixels; + + ret = configure_tile(this, node, &node); + if (ret < 0) + goto error; + + dst->win.x += dst->dx; + x_pixels += max(dst->dx, -dst->dx); + b2r2_log_info("%s: x_pixels=%d\n", __func__, x_pixels); + } + + dst->win.y += dst->dy; + y_pixels += max(dst->dy, -dst->dy); + + dst->win.x = dst_x; + dst->win.width = dst_w; + x_pixels = 0; + + b2r2_log_info("%s: y_pixels=%d\n", __func__, y_pixels); + } + + return 0; + +error: + b2r2_log_warn("%s: error!\n", __func__); + return ret; +} + +/** + * b2r2_node_split_assign_buffers() - assigns temporary buffers to the node list + */ +int b2r2_node_split_assign_buffers(struct b2r2_node_split_job *this, + struct b2r2_node *first, struct b2r2_work_buf *bufs, + u32 buf_count) +{ + struct b2r2_node *node = first; + + while (node != NULL) { + /* The indices are offset by one */ + if (node->dst_tmp_index) { + BUG_ON(node->dst_tmp_index > buf_count); + + b2r2_log_info("%s: assigning buf %d as dst\n", + __func__, node->dst_tmp_index); + + node->node.GROUP1.B2R2_TBA = + bufs[node->dst_tmp_index - 1].phys_addr; + } + if (node->src_tmp_index) { + u32 addr = bufs[node->src_tmp_index - 1].phys_addr; + + b2r2_log_info("%s: " + "assigning buf %d as src %d ", __func__, + node->src_tmp_index, node->src_index); + + BUG_ON(node->src_tmp_index > buf_count); + + switch (node->src_index) { + case 1: + b2r2_log_info("1\n"); + node->node.GROUP3.B2R2_SBA = addr; + break; + case 2: + b2r2_log_info("2\n"); + node->node.GROUP4.B2R2_SBA = addr; + break; + case 3: + b2r2_log_info("3\n"); + node->node.GROUP5.B2R2_SBA = addr; + break; + default: + BUG_ON(1); + break; + } + } + + b2r2_log_info("%s: tba=%p\tsba=%p\n", __func__, + (void *)node->node.GROUP1.B2R2_TBA, + (void *)node->node.GROUP4.B2R2_SBA); + + node = node->next; + } + + return 0; +} + +/** + * b2r2_node_split_unassign_buffers() - releases temporary buffers + */ +void b2r2_node_split_unassign_buffers(struct b2r2_node_split_job *this, + struct b2r2_node *first) +{ + return; +} + +/** + * b2r2_node_split_cancel() - cancels and releases a job instance + */ +void b2r2_node_split_cancel(struct b2r2_node_split_job *this) +{ + memset(this, 0, sizeof(*this)); + + return; +} + +/* + * Private functions + */ + +static int check_rect(const struct b2r2_blt_img *img, + const struct b2r2_blt_rect *rect, + const struct b2r2_blt_rect *clip) +{ + int ret; + + s32 l, r, b, t; + + /* Check rectangle dimensions*/ + if ((rect->width <= 0) || (rect->height <= 0)) { + b2r2_log_warn("%s: " + "Illegal rect (%d, %d, %d, %d)\n", + __func__, rect->x, rect->y, rect->width, rect->height); + ret = -EINVAL; + goto error; + } + + /* If we are using clip we should only look at the intersection of the + rects */ + if (clip) { + l = MAX(rect->x, clip->x); + t = MAX(rect->y, clip->y); + r = MIN(rect->x + rect->width, clip->x + clip->width); + b = MIN(rect->y + rect->height, clip->y + clip->height); + } else { + l = rect->x; + t = rect->y; + r = rect->x + rect->width; + b = rect->y + rect->height; + } + + /* Check so that the rect isn't outside the buffer */ + if ((l < 0) || (t < 0) || (l >= img->width) || (t >= img->height)) { + b2r2_log_warn("%s: " + "rect origin outside buffer\n", __func__); + ret = -EINVAL; + goto error; + } + + if ((r > img->width) || (b > img->height)) { + b2r2_log_warn("%s: " + "rect ends outside buffer\n", __func__); + ret = -EINVAL; + goto error; + } + + /* Check so the intersected rectangle isn't empty */ + if ((l == r) || (t == b)) { + b2r2_log_warn("%s: " + "rect is empty (width or height zero)\n", + __func__); + ret = -EINVAL; + goto error; + } + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * analyze_fmt_conv() - analyze the format conversions needed for a job + */ +static int analyze_fmt_conv(struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, + const u32 **vmx, u32 *node_count) +{ + if (is_rgb_fmt(src->fmt)) { + if (is_yvu_fmt(dst->fmt)) + *vmx = &vmx_rgb_to_yvu[0]; + else if (dst->fmt == B2R2_BLT_FMT_24_BIT_YUV888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_AYUV8888 || + dst->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + /* + * (A)YUV/VUY(A) formats differ only in component + * order. This is handled by the endianness bit + * in B2R2_STY/TTY registers when src/target are set. + */ + *vmx = &vmx_rgb_to_blt_yuv888[0]; + else if (is_yuv_fmt(dst->fmt)) + *vmx = &vmx_rgb_to_yuv[0]; + else if (is_bgr_fmt(dst->fmt)) + *vmx = &vmx_rgb_to_bgr[0]; + } else if (is_yvu_fmt(src->fmt)) { + if (is_rgb_fmt(dst->fmt)) + *vmx = &vmx_yvu_to_rgb[0]; + else if (is_bgr_fmt(dst->fmt)) + *vmx = &vmx_yvu_to_bgr[0]; + else if (dst->fmt == B2R2_BLT_FMT_24_BIT_YUV888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_AYUV8888 || + dst->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + *vmx = &vmx_yvu_to_blt_yuv888[0]; + else if (is_yuv_fmt(dst->fmt) && + !is_yvu_fmt(dst->fmt)) + *vmx = &vmx_yvu_to_yuv[0]; + } else if (src->fmt == B2R2_BLT_FMT_24_BIT_YUV888 || + src->fmt == B2R2_BLT_FMT_32_BIT_AYUV8888 || + src->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + src->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) { + /* + * (A)YUV/VUY(A) formats differ only in component + * order. This is handled by the endianness bit + * in B2R2_STY/TTY registers when src/target are set. + */ + if (is_rgb_fmt(dst->fmt)) { + *vmx = &vmx_blt_yuv888_to_rgb[0]; + } else if (is_yvu_fmt(dst->fmt)) { + *vmx = &vmx_blt_yuv888_to_yvu[0]; + } else if (is_yuv_fmt(dst->fmt)) { + switch (dst->fmt) { + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: /* do nothing */ + break; + default: + *vmx = &vmx_blt_yuv888_to_yuv[0]; + break; + } + } + } else if (is_yuv_fmt(src->fmt)) { + if (is_rgb_fmt(dst->fmt)) + *vmx = &vmx_yuv_to_rgb[0]; + else if (is_bgr_fmt(dst->fmt)) + *vmx = &vmx_yuv_to_bgr[0]; + else if (dst->fmt == B2R2_BLT_FMT_24_BIT_YUV888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_AYUV8888 || + dst->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + *vmx = &vmx_yuv_to_blt_yuv888[0]; + else if (is_yvu_fmt(dst->fmt)) + *vmx = &vmx_yvu_to_yuv[0]; + } else if (is_bgr_fmt(src->fmt)) { + if (is_rgb_fmt(dst->fmt)) + *vmx = &vmx_rgb_to_bgr[0]; + else if (is_yvu_fmt(dst->fmt)) + *vmx = &vmx_bgr_to_yvu[0]; + else if (dst->fmt == B2R2_BLT_FMT_24_BIT_YUV888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_AYUV8888 || + dst->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + dst->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + BUG_ON(1); + else if (is_yuv_fmt(dst->fmt)) + *vmx = &vmx_bgr_to_yuv[0]; + } + + if (dst->type == B2R2_FMT_TYPE_RASTER) { + *node_count = 1; + } else if (dst->type == B2R2_FMT_TYPE_SEMI_PLANAR) { + *node_count = 2; + } else if (dst->type == B2R2_FMT_TYPE_PLANAR) { + *node_count = 3; + } else { + /* That's strange... */ + BUG_ON(1); + } + + return 0; +} + +/** + * analyze_color_fill() - analyze a color fill operation + */ +static int analyze_color_fill(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count) +{ + int ret; + + /* Destination must be raster for raw fill to work */ + if (this->dst.type != B2R2_FMT_TYPE_RASTER) { + b2r2_log_warn("%s: fill requires raster destination\n", + __func__); + ret = -EINVAL; + goto error; + } + + /* We will try to fill the entire rectangle in one go */ + memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win)); + + /* Check if this is a direct fill */ + if ((!this->blend) && ((this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW) || + (this->dst.fmt == B2R2_BLT_FMT_32_BIT_ARGB8888) || + (this->dst.fmt == B2R2_BLT_FMT_32_BIT_ABGR8888) || + (this->dst.fmt == B2R2_BLT_FMT_32_BIT_AYUV8888) || + (this->dst.fmt == B2R2_BLT_FMT_32_BIT_VUYA8888))) { + this->type = B2R2_DIRECT_FILL; + + /* The color format will be the same as the dst fmt */ + this->src.fmt = this->dst.fmt; + + /* The entire destination rectangle will be */ + memcpy(&this->dst.win, &this->dst.rect, + sizeof(this->dst.win)); + *node_count = 1; + } else { + this->type = B2R2_FILL; + + /* Determine the fill color format */ + if (this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW) { + /* The color format will be the same as the dst fmt */ + this->src.fmt = this->dst.fmt; + } else { + /* If the dst fmt is YUV the fill fmt will be as well */ + if (is_yuv_fmt(this->dst.fmt)) { + this->src.fmt = B2R2_BLT_FMT_32_BIT_AYUV8888; + } else if (is_rgb_fmt(this->dst.fmt)) { + this->src.fmt = B2R2_BLT_FMT_32_BIT_ARGB8888; + } else if (is_bgr_fmt(this->dst.fmt)) { + /* Color will still be ARGB, we will translate + using IVMX (configured later) */ + this->src.fmt = B2R2_BLT_FMT_32_BIT_ARGB8888; + } else { + /* Wait, what? */ + b2r2_log_warn("%s: " + "Illegal destination format for fill", + __func__); + ret = -EINVAL; + goto error; + } + } + + /* Also, B2R2 seems to ignore the pixel alpha value */ + if (((this->flags & B2R2_BLT_FLAG_PER_PIXEL_ALPHA_BLEND) + != 0) && + ((this->flags & B2R2_BLT_FLAG_SOURCE_FILL_RAW) + == 0) && fmt_has_alpha(this->src.fmt)) { + u8 pixel_alpha = get_alpha(this->src.fmt, + this->src.color); + u32 new_global = pixel_alpha * this->global_alpha / 255; + + this->global_alpha = (u8)new_global; + + /* Set the pixel alpha to full opaque so we don't get + any nasty suprises */ + this->src.color = set_alpha(this->src.fmt, 0xFF, + this->src.color); + } + + ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx, + node_count); + if (ret < 0) + goto error; + } + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; + +} + +/** + * analyze_transform() - analyze a transform operation (rescale, rotate, etc.) + */ +static int analyze_transform(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + bool is_scaling; + + b2r2_log_info("%s\n", __func__); + + /* + * The transform enum is defined so that all rotation transforms are + * masked with the rotation flag + */ + this->rotation = (this->transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) != 0; + + /* B2R2 cannot do rotations if the destination is not raster, or 422R */ + if (this->rotation && (this->dst.type != B2R2_FMT_TYPE_RASTER || + this->dst.fmt == B2R2_BLT_FMT_Y_CB_Y_CR || + this->dst.fmt == B2R2_BLT_FMT_CB_Y_CR_Y)) { + b2r2_log_warn("%s: Unsupported operation " + "(rot && (!dst_raster || dst==422R))", + __func__); + ret = -ENOSYS; + goto unsupported; + } + + /* Flip the image by changing the scan order of the destination */ + if (this->transform & B2R2_BLT_TRANSFORM_FLIP_H) + this->dst.hso = B2R2_TY_HSO_RIGHT_TO_LEFT; + if (this->transform & B2R2_BLT_TRANSFORM_FLIP_V) + this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP; + + /* Check for scaling */ + if (this->rotation) { + is_scaling = (this->src.rect.width != this->dst.rect.height) || + (this->src.rect.height != this->dst.rect.width); + } else { + is_scaling = (this->src.rect.width != this->dst.rect.width) || + (this->src.rect.height != this->dst.rect.height); + } + + /* Plane separated formats must be treated as scaling */ + is_scaling = is_scaling || + (this->src.type == B2R2_FMT_TYPE_SEMI_PLANAR) || + (this->src.type == B2R2_FMT_TYPE_PLANAR) || + (this->dst.type == B2R2_FMT_TYPE_SEMI_PLANAR) || + (this->dst.type == B2R2_FMT_TYPE_PLANAR); + + if (is_scaling && this->rotation && this->blend) { + /* TODO: This is unsupported. Fix it! */ + b2r2_log_info("%s: Unsupported operation (rot+rescale+blend)\n", + __func__); + ret = -ENOSYS; + goto unsupported; + } + + /* Check which type of transform */ + if (is_scaling && this->rotation) { + ret = analyze_rot_scale(this, req, node_count, buf_count); + if (ret < 0) + goto error; + } else if (is_scaling) { + ret = analyze_scaling(this, req, node_count, buf_count); + if (ret < 0) + goto error; + } else if (this->rotation) { + ret = analyze_rotate(this, req, node_count, buf_count); + if (ret < 0) + goto error; + } else { + /* No additional nodes needed for a flip */ + ret = analyze_copy(this, req, node_count, buf_count); + if (ret < 0) + goto error; + this->type = B2R2_FLIP; + } + + return 0; + +error: + b2r2_log_warn("%s: error!\n", __func__); +unsupported: + return ret; +} + +/** + * analyze_copy() - analyze a copy operation + */ +static int analyze_copy(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win)); + + if (!this->blend && + !(this->flags & B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) && + (this->src.fmt == this->dst.fmt) && + (this->src.type == B2R2_FMT_TYPE_RASTER) && + (this->dst.rect.x >= this->clip_rect.x) && + (this->dst.rect.y >= this->clip_rect.y) && + (this->dst.rect.x + this->dst.rect.width <= + this->clip_rect.x + this->clip_rect.width) && + (this->dst.rect.y + this->dst.rect.height <= + this->clip_rect.y + this->clip_rect.height)) { + this->type = B2R2_DIRECT_COPY; + *node_count = 1; + } else { + u32 copy_count; + + this->type = B2R2_COPY; + + ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx, + ©_count); + if (ret < 0) + goto error; + + *node_count = copy_count; + } + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +static int calc_rot_count(u32 width, u32 height) +{ + int count; + + count = width / B2R2_ROTATE_MAX_WIDTH; + if (width % B2R2_ROTATE_MAX_WIDTH) + count++; + if (height > B2R2_ROTATE_MAX_WIDTH && + height % B2R2_ROTATE_MAX_WIDTH) + count *= 2; + + return count; +} + +static int analyze_rot_scale_downscale(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + struct b2r2_node_split_buf *src = &this->src; + struct b2r2_node_split_buf *dst = &this->dst; + struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0]; + + u32 num_rows; + u32 num_cols; + + u32 rot_count; + u32 rescale_count; + + u32 nodes_per_rot; + u32 nodes_per_rescale; + + u32 right_width; + u32 bottom_height; + + const u32 *dummy_vmx; + + b2r2_log_info("%s\n", __func__); + + /* Calculate the desired tmp buffer size */ + tmp->win.width = rescale(B2R2_RESCALE_MAX_WIDTH - 1, this->h_rsf); + tmp->win.width >>= 10; + tmp->win.width = min(tmp->win.width, dst->rect.height); + tmp->win.height = dst->rect.width; + + setup_tmp_buf(tmp, this->max_buf_size, dst->fmt, tmp->win.width, + tmp->win.height); + tmp->tmp_buf_index = 1; + this->work_bufs[0].size = tmp->pitch * tmp->height; + + tmp->win.width = tmp->rect.width; + tmp->win.height = tmp->rect.height; + + tmp->dither = dst->dither; + dst->dither = 0; + + /* Update the dst window with the actual tmp buffer dimensions */ + dst->win.width = tmp->win.height; + dst->win.height = tmp->win.width; + + /* The rotated stripes are written to the destination bottom-up */ + if (this->dst.vso == B2R2_TY_VSO_TOP_TO_BOTTOM) + this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP; + else + this->dst.vso = B2R2_TY_VSO_TOP_TO_BOTTOM; + + /* + * Calculate how many nodes are required to copy to and from the tmp + * buffer + */ + ret = analyze_fmt_conv(src, tmp, &this->ivmx, &nodes_per_rescale); + if (ret < 0) + goto error; + + /* We will not do any format conversion in the rotation stage */ + ret = analyze_fmt_conv(tmp, dst, &dummy_vmx, &nodes_per_rot); + if (ret < 0) + goto error; + + /* Calculate node count for the inner tiles */ + num_cols = dst->rect.width / dst->win.width; + num_rows = dst->rect.height / dst->win.height; + + rescale_count = num_cols * num_rows; + rot_count = calc_rot_count(dst->win.height, dst->win.width) * + num_cols * num_rows; + + right_width = dst->rect.width % dst->win.width; + bottom_height = dst->rect.height % dst->win.height; + + /* Calculate node count for the rightmost tiles */ + if (right_width) { + u32 count = calc_rot_count(dst->win.height, right_width); + + rot_count += count * num_rows; + rescale_count += num_rows; + b2r2_log_info("%s: rightmost: %d nodes\n", __func__, + count*num_rows); + } + + /* Calculate node count for the bottom tiles */ + if (bottom_height) { + u32 count = calc_rot_count(bottom_height, dst->win.width); + + rot_count += count * num_cols; + rescale_count += num_cols; + b2r2_log_info("%s: bottom: %d nodes\n", __func__, + count * num_cols); + + } + + /* And finally for the bottom right corner */ + if (right_width && bottom_height) { + u32 count = calc_rot_count(bottom_height, right_width); + + rot_count += count; + rescale_count++; + b2r2_log_info("%s: bottom right: %d nodes\n", __func__, count); + + } + + *node_count = rot_count * nodes_per_rot; + *node_count += rescale_count * nodes_per_rescale; + *buf_count = 1; + + return 0; + +error: + b2r2_log_warn("%s: error!\n", __func__); + return ret; +} + +static int analyze_rot_scale_upscale(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + /* TODO: When upscaling we should optimally to the rotation first... */ + return analyze_rot_scale_downscale(this, req, node_count, buf_count); +} + +/** + * analyze_rot_scaling() - analyzes a combined rotation and scaling op + */ +static int analyze_rot_scale(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + bool upscale; + + ret = analyze_scale_factors(this); + if (ret < 0) + goto error; + + upscale = (u32)this->h_rsf * (u32)this->v_rsf < (1 << 20); + + if (upscale) + ret = analyze_rot_scale_upscale(this, req, node_count, + buf_count); + else + ret = analyze_rot_scale_downscale(this, req, node_count, + buf_count); + + if (ret < 0) + goto error; + + this->type = B2R2_SCALE_AND_ROTATE; + + return 0; + +error: + return ret; +} + +/** + * analyze_scaling() - analyze a rescale operation + */ +static int analyze_scaling(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + u32 copy_count; + u32 nbr_cols; + + s32 dst_w; + + b2r2_log_info("%s\n", __func__); + + ret = analyze_scale_factors(this); + if (ret < 0) + goto error; + + /* Find out how many nodes a simple copy would require */ + ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx, + ©_count); + if (ret < 0) + goto error; + + memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win)); + + /* + * We need to subtract from the actual maximum rescale width since the + * start of the stripe will be floored and the end ceiled. This could in + * some cases cause the stripe to be one pixel more than the maximum + * width. + * + * Example: + * x = 127.8, w = 127.8 + * + * The stripe will touch pixels 127.8 through 255.6, i.e. 129 pixels. + */ + dst_w = rescale(B2R2_RESCALE_MAX_WIDTH - 1, this->h_rsf); + if (dst_w < (1 << 10)) + dst_w = 1; + else + dst_w >>= 10; + + b2r2_log_info("%s: dst_w=%d dst.rect.width=%d\n", __func__, dst_w, + this->dst.rect.width); + + this->dst.win.width = min(dst_w, this->dst.rect.width); + + b2r2_log_info("%s: dst.win.width=%d\n", __func__, this->dst.win.width); + + nbr_cols = this->dst.rect.width / this->dst.win.width; + if (this->dst.rect.width % this->dst.win.width) + nbr_cols++; + + *node_count = copy_count * nbr_cols; + + this->type = B2R2_SCALE; + + b2r2_log_info("%s exit\n", __func__); + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; + +} + +/** + * analyze_rotate() - analyze a rotate operation + */ +static int analyze_rotate(struct b2r2_node_split_job *this, + const struct b2r2_blt_request *req, u32 *node_count, + u32 *buf_count) +{ + int ret; + + u32 nodes_per_tile; + + /* Find out how many nodes a simple copy would require */ + ret = analyze_fmt_conv(&this->src, &this->dst, &this->ivmx, + &nodes_per_tile); + if (ret < 0) + goto error; + + this->type = B2R2_ROTATE; + + /* The rotated stripes are written to the destination bottom-up */ + if (this->dst.vso == B2R2_TY_VSO_TOP_TO_BOTTOM) + this->dst.vso = B2R2_TY_VSO_BOTTOM_TO_TOP; + else + this->dst.vso = B2R2_TY_VSO_TOP_TO_BOTTOM; + + memcpy(&this->dst.win, &this->dst.rect, sizeof(this->dst.win)); + + this->dst.win.height = min(this->dst.win.height, B2R2_ROTATE_MAX_WIDTH); + + /* + * B2R2 cannot do rotations on stripes that are not a multiple of 16 + * pixels high (if larger than 16 pixels). + */ + if (this->dst.win.width > 16) + this->dst.win.width -= (this->dst.win.width % 16); + + /* Blending cannot be combined with rotation */ + if (this->blend) { + struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0]; + enum b2r2_blt_fmt tmp_fmt; + + if (is_yuv_fmt(this->dst.fmt)) + tmp_fmt = B2R2_BLT_FMT_32_BIT_AYUV8888; + else if (is_bgr_fmt(this->dst.fmt)) + tmp_fmt = B2R2_BLT_FMT_32_BIT_ABGR8888; + else + tmp_fmt = B2R2_BLT_FMT_32_BIT_ARGB8888; + + setup_tmp_buf(tmp, this->max_buf_size, tmp_fmt, + this->dst.win.width, this->dst.win.height); + + tmp->tmp_buf_index = 1; + + tmp->vso = B2R2_TY_VSO_BOTTOM_TO_TOP; + + this->dst.win.width = tmp->rect.width; + this->dst.win.height = tmp->rect.height; + + memcpy(&tmp->win, &tmp->rect, sizeof(tmp->win)); + + *buf_count = 1; + this->work_bufs[0].size = tmp->pitch * tmp->height; + + /* + * One more node per tile is required to rotate to the temp + * buffer. + */ + nodes_per_tile++; + } + + /* Finally, calculate the node count */ + *node_count = nodes_per_tile * + calc_rot_count(this->src.rect.width, this->src.rect.height); + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * analyze_scale_factors() - determines the scale factors for the op + */ +static int analyze_scale_factors(struct b2r2_node_split_job *this) +{ + int ret; + + u16 hsf; + u16 vsf; + + if (this->rotation) { + ret = calculate_scale_factor(this->src.rect.width, + this->dst.rect.height, &hsf); + if (ret < 0) + goto error; + + ret = calculate_scale_factor(this->src.rect.height, + this->dst.rect.width, &vsf); + if (ret < 0) + goto error; + } else { + ret = calculate_scale_factor(this->src.rect.width, + this->dst.rect.width, &hsf); + if (ret < 0) + goto error; + + ret = calculate_scale_factor(this->src.rect.height, + this->dst.rect.height, &vsf); + if (ret < 0) + goto error; + } + + this->h_rescale = hsf != (1 << 10); + this->v_rescale = vsf != (1 << 10); + + this->h_rsf = hsf; + this->v_rsf = vsf; + + b2r2_log_info("%s: h_rsf=%.4x\n", __func__, this->h_rsf); + b2r2_log_info("%s: v_rsf=%.4x\n", __func__, this->v_rsf); + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * configure_tile() - configures one tile of a blit operation + */ +static int configure_tile(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next) +{ + int ret = 0; + + struct b2r2_node *last; + struct b2r2_node_split_buf *src = &this->src; + struct b2r2_node_split_buf *dst = &this->dst; + + struct b2r2_blt_rect dst_norm; + struct b2r2_blt_rect src_norm; + + /* Normalize the dest coords to the dest rect coordinate space */ + dst_norm.x = dst->win.x - dst->rect.x; + dst_norm.y = dst->win.y - dst->rect.y; + dst_norm.width = dst->win.width; + dst_norm.height = dst->win.height; + + if (dst->vso == B2R2_TY_VSO_BOTTOM_TO_TOP) { + /* The y coord should be counted from the bottom */ + dst_norm.y = dst->rect.height - (dst_norm.y + 1); + } + if (dst->hso == B2R2_TY_HSO_RIGHT_TO_LEFT) { + /* The x coord should be counted from the right */ + dst_norm.x = dst->rect.width - (dst_norm.x + 1); + } + + /* If the destination is rotated we should swap x, y */ + if (this->rotation) { + src_norm.x = dst_norm.y; + src_norm.y = dst_norm.x; + src_norm.width = dst_norm.height; + src_norm.height = dst_norm.width; + } else { + src_norm.x = dst_norm.x; + src_norm.y = dst_norm.y; + src_norm.width = dst_norm.width; + src_norm.height = dst_norm.height; + } + + /* Convert to src coordinate space */ + src->win.x = src_norm.x + src->rect.x; + src->win.y = src_norm.y + src->rect.y; + src->win.width = src_norm.width; + src->win.height = src_norm.height; + + /* Do the configuration depending on operation type */ + switch (this->type) { + case B2R2_DIRECT_FILL: + configure_direct_fill(node, this->src.color, dst, &last); + break; + + case B2R2_DIRECT_COPY: + configure_direct_copy(node, src, dst, &last); + break; + + case B2R2_FILL: + ret = configure_fill(node, src->color, src->fmt, + dst, this->ivmx, &last); + break; + + case B2R2_FLIP: /* FLIP is just a copy with different VSO/HSO */ + case B2R2_COPY: + ret = configure_copy(node, src, dst, this->ivmx, &last, this); + break; + + case B2R2_ROTATE: + { + struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0]; + + if (this->blend) { + b2r2_log_info("%s: rotation + blend\n", + __func__); + + tmp->win.x = 0; + tmp->win.y = tmp->win.height - 1; + tmp->win.width = dst->win.width; + tmp->win.height = dst->win.height; + + /* Rotate to the temp buf */ + ret = configure_rotate(node, src, tmp, + this->ivmx, &node, NULL); + if (ret < 0) + goto error; + + /* Then do a copy to the destination */ + ret = configure_copy(node, tmp, dst, NULL, + &last, this); + } else { + /* Just do a rotation */ + ret = configure_rotate(node, src, dst, + this->ivmx, &last, this); + } + } + break; + + case B2R2_SCALE: + ret = configure_scale(node, src, dst, this->h_rsf, this->v_rsf, + this->ivmx, &last, this); + break; + + case B2R2_SCALE_AND_ROTATE: + ret = configure_rot_scale(this, node, &last); + break; + + default: + b2r2_log_warn("%s: Unsupported request\n", __func__); + ret = -ENOSYS; + goto error; + break; + + } + + if (ret < 0) + goto error; + + /* Scale and rotate will configure its own blending and clipping */ + if (this->type != B2R2_SCALE_AND_ROTATE) { + + /* Configure blending and clipping */ + do { + if (node == NULL) { + b2r2_log_warn("%s: " + "Internal error! Out of nodes!\n", + __func__); + ret = -ENOMEM; + goto error; + } + + if (this->blend) + configure_blend(node, this->flags, + this->global_alpha, + this->swap_fg_bg); + if (this->clip) + configure_clip(node, &this->clip_rect); + + node = node->next; + + } while (node != last); + } + + /* Consume the nodes */ + *next = last; + + return 0; + +error: + b2r2_log_warn("%s: Error!\n", __func__); + return ret; +} + +/* + * configure_sub_rot() - configure a sub-rotation + * + * This functions configures a set of nodes for rotation using the destination + * window instead of the rectangle for calculating tiles. + */ +static int configure_sub_rot(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, + const u32 *ivmx, struct b2r2_node **next, + struct b2r2_node_split_job *job) +{ + int ret; + + struct b2r2_blt_rect src_win; + struct b2r2_blt_rect dst_win; + + u32 y_pixels = 0; + u32 x_pixels = 0; + + memcpy(&src_win, &src->win, sizeof(src_win)); + memcpy(&dst_win, &dst->win, sizeof(dst_win)); + + b2r2_log_info("%s: src_win=(%d, %d, %d, %d) " + "dst_win=(%d, %d, %d, %d)\n", __func__, + src_win.x, src_win.y, src_win.width, src_win.height, + dst_win.x, dst_win.y, dst_win.width, dst_win.height); + + dst->win.height = B2R2_ROTATE_MAX_WIDTH; + if (dst->win.width % B2R2_ROTATE_MAX_WIDTH) + dst->win.width -= dst->win.width % B2R2_ROTATE_MAX_WIDTH; + + while (x_pixels < dst_win.width) { + u32 src_x = src->win.x; + u32 src_w = src->win.width; + u32 dst_y = dst->win.y; + u32 dst_h = dst->win.height; + + dst->win.width = min(dst->win.width, + dst_win.width - (int)x_pixels); + src->win.height = dst->win.width; + + b2r2_log_info("%s: x_pixels=%d\n", __func__, x_pixels); + + while (y_pixels < dst_win.height) { + dst->win.height = min(dst->win.height, + dst_win.height - (int)y_pixels); + src->win.width = dst->win.height; + + b2r2_log_info("%s: y_pixels=%d\n", __func__, y_pixels); + + ret = configure_rotate(node, src, dst, ivmx, &node, + job); + if (ret < 0) + goto error; + + src->win.x += (src->hso == B2R2_TY_HSO_LEFT_TO_RIGHT) ? + src->win.width : -src->win.width; + dst->win.y += (dst->vso == B2R2_TY_VSO_TOP_TO_BOTTOM) ? + dst->win.height : -dst->win.height; + + y_pixels += dst->win.height; + } + + src->win.x = src_x; + src->win.y += (src->vso == B2R2_TY_VSO_TOP_TO_BOTTOM) ? + src->win.height : -src->win.height; + src->win.width = src_w; + + dst->win.x += (dst->hso == B2R2_TY_HSO_LEFT_TO_RIGHT) ? + dst->win.width : -dst->win.width; + dst->win.y = dst_y; + dst->win.height = dst_h; + + x_pixels += dst->win.width; + y_pixels = 0; + + } + + memcpy(&src->win, &src_win, sizeof(src->win)); + memcpy(&dst->win, &dst_win, sizeof(dst->win)); + + *next = node; + + return 0; + +error: + b2r2_log_warn("%s: error!\n", __func__); + return ret; +} + +/** + * configure_rot_downscale() - configures a combined rotate and downscale + * + * When doing a downscale it is better to do the rotation last. + */ +static int configure_rot_downscale(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next) +{ + int ret; + + struct b2r2_node_split_buf *src = &this->src; + struct b2r2_node_split_buf *dst = &this->dst; + struct b2r2_node_split_buf *tmp = &this->tmp_bufs[0]; + + tmp->win.x = 0; + tmp->win.y = 0; + tmp->win.width = dst->win.height; + tmp->win.height = dst->win.width; + + ret = configure_scale(node, src, tmp, this->h_rsf, this->v_rsf, + this->ivmx, &node, this); + if (ret < 0) + goto error; + + ret = configure_sub_rot(node, tmp, dst, NULL, &node, this); + if (ret < 0) + goto error; + + *next = node; + + return 0; + +error: + b2r2_log_info("%s: error!\n", __func__); + return ret; +} + +/** + * configure_rot_upscale() - configures a combined rotate and upscale + * + * When doing an upscale it is better to do the rotation first. + */ +static int configure_rot_upscale(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next) +{ + /* TODO: Implement a optimal upscale (rotation first) */ + return configure_rot_downscale(this, node, next); +} + +/** + * configure_rot_scale() - configures a combined rotation and scaling op + */ +static int configure_rot_scale(struct b2r2_node_split_job *this, + struct b2r2_node *node, struct b2r2_node **next) +{ + int ret; + + bool upscale = (u32)this->h_rsf * (u32)this->v_rsf < (1 << 10); + + if (upscale) + ret = configure_rot_upscale(this, node, next); + else + ret = configure_rot_downscale(this, node, next); + + if (ret < 0) + goto error; + + return 0; + +error: + b2r2_log_warn("%s: error!\n", __func__); + return ret; +} + +/** + * configure_direct_fill() - configures the given node for direct fill + * + * @node - the node to configure + * @color - the fill color + * @dst - the destination buffer + * @next - the next empty node in the node list + * + * This operation will always consume one node only. + */ +static void configure_direct_fill(struct b2r2_node *node, u32 color, + struct b2r2_node_split_buf *dst, struct b2r2_node **next) +{ + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_FILL | B2R2_CIC_SOURCE_1; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_DIRECT_FILL; + + /* Target setup */ + set_target(node, dst->addr, dst); + + /* Source setup */ + + /* It seems B2R2 checks so that source and dest has the same format */ + node->node.GROUP3.B2R2_STY = to_native_fmt(dst->fmt); + node->node.GROUP2.B2R2_S1CF = color; + node->node.GROUP2.B2R2_S2CF = 0; + + /* Consume the node */ + *next = node->next; +} + +/** + * configure_direct_copy() - configures the node for direct copy + * + * @node - the node to configure + * @src - the source buffer + * @dst - the destination buffer + * @next - the next empty node in the node list + * + * This operation will always consume one node only. + */ +static void configure_direct_copy(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, struct b2r2_node **next) +{ + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_DIRECT_COPY; + + /* Source setup, use the base function to avoid altering the INS */ + set_src(&node->node.GROUP3, src->addr, src); + + /* Target setup */ + set_target(node, dst->addr, dst); + + /* Consume the node */ + *next = node->next; +} + +/** + * configure_fill() - configures the given node for color fill + * + * @node - the node to configure + * @color - the fill color + * @fmt - the source color format + * @dst - the destination buffer + * @next - the next empty node in the node list + * + * A normal fill operation can be combined with any other per pixel operations + * such as blend. + * + * This operation will consume as many nodes as are required to write to the + * destination format. + */ +static int configure_fill(struct b2r2_node *node, u32 color, + enum b2r2_blt_fmt fmt, struct b2r2_node_split_buf *dst, + const u32 *ivmx, struct b2r2_node **next) +{ + int ret; + struct b2r2_node *last; + + /* Configure the destination */ + ret = configure_dst(node, dst, ivmx, &last); + if (ret < 0) + goto error; + + do { + if (node == NULL) { + b2r2_log_warn("%s: " + "Internal error! Out of nodes!\n", __func__); + ret = -ENOMEM; + goto error; + } + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2 | + B2R2_CIC_COLOR_FILL; + node->node.GROUP0.B2R2_INS |= + B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER; + node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3; + + /* B2R2 has a bug that disables color fill from S2. As a + workaround we use S1 for the color. */ + node->node.GROUP2.B2R2_S1CF = 0; + node->node.GROUP2.B2R2_S2CF = color; + + /* TO BE REMOVED: */ + set_src_2(node, dst->addr, dst); + node->node.GROUP4.B2R2_STY = to_native_fmt(fmt); + + /* Setup the iVMX for color conversion */ + if (ivmx != NULL) + set_ivmx(node, ivmx); + + if ((dst->type == B2R2_FMT_TYPE_PLANAR) || + (dst->type == B2R2_FMT_TYPE_SEMI_PLANAR)) { + + node->node.GROUP0.B2R2_INS |= + B2R2_INS_RESCALE2D_ENABLED; + node->node.GROUP8.B2R2_FCTL = + B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER | + B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER | + B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER | + B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER; + node->node.GROUP9.B2R2_RSF = + (1 << (B2R2_RSF_HSRC_INC_SHIFT + 10)) | + (1 << (B2R2_RSF_VSRC_INC_SHIFT + 10)); + node->node.GROUP9.B2R2_RZI = + B2R2_RZI_DEFAULT_HNB_REPEAT | + (2 << B2R2_RZI_VNB_REPEAT_SHIFT); + + node->node.GROUP10.B2R2_RSF = + (1 << (B2R2_RSF_HSRC_INC_SHIFT + 10)) | + (1 << (B2R2_RSF_VSRC_INC_SHIFT + 10)); + node->node.GROUP10.B2R2_RZI = + B2R2_RZI_DEFAULT_HNB_REPEAT | + (2 << B2R2_RZI_VNB_REPEAT_SHIFT); + } + + node = node->next; + + } while (node != last); + + /* Consume the nodes */ + *next = node; + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * configure_copy() - configures the given node for a copy operation + * + * @node - the node to configure + * @src - the source buffer + * @dst - the destination buffer + * @ivmx - the iVMX to use for color conversion + * @next - the next empty node in the node list + * + * This operation will consume as many nodes as are required to write to the + * destination format. + */ +static int configure_copy(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next, + struct b2r2_node_split_job *this) +{ + int ret; + + struct b2r2_node *last; + + ret = configure_dst(node, dst, ivmx, &last); + if (ret < 0) + goto error; + + /* Configure the source for each node */ + do { + if (node == NULL) { + b2r2_log_warn("%s: " + " Internal error! Out of nodes!\n", __func__); + ret = -ENOMEM; + goto error; + } + + node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BYPASS_S2_S3; + if (this != NULL && + (this->flags & B2R2_BLT_FLAG_SOURCE_COLOR_KEY) + != 0) { + u32 key_color = 0; + + node->node.GROUP0.B2R2_ACK |= + B2R2_ACK_CKEY_SEL_SRC_AFTER_CLUT | + B2R2_ACK_CKEY_RED_MATCH_IF_BETWEEN | + B2R2_ACK_CKEY_GREEN_MATCH_IF_BETWEEN | + B2R2_ACK_CKEY_BLUE_MATCH_IF_BETWEEN; + node->node.GROUP0.B2R2_INS |= B2R2_INS_CKEY_ENABLED; + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_COLOR_KEY; + + key_color = to_RGB888(this->flag_param, src->fmt); + node->node.GROUP12.B2R2_KEY1 = key_color; + node->node.GROUP12.B2R2_KEY2 = key_color; + } + + if (this != NULL && + (this->flags & + B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) { + struct b2r2_blt_request *request = + container_of(this, struct b2r2_blt_request, + node_split_job); + node->node.GROUP0.B2R2_INS |= B2R2_INS_CLUTOP_ENABLED; + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLUT; + node->node.GROUP7.B2R2_CCO = + B2R2_CCO_CLUT_COLOR_CORRECTION | + B2R2_CCO_CLUT_UPDATE; + node->node.GROUP7.B2R2_CML = request->clut_phys_addr; + } + /* Configure the source(s) */ + configure_src(node, src, ivmx); + + node = node->next; + } while (node != last); + + /* Consume the nodes */ + *next = node; + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * configure_rotate() - configures the given node for rotation + * + * @node - the node to configure + * @src - the source buffer + * @dst - the destination buffer + * @ivmx - the iVMX to use for color conversion + * @next - the next empty node in the node list + * + * This operation will consume as many nodes are are required by the combination + * of rotating and writing the destination format. + */ +static int configure_rotate(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next, + struct b2r2_node_split_job *this) +{ + int ret; + + struct b2r2_node *last; + + ret = configure_copy(node, src, dst, ivmx, &last, this); + if (ret < 0) + goto error; + + do { + if (node == NULL) { + b2r2_log_warn("%s: " + "Internal error! Out of nodes!\n", __func__); + ret = -ENOMEM; + goto error; + } + + node->node.GROUP0.B2R2_INS |= B2R2_INS_ROTATION_ENABLED; + + b2r2_log_debug("%s:\n" + "\tB2R2_TXY: %.8x\tB2R2_TSZ: %.8x\n" + "\tB2R2_S1XY: %.8x\tB2R2_S1SZ: %.8x\n" + "\tB2R2_S2XY: %.8x\tB2R2_S2SZ: %.8x\n" + "\tB2R2_S3XY: %.8x\tB2R2_S3SZ: %.8x\n" + "-----------------------------------\n", + __func__, node->node.GROUP1.B2R2_TXY, + node->node.GROUP1.B2R2_TSZ, + node->node.GROUP3.B2R2_SXY, + node->node.GROUP3.B2R2_SSZ, + node->node.GROUP4.B2R2_SXY, + node->node.GROUP4.B2R2_SSZ, + node->node.GROUP5.B2R2_SXY, + node->node.GROUP5.B2R2_SSZ); + + node = node->next; + + } while (node != last); + + /* Consume the nodes */ + *next = node; + + return 0; +error: + b2r2_log_warn("%s: error!\n", __func__); + return ret; +} + +/** + * configure_scale() - configures the given node for scaling + * + * @node - the node to configure + * @src - the source buffer + * @dst - the destination buffer + * @h_rsf - the horizontal rescale factor + * @v_rsf - the vertical rescale factor + * @ivmx - the iVMX to use for color conversion + * @next - the next empty node in the node list + */ +static int configure_scale(struct b2r2_node *node, + struct b2r2_node_split_buf *src, + struct b2r2_node_split_buf *dst, + u16 h_rsf, u16 v_rsf, + const u32 *ivmx, struct b2r2_node **next, + struct b2r2_node_split_job *this) +{ + int ret; + + struct b2r2_node *last; + + struct b2r2_filter_spec *hf = NULL; + struct b2r2_filter_spec *vf = NULL; + + u32 fctl = 0; + u32 rsf = 0; + u32 rzi = 0; + u32 hsrc_init = 0; + u32 vsrc_init = 0; + u32 hfp = 0; + u32 vfp = 0; + + u16 luma_h_rsf = h_rsf; + u16 luma_v_rsf = v_rsf; + + struct b2r2_filter_spec *luma_hf = NULL; + struct b2r2_filter_spec *luma_vf = NULL; + + u32 luma_fctl = 0; + u32 luma_rsf = 0; + u32 luma_rzi = 0; + u32 luma_hsrc_init = 0; + u32 luma_vsrc_init = 0; + u32 luma_hfp = 0; + u32 luma_vfp = 0; + + s32 src_x; + s32 src_y; + s32 src_w; + s32 src_h; + + bool upsample; + bool downsample; + + struct b2r2_blt_rect tmp_win = src->win; + + /* Rescale the normalized source window */ + src_x = inv_rescale(src->win.x - src->rect.x, luma_h_rsf); + src_y = inv_rescale(src->win.y - src->rect.y, luma_v_rsf); + src_w = inv_rescale(src->win.width, luma_h_rsf); + src_h = inv_rescale(src->win.height, luma_v_rsf); + + /* Convert to src coordinate space */ + src->win.x = (src_x >> 10) + src->rect.x; + src->win.y = (src_y >> 10) + src->rect.y; + + /* + * Since the stripe might start and end on a fractional pixel + * we need to count all the touched pixels in the width. + * + * Example: + * src_x = 1.8, src_w = 2.8 + * + * The stripe touches pixels 1.8 through 4.6, i.e. 4 pixels + */ + src->win.width = ((src_x & 0x3ff) + src_w + 0x3ff) >> 10; + src->win.height = ((src_y & 0x3ff) + src_h + 0x3ff) >> 10; + + luma_hsrc_init = src_x & 0x3ff; + luma_vsrc_init = src_y & 0x3ff; + + /* Check for upsampling of chroma */ + upsample = src->type != B2R2_FMT_TYPE_RASTER && + !is_yuv444_fmt(src->fmt); + if (upsample) { + h_rsf /= 2; + + if (is_yuv420_fmt(src->fmt)) + v_rsf /= 2; + } + + /* Check for downsampling of chroma */ + downsample = dst->type != B2R2_FMT_TYPE_RASTER && + !is_yuv444_fmt(dst->fmt); + if (downsample) { + h_rsf *= 2; + + if (is_yuv420_fmt(dst->fmt)) + v_rsf *= 2; + } + + src_x = inv_rescale(tmp_win.x - src->rect.x, h_rsf); + src_y = inv_rescale(tmp_win.y - src->rect.y, v_rsf); + hsrc_init = src_x & 0x3ff; + vsrc_init = src_y & 0x3ff; + + /* Configure resize and filters */ + fctl = B2R2_FCTL_HF2D_MODE_ENABLE_RESIZER | + B2R2_FCTL_VF2D_MODE_ENABLE_RESIZER; + luma_fctl = B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_RESIZER | + B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_RESIZER; + + rsf = (h_rsf << B2R2_RSF_HSRC_INC_SHIFT) | + (v_rsf << B2R2_RSF_VSRC_INC_SHIFT); + luma_rsf = (luma_h_rsf << B2R2_RSF_HSRC_INC_SHIFT) | + (luma_v_rsf << B2R2_RSF_VSRC_INC_SHIFT); + + rzi = B2R2_RZI_DEFAULT_HNB_REPEAT | + (2 << B2R2_RZI_VNB_REPEAT_SHIFT) | + (hsrc_init << B2R2_RZI_HSRC_INIT_SHIFT) | + (vsrc_init << B2R2_RZI_VSRC_INIT_SHIFT); + luma_rzi = B2R2_RZI_DEFAULT_HNB_REPEAT | + (2 << B2R2_RZI_VNB_REPEAT_SHIFT) | + (luma_hsrc_init << B2R2_RZI_HSRC_INIT_SHIFT) | + (luma_vsrc_init << B2R2_RZI_VSRC_INIT_SHIFT); + + /* + * We should only filter if there is an actual rescale (i.e. not when + * up or downsampling). + */ + if (luma_h_rsf != (1 << 10)) { + hf = b2r2_filter_find(h_rsf); + luma_hf = b2r2_filter_find(luma_h_rsf); + } + if (luma_v_rsf != (1 << 10)) { + vf = b2r2_filter_find(v_rsf); + luma_vf = b2r2_filter_find(luma_v_rsf); + } + + if (hf) { + fctl |= B2R2_FCTL_HF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER; + hfp = hf->h_coeffs_phys_addr; + } + + if (vf) { + fctl |= B2R2_FCTL_VF2D_MODE_ENABLE_COLOR_CHANNEL_FILTER; + vfp = vf->v_coeffs_phys_addr; + } + + if (luma_hf) { + luma_fctl |= B2R2_FCTL_LUMA_HF2D_MODE_ENABLE_FILTER; + luma_hfp = luma_hf->h_coeffs_phys_addr; + } + + if (luma_vf) { + luma_fctl |= B2R2_FCTL_LUMA_VF2D_MODE_ENABLE_FILTER; + luma_vfp = luma_vf->v_coeffs_phys_addr; + } + + ret = configure_copy(node, src, dst, ivmx, &last, this); + if (ret < 0) + goto error; + + do { + bool chroma_rescale = + (h_rsf != (1 << 10)) || (v_rsf != (1 << 10)); + bool luma_rescale = + (luma_h_rsf != (1 << 10)) || (luma_v_rsf != (1 << 10)); + bool src_raster = src->type == B2R2_FMT_TYPE_RASTER; + bool dst_raster = dst->type == B2R2_FMT_TYPE_RASTER; + bool dst_chroma = + node->node.GROUP1.B2R2_TTY & B2R2_TTY_CHROMA_NOT_LUMA; + + if (node == NULL) { + b2r2_log_warn("%s: " + "Internal error! Out of nodes!\n", __func__); + ret = -ENOMEM; + goto error; + } + + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_FILTER_CONTROL; + + /* + * If the source format is anything other than raster, we + * always have to enable both chroma and luma resizers. This + * could be a bug in the hardware, since it is not mentioned in + * the specification. + * + * Otherwise, we will only enable the chroma resizer when + * writing chroma and the luma resizer when writing luma + * (or both when writing raster). Also, if there is no rescale + * to be done there's no point in using the resizers. + */ + + if (!src_raster || (chroma_rescale && + (dst_raster || dst_chroma))) { + /* Enable chroma resize */ + node->node.GROUP0.B2R2_INS |= + B2R2_INS_RESCALE2D_ENABLED; + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_RESIZE_CHROMA; + node->node.GROUP8.B2R2_FCTL |= fctl; + + node->node.GROUP9.B2R2_RSF = rsf; + node->node.GROUP9.B2R2_RZI = rzi; + node->node.GROUP9.B2R2_HFP = hfp; + node->node.GROUP9.B2R2_VFP = vfp; + } + + if (!src_raster || (luma_rescale && + (dst_raster || !dst_chroma))) { + /* Enable luma resize */ + node->node.GROUP0.B2R2_INS |= + B2R2_INS_RESCALE2D_ENABLED; + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_RESIZE_LUMA; + node->node.GROUP8.B2R2_FCTL |= luma_fctl; + + node->node.GROUP10.B2R2_RSF = luma_rsf; + node->node.GROUP10.B2R2_RZI = luma_rzi; + node->node.GROUP10.B2R2_HFP = luma_hfp; + node->node.GROUP10.B2R2_VFP = luma_vfp; + } + + b2r2_log_info("%s:\n" + "\tB2R2_TXY: %.8x\tB2R2_TSZ: %.8x\n" + "\tB2R2_S1XY: %.8x\tB2R2_S1SZ: %.8x\n" + "\tB2R2_S2XY: %.8x\tB2R2_S2SZ: %.8x\n" + "\tB2R2_S3XY: %.8x\tB2R2_S3SZ: %.8x\n" + "----------------------------------\n", + __func__, node->node.GROUP1.B2R2_TXY, + node->node.GROUP1.B2R2_TSZ, + node->node.GROUP3.B2R2_SXY, + node->node.GROUP3.B2R2_SSZ, + node->node.GROUP4.B2R2_SXY, + node->node.GROUP4.B2R2_SSZ, + node->node.GROUP5.B2R2_SXY, + node->node.GROUP5.B2R2_SSZ); + + node = node->next; + + } while (node != last); + + + + /* Consume the nodes */ + *next = node; + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * configure_src() - configures the source registers and the iVMX + * + * @node - the node to configure + * @src - the source buffer + * @ivmx - the iVMX to use for color conversion + * + * This operation will not consume any nodes + */ +static void configure_src(struct b2r2_node *node, + struct b2r2_node_split_buf *src, const u32 *ivmx) +{ + struct b2r2_node_split_buf tmp_buf; + + b2r2_log_info("%s: src.win=(%d, %d, %d, %d)\n", __func__, + src->win.x, src->win.y, src->win.width, + src->win.height); + + /* Configure S1 - S3 */ + switch (src->type) { + case B2R2_FMT_TYPE_RASTER: + set_src_2(node, src->addr, src); + break; + case B2R2_FMT_TYPE_SEMI_PLANAR: + memcpy(&tmp_buf, src, sizeof(tmp_buf)); + + /* + * For 420 and 422 the chroma has lower resolution than the + * luma + */ + if (!is_yuv444_fmt(src->fmt)) { + tmp_buf.win.x >>= 1; + tmp_buf.win.width = (tmp_buf.win.width + 1) / 2; + + if (is_yuv420_fmt(src->fmt)) { + tmp_buf.win.height = + (tmp_buf.win.height + 1) / 2; + tmp_buf.win.y >>= 1; + } + } + + set_src_3(node, src->addr, src); + set_src_2(node, tmp_buf.chroma_addr, &tmp_buf); + break; + case B2R2_FMT_TYPE_PLANAR: + memcpy(&tmp_buf, src, sizeof(tmp_buf)); + + if (!is_yuv444_fmt(src->fmt)) { + /* + * Each chroma buffer will have half as many values + * per line as the luma buffer + */ + tmp_buf.pitch = (tmp_buf.pitch + 1) / 2; + + /* Horizontal resolution is half */ + tmp_buf.win.x >>= 1; + tmp_buf.win.width = (tmp_buf.win.width + 1) / 2; + + /* + * If the buffer is in YUV420 format, the vertical + * resolution is half as well + */ + if (is_yuv420_fmt(src->fmt)) { + tmp_buf.win.height = + (tmp_buf.win.height + 1) / 2; + tmp_buf.win.y >>= 1; + } + } + + set_src_3(node, src->addr, src); /* Y */ + set_src_2(node, tmp_buf.chroma_addr, &tmp_buf); /* U */ + set_src_1(node, tmp_buf.chroma_cr_addr, &tmp_buf); /* V */ + + break; + default: + /* Should never, ever happen */ + BUG_ON(1); + break; + } + + /* Configure the iVMX for color space conversions */ + if (ivmx != NULL) + set_ivmx(node, ivmx); +} + +/** + * configure_dst() - configures the destination registers of the given node + * + * @node - the node to configure + * @ivmx - the iVMX to use for color conversion + * @dst - the destination buffer + * + * This operation will consume as many nodes as are required to write the + * destination format. + */ +static int configure_dst(struct b2r2_node *node, + struct b2r2_node_split_buf *dst, const u32 *ivmx, + struct b2r2_node **next) +{ + int ret; + int nbr_planes = 1; + int i; + + struct b2r2_node_split_buf dst_planes[3]; + + b2r2_log_info("%s: dst.win=(%d, %d, %d, %d)\n", __func__, + dst->win.x, dst->win.y, dst->win.width, + dst->win.height); + + memcpy(&dst_planes[0], dst, sizeof(dst_planes[0])); + + if (dst->type != B2R2_FMT_TYPE_RASTER) { + /* There will be at least 2 planes */ + nbr_planes = 2; + + memcpy(&dst_planes[1], dst, sizeof(dst_planes[1])); + + dst_planes[1].addr = dst->chroma_addr; + dst_planes[1].plane_selection = B2R2_TTY_CHROMA_NOT_LUMA; + + if (!is_yuv444_fmt(dst->fmt)) { + /* Horizontal resolution is half */ + dst_planes[1].win.x /= 2; + /* + * Must round up the chroma size to handle cases when + * luma size is not divisible by 2. E.g. luma width==7 r + * equires chroma width==4. Chroma width==7/2==3 is only + * enough for luma width==6. + */ + dst_planes[1].win.width = + (dst_planes[1].win.width + 1) / 2; + + /* + * If the buffer is in YUV420 format, the vertical + * resolution is half as well. Height must be rounded in + * the same way as is done for width. + */ + if (is_yuv420_fmt(dst->fmt)) { + dst_planes[1].win.y /= 2; + dst_planes[1].win.height = + (dst_planes[1].win.height + 1) / 2; + } + } + + if (dst->type == B2R2_FMT_TYPE_PLANAR) { + /* There will be a third plane as well */ + nbr_planes = 3; + + if (!is_yuv444_fmt(dst->fmt)) { + /* The chroma planes have half the luma pitch */ + dst_planes[1].pitch /= 2; + } + + memcpy(&dst_planes[2], &dst_planes[1], + sizeof(dst_planes[2])); + dst_planes[2].addr = dst->chroma_cr_addr; + + /* + * The third plane will be Cr. + * The flag B2R2_TTY_CB_NOT_CR actually works + * the other way around, i.e. as if it was + * B2R2_TTY_CR_NOT_CB. + */ + dst_planes[2].chroma_selection = B2R2_TTY_CB_NOT_CR; + } + + } + + /* Configure one node for each plane */ + for (i = 0; i < nbr_planes; i++) { + + if (node == NULL) { + b2r2_log_warn("%s: " + "Internal error! Out of nodes!\n", __func__); + ret = -ENOMEM; + goto error; + } + + /* + * When writing chroma, there's no need to read the luma and + * vice versa. + */ + if ((node->node.GROUP3.B2R2_STY & B2R2_NATIVE_YUV) && + (nbr_planes > 1)) { + if (i != 0) { + node->node.GROUP4.B2R2_STY |= + B2R2_S3TY_ENABLE_BLANK_ACCESS; + } + if (i != 1) { + node->node.GROUP0.B2R2_INS &= + ~B2R2_INS_SOURCE_2_FETCH_FROM_MEM; + node->node.GROUP0.B2R2_INS |= + B2R2_INS_SOURCE_2_COLOR_FILL_REGISTER; + } + if (i != 2) { + node->node.GROUP0.B2R2_INS &= + ~B2R2_INS_SOURCE_1_FETCH_FROM_MEM; + node->node.GROUP0.B2R2_INS |= + B2R2_INS_SOURCE_1_COLOR_FILL_REGISTER; + } + } else if ((node->node.GROUP3.B2R2_STY & + (B2R2_NATIVE_YCBCR42X_MBN | + B2R2_NATIVE_YCBCR42X_R2B)) && + (nbr_planes > 1)) { + if (i != 0) { + node->node.GROUP4.B2R2_STY |= + B2R2_S3TY_ENABLE_BLANK_ACCESS; + } + } + + + set_target(node, dst_planes[i].addr, &dst_planes[i]); + + node = node->next; + } + + /* Consume the nodes */ + *next = node; + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; + +} + +/** + * configure_blend() - configures the given node for alpha blending + * + * @node - the node to configure + * @flags - the flags passed in the blt_request + * @global_alpha - the global alpha to use (if enabled in flags) + * @swap_fg_bg - if true, fg will be on s1 instead of s2 + * + * This operation will not consume any nodes. + * + * NOTE: This method should be called _AFTER_ the destination has been + * configured. + * + * WARNING: Take care when using this with semi-planar or planar sources since + * either S1 or S2 will be overwritten! + */ +static void configure_blend(struct b2r2_node *node, u32 flags, u32 global_alpha, + bool swap_fg_bg) +{ + node->node.GROUP0.B2R2_ACK &= ~(B2R2_ACK_MODE_BYPASS_S2_S3); + + /* Check if the foreground is premultiplied */ + if ((flags & B2R2_BLT_FLAG_SRC_IS_NOT_PREMULT) != 0) + node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BLEND_NOT_PREMULT; + else + node->node.GROUP0.B2R2_ACK |= B2R2_ACK_MODE_BLEND_PREMULT; + + + /* Check if global alpha blend should be enabled */ + if ((flags & B2R2_BLT_FLAG_GLOBAL_ALPHA_BLEND) != 0) { + + /* B2R2 expects the global alpha to be in 0...128 range */ + global_alpha = (global_alpha*128)/255; + + node->node.GROUP0.B2R2_ACK |= + global_alpha << B2R2_ACK_GALPHA_ROPID_SHIFT; + } else { + node->node.GROUP0.B2R2_ACK |= + (128 << B2R2_ACK_GALPHA_ROPID_SHIFT); + } + + /* Copy the destination config to the appropriate source and clear any + clashing flags */ + if (swap_fg_bg) { + /* S1 will be foreground, S2 background */ + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM; + node->node.GROUP0.B2R2_ACK |= B2R2_ACK_SWAP_FG_BG; + + node->node.GROUP4.B2R2_SBA = node->node.GROUP1.B2R2_TBA; + node->node.GROUP4.B2R2_STY = node->node.GROUP1.B2R2_TTY; + node->node.GROUP4.B2R2_SXY = node->node.GROUP1.B2R2_TXY; + node->node.GROUP4.B2R2_SSZ = node->node.GROUP1.B2R2_TSZ; + + node->node.GROUP4.B2R2_STY &= ~(B2R2_S2TY_A1_SUBST_KEY_MODE | + B2R2_S2TY_CHROMA_LEFT_EXT_AVERAGE); + } else { + /* S1 will be background, S2 foreground */ + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_FETCH_FROM_MEM; + + node->node.GROUP3.B2R2_SBA = node->node.GROUP1.B2R2_TBA; + node->node.GROUP3.B2R2_STY |= node->node.GROUP1.B2R2_TTY; + node->node.GROUP3.B2R2_SXY = node->node.GROUP1.B2R2_TXY; + + node->node.GROUP3.B2R2_STY &= ~(B2R2_S1TY_A1_SUBST_KEY_MODE); + + } +} + +/** + * configure_clip() - configures destination clipping for the given node + * + * @node - the node to configure + * @clip_rect - the clip rectangle + * + * This operation does not consume any nodes. + */ +static void configure_clip(struct b2r2_node *node, + struct b2r2_blt_rect *clip_rect) +{ + s32 l = clip_rect->x; + s32 r = clip_rect->x + clip_rect->width - 1; + s32 t = clip_rect->y; + s32 b = clip_rect->y + clip_rect->height - 1; + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLIP_WINDOW; + node->node.GROUP0.B2R2_INS |= B2R2_INS_RECT_CLIP_ENABLED; + + /* Clip window setup */ + node->node.GROUP6.B2R2_CWO = + ((t & 0x7FFF) << B2R2_CWO_Y_SHIFT) | + ((l & 0x7FFF) << B2R2_CWO_X_SHIFT); + node->node.GROUP6.B2R2_CWS = + ((b & 0x7FFF) << B2R2_CWO_Y_SHIFT) | + ((r & 0x7FFF) << B2R2_CWO_X_SHIFT); +} + +/** + * set_buf() - configures the given buffer with the provided values + * + * @addr - the physical base address + * @img - the blt image to base the buffer on + * @rect - the rectangle to use + * @color_fill - determines whether the buffer should be used for color fill + * @color - the color to use in case of color fill + */ +static void set_buf(struct b2r2_node_split_buf *buf, u32 addr, + const struct b2r2_blt_img *img, + const struct b2r2_blt_rect *rect, bool color_fill, u32 color) +{ + memset(buf, 0, sizeof(*buf)); + + buf->fmt = img->fmt; + buf->type = get_fmt_type(img->fmt); + + if (color_fill) { + buf->type = B2R2_FMT_TYPE_RASTER; + buf->color = color; + } else { + buf->addr = addr; + + buf->alpha_range = get_alpha_range(img->fmt); + + if (img->pitch == 0) + buf->pitch = fmt_byte_pitch(img->fmt, img->width); + else + buf->pitch = img->pitch; + + buf->height = img->height; + buf->width = img->width; + + switch (buf->type) { + case B2R2_FMT_TYPE_SEMI_PLANAR: + buf->chroma_addr = (u32)(((u8 *)addr) + + buf->pitch * buf->height); + break; + case B2R2_FMT_TYPE_PLANAR: + if (is_yuv422_fmt(buf->fmt) || + is_yuv420_fmt(buf->fmt)) { + buf->chroma_addr = (u32)(((u8 *)addr) + + buf->pitch * buf->height); + } else { + buf->chroma_cr_addr = (u32)(((u8 *)addr) + + buf->pitch * buf->height); + } + if (is_yuv420_fmt(buf->fmt)) { + /* + * Use ceil(height/2) in case + * buffer height is not divisible by 2. + */ + buf->chroma_cr_addr = + (u32)(((u8 *)buf->chroma_addr) + + (buf->pitch >> 1) * + ((buf->height + 1) >> 1)); + } else if (is_yuv422_fmt(buf->fmt)) { + buf->chroma_cr_addr = + (u32)(((u8 *)buf->chroma_addr) + + (buf->pitch >> 1) * buf->height); + } else if (is_yvu420_fmt(buf->fmt)) { + buf->chroma_addr = + (u32)(((u8 *)buf->chroma_cr_addr) + + (buf->pitch >> 1) * + ((buf->height + 1) >> 1)); + } else if (is_yvu422_fmt(buf->fmt)) { + buf->chroma_addr = + (u32)(((u8 *)buf->chroma_cr_addr) + + (buf->pitch >> 1) * buf->height); + } + break; + default: + break; + } + + memcpy(&buf->rect, rect, sizeof(buf->rect)); + } +} + +/** + * setup_tmp_buf() - configure a temporary buffer + */ +static int setup_tmp_buf(struct b2r2_node_split_buf *tmp, u32 max_size, + enum b2r2_blt_fmt pref_fmt, u32 pref_width, u32 pref_height) +{ + int ret; + + enum b2r2_blt_fmt fmt; + + u32 width; + u32 height; + u32 pitch; + u32 size; + + /* Determine what format we should use for the tmp buf */ + if (is_rgb_fmt(pref_fmt)) { + fmt = B2R2_BLT_FMT_32_BIT_ARGB8888; + } else if (is_bgr_fmt(pref_fmt)) { + fmt = B2R2_BLT_FMT_32_BIT_ABGR8888; + } else if (is_yvu_fmt(pref_fmt)) { + fmt = B2R2_BLT_FMT_CB_Y_CR_Y; + } else if (is_yuv_fmt(pref_fmt)) { + fmt = B2R2_BLT_FMT_32_BIT_AYUV8888; + } else { + /* Wait, what? */ + b2r2_log_warn("%s: " + "Cannot create tmp buf from this fmt (%d)\n", __func__, + pref_fmt); + ret = -EINVAL; + goto error; + } + + /* See if we can fit the entire preferred rectangle */ + width = pref_width; + height = pref_height; + pitch = fmt_byte_pitch(fmt, width); + size = pitch * height; + + if (size > max_size) { + /* We need to limit the size, so we choose a different width */ + width = MIN(width, B2R2_RESCALE_MAX_WIDTH); + pitch = fmt_byte_pitch(fmt, width); + height = MIN(height, max_size / pitch); + size = pitch * height; + } + + /* We should at least have enough room for one scanline */ + if (height == 0) { + b2r2_log_warn("%s: Not enough tmp mem!\n", + __func__); + ret = -ENOMEM; + goto error; + } + + memset(tmp, 0, sizeof(*tmp)); + + tmp->fmt = fmt; + tmp->type = B2R2_FMT_TYPE_RASTER; + tmp->height = height; + tmp->width = width; + tmp->pitch = pitch; + + tmp->rect.width = width; + tmp->rect.height = tmp->height; + tmp->alpha_range = B2R2_TY_ALPHA_RANGE_255; + + return 0; +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; + +} + +/** + * get_alpha_range() - returns the alpha range of the given format + */ +static enum b2r2_ty get_alpha_range(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_24_BIT_ARGB8565: + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + case B2R2_BLT_FMT_8_BIT_A8: + case B2R2_BLT_FMT_32_BIT_ABGR8888: + return B2R2_TY_ALPHA_RANGE_255; /* 0 - 255 */ + default: + return B2R2_TY_ALPHA_RANGE_128; /* 0 - 128 */ + } +} + +/** + * get_alpha() - returns the pixel alpha in 0...255 range + */ +static u8 get_alpha(enum b2r2_blt_fmt fmt, u32 pixel) +{ + switch (fmt) { + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_32_BIT_ABGR8888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + return (pixel >> 24) & 0xff; + case B2R2_BLT_FMT_32_BIT_VUYA8888: + return pixel & 0xff; + case B2R2_BLT_FMT_24_BIT_ARGB8565: + return (pixel & 0xfff) >> 16; + case B2R2_BLT_FMT_16_BIT_ARGB4444: + return (((pixel >> 12) & 0xf) * 255) / 15; + case B2R2_BLT_FMT_16_BIT_ARGB1555: + return (pixel >> 15) * 255; + case B2R2_BLT_FMT_1_BIT_A1: + return pixel * 255; + case B2R2_BLT_FMT_8_BIT_A8: + return pixel; + default: + return 255; + } +} + +/** + * set_alpha() - returns a color value with the alpha component set + */ +static u32 set_alpha(enum b2r2_blt_fmt fmt, u8 alpha, u32 color) +{ + u32 alpha_mask; + + switch (fmt) { + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_32_BIT_ABGR8888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + color &= 0x00ffffff; + alpha_mask = alpha << 24; + break; + case B2R2_BLT_FMT_32_BIT_VUYA8888: + color &= 0xffffff00; + alpha_mask = alpha; + break; + case B2R2_BLT_FMT_24_BIT_ARGB8565: + color &= 0x00ffff; + alpha_mask = alpha << 16; + break; + case B2R2_BLT_FMT_16_BIT_ARGB4444: + color &= 0x0fff; + alpha_mask = (alpha << 8) & 0xF000; + break; + case B2R2_BLT_FMT_16_BIT_ARGB1555: + color &= 0x7fff; + alpha_mask = (alpha / 255) << 15 ; + break; + case B2R2_BLT_FMT_1_BIT_A1: + color = 0; + alpha_mask = (alpha / 255); + break; + case B2R2_BLT_FMT_8_BIT_A8: + color = 0; + alpha_mask = alpha; + break; + default: + alpha_mask = 0; + } + + return color | alpha_mask; +} + +/** + * fmt_has_alpha() - returns whether the given format carries an alpha value + */ +static bool fmt_has_alpha(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_16_BIT_ARGB4444: + case B2R2_BLT_FMT_16_BIT_ARGB1555: + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_32_BIT_ABGR8888: + case B2R2_BLT_FMT_24_BIT_ARGB8565: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + case B2R2_BLT_FMT_1_BIT_A1: + case B2R2_BLT_FMT_8_BIT_A8: + return true; + default: + return false; + } +} + +/** + * is_rgb_fmt() - returns whether the given format is a rgb format + */ +static bool is_rgb_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_16_BIT_ARGB4444: + case B2R2_BLT_FMT_16_BIT_ARGB1555: + case B2R2_BLT_FMT_16_BIT_RGB565: + case B2R2_BLT_FMT_24_BIT_RGB888: + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_24_BIT_ARGB8565: + case B2R2_BLT_FMT_1_BIT_A1: + case B2R2_BLT_FMT_8_BIT_A8: + return true; + default: + return false; + } +} + +/** + * is_bgr_fmt() - returns whether the given format is a bgr format + */ +static bool is_bgr_fmt(enum b2r2_blt_fmt fmt) +{ + return (fmt == B2R2_BLT_FMT_32_BIT_ABGR8888); +} + +/** + * is_yuv_fmt() - returns whether the given format is a yuv format + */ +static bool is_yuv_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + case B2R2_BLT_FMT_Y_CB_Y_CR: + case B2R2_BLT_FMT_CB_Y_CR_Y: + case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: + case B2R2_BLT_FMT_YUV444_PACKED_PLANAR: + return true; + default: + return false; + } +} + +/** + * is_yvu_fmt() - returns whether the given format is a yvu format + */ +static bool is_yvu_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_Y_CB_Y_CR: + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + return true; + default: + return false; + } +} + +/** + * is_yuv420_fmt() - returns whether the given format is a yuv420 format + */ +static bool is_yuv420_fmt(enum b2r2_blt_fmt fmt) +{ + + switch (fmt) { + case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: + return true; + default: + return false; + } +} + +static bool is_yuv422_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_Y_CB_Y_CR: + case B2R2_BLT_FMT_CB_Y_CR_Y: + case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: + return true; + default: + return false; + } +} + +/** + * is_yvu420_fmt() - returns whether the given format is a yvu420 format + */ +static bool is_yvu420_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + return true; + default: + return false; + } +} + +static bool is_yvu422_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_CB_Y_CR_Y: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + return true; + default: + return false; + } +} + + +/** + * is_yuv444_fmt() - returns whether the given format is a yuv444 format + */ +static bool is_yuv444_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + case B2R2_BLT_FMT_YUV444_PACKED_PLANAR: + return true; + default: + return false; + } +} + +/** + * get_fmt_byte_pitch() - returns the pitch of a pixmap with the given width + */ +static int fmt_byte_pitch(enum b2r2_blt_fmt fmt, u32 width) +{ + int pitch; + + switch (fmt) { + + case B2R2_BLT_FMT_1_BIT_A1: + pitch = width >> 3; /* Shift is faster than division */ + if ((width & 0x3) != 0) /* Check for remainder */ + pitch++; + return pitch; + + case B2R2_BLT_FMT_8_BIT_A8: /* Fall through */ + case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: /* Fall through */ + case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: /* Fall through */ + case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: /* Fall through */ + case B2R2_BLT_FMT_YUV444_PACKED_PLANAR: + return width; + + case B2R2_BLT_FMT_16_BIT_ARGB4444: /* Fall through */ + case B2R2_BLT_FMT_16_BIT_ARGB1555: /* Fall through */ + case B2R2_BLT_FMT_16_BIT_RGB565: /* Fall through */ + case B2R2_BLT_FMT_Y_CB_Y_CR: /* Fall through */ + case B2R2_BLT_FMT_CB_Y_CR_Y: + return width << 1; + + case B2R2_BLT_FMT_24_BIT_RGB888: /* Fall through */ + case B2R2_BLT_FMT_24_BIT_ARGB8565: /* Fall through */ + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_24_BIT_VUY888: + return width * 3; + + case B2R2_BLT_FMT_32_BIT_ARGB8888: /* Fall through */ + case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Fall through */ + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + return width << 2; + + default: + /* Should never, ever happen */ + BUG_ON(1); + return 0; + } +} + +/** + * to_native_fmt() - returns the native B2R2 format + */ +static enum b2r2_native_fmt to_native_fmt(enum b2r2_blt_fmt fmt) +{ + + switch (fmt) { + case B2R2_BLT_FMT_UNUSED: + return B2R2_NATIVE_RGB565; + case B2R2_BLT_FMT_1_BIT_A1: + return B2R2_NATIVE_A1; + case B2R2_BLT_FMT_8_BIT_A8: + return B2R2_NATIVE_A8; + case B2R2_BLT_FMT_16_BIT_RGB565: + return B2R2_NATIVE_RGB565; + case B2R2_BLT_FMT_16_BIT_ARGB4444: + return B2R2_NATIVE_ARGB4444; + case B2R2_BLT_FMT_16_BIT_ARGB1555: + return B2R2_NATIVE_ARGB1555; + case B2R2_BLT_FMT_24_BIT_ARGB8565: + return B2R2_NATIVE_ARGB8565; + case B2R2_BLT_FMT_24_BIT_RGB888: + return B2R2_NATIVE_RGB888; + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_24_BIT_VUY888: /* Not actually supported by HW */ + return B2R2_NATIVE_YCBCR888; + case B2R2_BLT_FMT_32_BIT_ABGR8888: /* Not actually supported by HW */ + case B2R2_BLT_FMT_32_BIT_ARGB8888: + return B2R2_NATIVE_ARGB8888; + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: /* Not actually supported by HW */ + return B2R2_NATIVE_AYCBCR8888; + case B2R2_BLT_FMT_CB_Y_CR_Y: + return B2R2_NATIVE_YCBCR422R; + case B2R2_BLT_FMT_Y_CB_Y_CR: + return B2R2_NATIVE_YCBCR422R; + case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + return B2R2_NATIVE_YCBCR42X_R2B; + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: + return B2R2_NATIVE_YCBCR42X_MBN; + case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV444_PACKED_PLANAR: + return B2R2_NATIVE_YUV; + default: + /* Should never ever happen */ + return B2R2_NATIVE_BYTE; + } +} + +/** + * Bit-expand the color from fmt to RGB888 with blue at LSB. + * Copy MSBs into missing LSBs. + */ +static u32 to_RGB888(u32 color, const enum b2r2_blt_fmt fmt) +{ + u32 out_color = 0; + u32 r = 0; + u32 g = 0; + u32 b = 0; + switch (fmt) { + case B2R2_BLT_FMT_16_BIT_ARGB4444: + r = ((color & 0xf00) << 12) | ((color & 0xf00) << 8); + g = ((color & 0xf0) << 8) | ((color & 0xf0) << 4); + b = ((color & 0xf) << 4) | (color & 0xf); + out_color = r | g | b; + break; + case B2R2_BLT_FMT_16_BIT_ARGB1555: + r = ((color & 0x7c00) << 9) | ((color & 0x7000) << 4); + g = ((color & 0x3e0) << 6) | ((color & 0x380) << 1); + b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2); + out_color = r | g | b; + break; + case B2R2_BLT_FMT_16_BIT_RGB565: + r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3); + g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1); + b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2); + out_color = r | g | b; + break; + case B2R2_BLT_FMT_24_BIT_RGB888: + case B2R2_BLT_FMT_32_BIT_ARGB8888: + out_color = color & 0xffffff; + break; + case B2R2_BLT_FMT_32_BIT_ABGR8888: + r = (color & 0xff) << 16; + g = color & 0xff00; + b = (color & 0xff0000) >> 16; + out_color = r | g | b; + break; + case B2R2_BLT_FMT_24_BIT_ARGB8565: + r = ((color & 0xf800) << 8) | ((color & 0xe000) << 3); + g = ((color & 0x7e0) << 5) | ((color & 0x600) >> 1); + b = ((color & 0x1f) << 3) | ((color & 0x1c) >> 2); + out_color = r | g | b; + break; + default: + break; + } + + return out_color; +} + +/** + * get_fmt_type() - returns the type of the given format (raster, planar, etc.) + */ +static enum b2r2_fmt_type get_fmt_type(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_16_BIT_ARGB4444: + case B2R2_BLT_FMT_16_BIT_ARGB1555: + case B2R2_BLT_FMT_16_BIT_RGB565: + case B2R2_BLT_FMT_24_BIT_RGB888: + case B2R2_BLT_FMT_32_BIT_ARGB8888: + case B2R2_BLT_FMT_Y_CB_Y_CR: + case B2R2_BLT_FMT_CB_Y_CR_Y: + case B2R2_BLT_FMT_32_BIT_ABGR8888: + case B2R2_BLT_FMT_24_BIT_ARGB8565: + case B2R2_BLT_FMT_24_BIT_YUV888: + case B2R2_BLT_FMT_32_BIT_AYUV8888: + case B2R2_BLT_FMT_24_BIT_VUY888: + case B2R2_BLT_FMT_32_BIT_VUYA8888: + case B2R2_BLT_FMT_1_BIT_A1: + case B2R2_BLT_FMT_8_BIT_A8: + return B2R2_FMT_TYPE_RASTER; + case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_PLANAR: + case B2R2_BLT_FMT_YUV444_PACKED_PLANAR: + return B2R2_FMT_TYPE_PLANAR; + case B2R2_BLT_FMT_YUV420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU420_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YVU422_PACKED_SEMI_PLANAR: + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: + return B2R2_FMT_TYPE_SEMI_PLANAR; + default: + return B2R2_FMT_TYPE_RASTER; + } +} + +/** + * is_transform() - returns whether the given request is a transform operation + */ +static bool is_transform(const struct b2r2_blt_request *req) +{ + return (req->user_req.transform != B2R2_BLT_TRANSFORM_NONE) || + (req->user_req.src_rect.width != + req->user_req.dst_rect.width) || + (req->user_req.src_rect.height != + req->user_req.dst_rect.height); +} + +/** + * calculate_scale_factor() - calculates the scale factor between the given + * values + */ +static int calculate_scale_factor(u32 from, u32 to, u16 *sf_out) +{ + int ret; + u32 sf; + + b2r2_log_info("%s\n", __func__); + + if (to == from) { + *sf_out = 1 << 10; + return 0; + } else if (to == 0) { + b2r2_log_err("%s: To is 0!\n", __func__); + BUG_ON(1); + } + + sf = (from << 10) / to; + + if ((sf & 0xffff0000) != 0) { + /* Overflow error */ + b2r2_log_warn("%s: " + "Scale factor too large\n", __func__); + ret = -EINVAL; + goto error; + } else if (sf == 0) { + b2r2_log_warn("%s: " + "Scale factor too small\n", __func__); + ret = -EINVAL; + goto error; + } + + *sf_out = (u16)sf; + + b2r2_log_info("%s exit\n", __func__); + + return 0; + +error: + b2r2_log_warn("%s: Exit...\n", __func__); + return ret; +} + +/** + * rescale() - rescales the given dimension + * + * Returns the rescaled dimension in 22.10 fixed point format. + */ +static s32 rescale(s32 dim, u16 sf) +{ + b2r2_log_info("%s\n", __func__); + + if (sf == 0) { + b2r2_log_err("%s: Scale factor is 0!\n", __func__); + BUG_ON(1); + } + + /* + * This is normally not safe to do, since it drastically decreases the + * precision of the integer part of the dimension. But since the B2R2 + * hardware only has 12-bit registers for these values, we are safe. + */ + return (dim << 20) / sf; +} + +/** + * inv_rescale() - does an inverted rescale of the given dimension + * + * Returns the rescaled dimension in 22.10 fixed point format. + */ +static s32 inv_rescale(s32 dim, u16 sf) +{ + if (sf == 0) + return dim; + + return dim * sf; +} + +/** + * set_target() - sets the target registers of the given node + */ +static void set_target(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf) +{ + s32 l; + s32 r; + s32 t; + s32 b; + + if (buf->tmp_buf_index) + node->dst_tmp_index = buf->tmp_buf_index; + + node->node.GROUP1.B2R2_TBA = addr; + node->node.GROUP1.B2R2_TTY = buf->pitch | to_native_fmt(buf->fmt) | + buf->alpha_range | buf->chroma_selection | buf->hso | + buf->vso | buf->dither | buf->plane_selection; + + if (buf->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + buf->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + node->node.GROUP1.B2R2_TTY |= B2R2_TY_ENDIAN_BIG_NOT_LITTLE; + + node->node.GROUP1.B2R2_TSZ = + ((buf->win.width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) | + ((buf->win.height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT); + node->node.GROUP1.B2R2_TXY = + ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) | + ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT); + + /* Check if the rectangle is outside the buffer */ + if (buf->vso == B2R2_TY_VSO_BOTTOM_TO_TOP) + t = buf->win.y - (buf->win.height - 1); + else + t = buf->win.y; + + if (buf->hso == B2R2_TY_HSO_RIGHT_TO_LEFT) + l = buf->win.x - (buf->win.width - 1); + else + l = buf->win.x; + + r = l + buf->win.width; + b = t + buf->win.height; + + /* Clip to the destination buffer to prevent memory overwrites */ + if ((l < 0) || (r > buf->width) || (t < 0) || (b > buf->height)) { + /* The clip rectangle is including the borders */ + l = MAX(l, 0); + r = MIN(r, buf->width) - 1; + t = MAX(t, 0); + b = MIN(b, buf->height) - 1; + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLIP_WINDOW; + node->node.GROUP0.B2R2_INS |= B2R2_INS_RECT_CLIP_ENABLED; + node->node.GROUP6.B2R2_CWO = + ((l & 0x7FFF) << B2R2_CWS_X_SHIFT) | + ((t & 0x7FFF) << B2R2_CWS_Y_SHIFT); + node->node.GROUP6.B2R2_CWS = + ((r & 0x7FFF) << B2R2_CWO_X_SHIFT) | + ((b & 0x7FFF) << B2R2_CWO_Y_SHIFT); + } + +} + +/** + * set_src() - configures the given source register with the given values + */ +static void set_src(struct b2r2_src_config *src, u32 addr, + struct b2r2_node_split_buf *buf) +{ + src->B2R2_SBA = addr; + src->B2R2_STY = buf->pitch | to_native_fmt(buf->fmt) | + buf->alpha_range | buf->hso | buf->vso; + + if (buf->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + buf->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + src->B2R2_STY |= B2R2_TY_ENDIAN_BIG_NOT_LITTLE; + + src->B2R2_SSZ = ((buf->win.width & 0xfff) << B2R2_SZ_WIDTH_SHIFT) | + ((buf->win.height & 0xfff) << B2R2_SZ_HEIGHT_SHIFT); + src->B2R2_SXY = ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) | + ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT); + +} + +/** + * set_src_1() - sets the source 1 registers of the given node + */ +static void set_src_1(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf) +{ + if (buf->tmp_buf_index) + node->src_tmp_index = buf->tmp_buf_index; + + node->src_index = 1; + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_1; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_1_FETCH_FROM_MEM; + + node->node.GROUP3.B2R2_SBA = addr; + node->node.GROUP3.B2R2_STY = buf->pitch | to_native_fmt(buf->fmt) | + buf->alpha_range | buf->hso | buf->vso; + + if (buf->fmt == B2R2_BLT_FMT_24_BIT_VUY888 || + buf->fmt == B2R2_BLT_FMT_32_BIT_VUYA8888) + node->node.GROUP3.B2R2_STY |= B2R2_TY_ENDIAN_BIG_NOT_LITTLE; + + node->node.GROUP3.B2R2_SXY = + ((buf->win.x & 0xffff) << B2R2_XY_X_SHIFT) | + ((buf->win.y & 0xffff) << B2R2_XY_Y_SHIFT); + + /* Source 1 has no size register */ +} + +/** + * set_src_2() - sets the source 2 registers of the given node + */ +static void set_src_2(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf) +{ + if (buf->tmp_buf_index) + node->src_tmp_index = buf->tmp_buf_index; + + node->src_index = 2; + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_2; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_2_FETCH_FROM_MEM; + + set_src(&node->node.GROUP4, addr, buf); +} + +/** + * set_src_3() - sets the source 3 registers of the given node + */ +static void set_src_3(struct b2r2_node *node, u32 addr, + struct b2r2_node_split_buf *buf) +{ + if (buf->tmp_buf_index) + node->src_tmp_index = buf->tmp_buf_index; + + node->src_index = 3; + + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_SOURCE_3; + node->node.GROUP0.B2R2_INS |= B2R2_INS_SOURCE_3_FETCH_FROM_MEM; + + set_src(&node->node.GROUP5, addr, buf); +} + +/** + * set_ivmx() - configures the iVMX registers with the given values + */ +static void set_ivmx(struct b2r2_node *node, const u32 *vmx_values) +{ + node->node.GROUP0.B2R2_CIC |= B2R2_CIC_IVMX; + node->node.GROUP0.B2R2_INS |= B2R2_INS_IVMX_ENABLED; + + node->node.GROUP15.B2R2_VMX0 = vmx_values[0]; + node->node.GROUP15.B2R2_VMX1 = vmx_values[1]; + node->node.GROUP15.B2R2_VMX2 = vmx_values[2]; + node->node.GROUP15.B2R2_VMX3 = vmx_values[3]; +} + +/** + * reset_nodes() - clears the node list + */ +static void reset_nodes(struct b2r2_node *node) +{ + while (node != NULL) { + memset(&node->node, 0, sizeof(node->node)); + + node->src_tmp_index = 0; + node->dst_tmp_index = 0; + + /* TODO: Implement support for short linked lists */ + node->node.GROUP0.B2R2_CIC = 0x7ffff; + + if (node->next != NULL) + node->node.GROUP0.B2R2_NIP = + node->next->physical_address; + node = node->next; + } +} + +int b2r2_node_split_init(void) +{ + b2r2_filters_init(); + + return 0; +} + +void b2r2_node_split_exit(void) +{ + b2r2_filters_exit(); +} |