diff options
Diffstat (limited to 'drivers/video/b2r2')
-rw-r--r-- | drivers/video/b2r2/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_api.c | 1643 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_blt_main.c | 827 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_control.h | 33 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_core.c | 699 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_core.h | 192 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_input_validation.c | 98 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_input_validation.h | 2 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_internal.h | 89 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_node_split.c | 60 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_profiler_socket.c | 5 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_utils.c | 295 | ||||
-rw-r--r-- | drivers/video/b2r2/b2r2_utils.h | 19 |
13 files changed, 2728 insertions, 1236 deletions
diff --git a/drivers/video/b2r2/Makefile b/drivers/video/b2r2/Makefile index 9900ebf8216..f271f4e7ea1 100644 --- a/drivers/video/b2r2/Makefile +++ b/drivers/video/b2r2/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_FB_B2R2) += b2r2.o -b2r2-objs = b2r2_blt_main.o b2r2_core.o b2r2_mem_alloc.o b2r2_generic.o b2r2_node_gen.o b2r2_node_split.o b2r2_profiler_socket.o b2r2_timing.o b2r2_filters.o b2r2_utils.o b2r2_input_validation.o b2r2_hw_convert.o +b2r2-objs = b2r2_api.o b2r2_blt_main.o b2r2_core.o b2r2_mem_alloc.o b2r2_generic.o b2r2_node_gen.o b2r2_node_split.o b2r2_profiler_socket.o b2r2_timing.o b2r2_filters.o b2r2_utils.o b2r2_input_validation.o b2r2_hw_convert.o ifdef CONFIG_B2R2_DEBUG b2r2-objs += b2r2_debug.o diff --git a/drivers/video/b2r2/b2r2_api.c b/drivers/video/b2r2/b2r2_api.c new file mode 100644 index 00000000000..0361e85ebf3 --- /dev/null +++ b/drivers/video/b2r2/b2r2_api.c @@ -0,0 +1,1643 @@ +/* + * Copyright (C) ST-Ericsson SA 2010/2012 + * + * ST-Ericsson B2R2 Blitter module API + * + * Author: Jorgen Nilsson <jorgen.nilsson@stericsson.com> + * Author: Robert Fekete <robert.fekete@stericsson.com> + * Author: Paul Wannback + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/list.h> +#ifdef CONFIG_ANDROID_PMEM +#include <linux/android_pmem.h> +#endif +#include <linux/fb.h> +#include <linux/uaccess.h> +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#endif +#include <asm/cacheflush.h> +#include <linux/smp.h> +#include <linux/dma-mapping.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/hwmem.h> +#include <linux/kref.h> + +#include "b2r2_internal.h" +#include "b2r2_control.h" +#include "b2r2_core.h" +#include "b2r2_timing.h" +#include "b2r2_utils.h" +#include "b2r2_debug.h" +#include "b2r2_input_validation.h" +#include "b2r2_profiler_socket.h" +#include "b2r2_hw.h" + +/* + * TODO: + * Implementation of query cap + * Support for user space virtual pointer to physically consecutive memory + * Support for user space virtual pointer to physically scattered memory + * Callback reads lagging behind in blt_api_stress app + * Store smaller items in the report list instead of the whole request + * Support read of many report records at once. + */ + +#define DATAS_START_SIZE 10 +#define DATAS_GROW_SIZE 5 + +/** + * @miscdev: The miscdev presenting b2r2 to the system + */ +struct b2r2_blt { + spinlock_t lock; + int next_job_id; + struct miscdevice miscdev; + struct device *dev; + struct mutex datas_lock; + /** + * datas - Stores the b2r2_blt_data mapped to the cliend handle + */ + struct b2r2_blt_data **datas; + /** + * data_count - The current maximum of active datas + */ + int data_count; +}; + +struct b2r2_blt_data { + struct b2r2_control_instance *ctl_instace[B2R2_MAX_NBR_DEVICES]; +}; + +/** + * Used to keep track of coming and going b2r2 cores and + * the number of active instance references + */ +struct b2r2_control_ref { + struct b2r2_control *b2r2_control; + spinlock_t lock; +}; + +/** + * b2r2_blt - The blitter device, /dev/b2r2_blt + */ +struct kref blt_refcount; +static struct b2r2_blt *b2r2_blt; + +/** + * b2r2_control - The core controls and synchronization mechanism + */ +static struct b2r2_control_ref b2r2_controls[B2R2_MAX_NBR_DEVICES]; + +/** + * b2r2_blt_add_control - Add the b2r2 core control + */ +void b2r2_blt_add_control(struct b2r2_control *cont) +{ + unsigned long flags; + BUG_ON(cont->id < 0 || cont->id >= B2R2_MAX_NBR_DEVICES); + + spin_lock_irqsave(&b2r2_controls[cont->id].lock, flags); + if (b2r2_controls[cont->id].b2r2_control == NULL) + b2r2_controls[cont->id].b2r2_control = cont; + spin_unlock_irqrestore(&b2r2_controls[cont->id].lock, flags); +} + +/** + * b2r2_blt_remove_control - Remove the b2r2 core control + */ +void b2r2_blt_remove_control(struct b2r2_control *cont) +{ + unsigned long flags; + BUG_ON(cont->id < 0 || cont->id >= B2R2_MAX_NBR_DEVICES); + + spin_lock_irqsave(&b2r2_controls[cont->id].lock, flags); + b2r2_controls[cont->id].b2r2_control = NULL; + spin_unlock_irqrestore(&b2r2_controls[cont->id].lock, flags); +} + +/** + * b2r2_blt_get_control - Lock control for writing/removal + */ +static struct b2r2_control *b2r2_blt_get_control(int i) +{ + struct b2r2_control *cont; + unsigned long flags; + BUG_ON(i < 0 || i >= B2R2_MAX_NBR_DEVICES); + + spin_lock_irqsave(&b2r2_controls[i].lock, flags); + cont = (struct b2r2_control *) b2r2_controls[i].b2r2_control; + if (cont != NULL) { + if (!cont->enabled) + cont = NULL; + else + kref_get(&cont->ref); + } + spin_unlock_irqrestore(&b2r2_controls[i].lock, flags); + + return cont; +} + +/** + * b2r2_blt_release_control - Unlock control for writing/removal + */ +static void b2r2_blt_release_control(int i) +{ + struct b2r2_control *cont; + unsigned long flags; + BUG_ON(i < 0 || i >= B2R2_MAX_NBR_DEVICES); + + spin_lock_irqsave(&b2r2_controls[i].lock, flags); + cont = (struct b2r2_control *) b2r2_controls[i].b2r2_control; + spin_unlock_irqrestore(&b2r2_controls[i].lock, flags); + if (cont != NULL) + kref_put(&cont->ref, b2r2_core_release); +} + +/** + * Increase size of array containing b2r2 handles + */ +static int grow_datas(void) +{ + struct b2r2_blt_data **new_datas = NULL; + int new_data_count = b2r2_blt->data_count + DATAS_GROW_SIZE; + int ret = 0; + + new_datas = kzalloc(new_data_count * sizeof(*new_datas), GFP_KERNEL); + if (new_datas == NULL) { + ret = -ENOMEM; + goto exit; + } + + memcpy(new_datas, b2r2_blt->datas, + b2r2_blt->data_count * sizeof(*b2r2_blt->datas)); + + kfree(b2r2_blt->datas); + + b2r2_blt->data_count = new_data_count; + b2r2_blt->datas = new_datas; +exit: + return ret; +} + +/** + * Allocate and/or reserve a b2r2 handle + */ +static int alloc_handle(struct b2r2_blt_data *blt_data) +{ + int handle; + int ret; + + mutex_lock(&b2r2_blt->datas_lock); + + if (b2r2_blt->datas == NULL) { + b2r2_blt->datas = kzalloc( + DATAS_START_SIZE * sizeof(*b2r2_blt->datas), + GFP_KERNEL); + if (b2r2_blt->datas == NULL) { + ret = -ENOMEM; + goto exit; + } + b2r2_blt->data_count = DATAS_START_SIZE; + } + + for (handle = 0; handle < b2r2_blt->data_count; handle++) { + if (b2r2_blt->datas[handle] == NULL) { + b2r2_blt->datas[handle] = blt_data; + break; + } + + if (handle == b2r2_blt->data_count - 1) { + ret = grow_datas(); + if (ret < 0) + goto exit; + } + } + ret = handle; +exit: + mutex_unlock(&b2r2_blt->datas_lock); + + return ret; +} + +/** + * Get b2r2 data from b2r2 handle + */ +static struct b2r2_blt_data *get_data(int handle) +{ + if (handle >= b2r2_blt->data_count || handle < 0) + return NULL; + else + return b2r2_blt->datas[handle]; +} + +/** + * Unreserve b2r2 handle + */ +static void free_handle(int handle) +{ + if (handle < b2r2_blt->data_count && handle >= 0) + b2r2_blt->datas[handle] = NULL; +} + +/** + * Get the next job number. This is the one returned to the client + * if the blit request was successful. + */ +static int get_next_job_id(void) +{ + int job_id; + unsigned long flags; + + spin_lock_irqsave(&b2r2_blt->lock, flags); + if (b2r2_blt->next_job_id < 1) + b2r2_blt->next_job_id = 1; + job_id = b2r2_blt->next_job_id++; + spin_unlock_irqrestore(&b2r2_blt->lock, flags); + + return job_id; +} + +/** + * Limit the number of cores used in some "easy" and impossible cases + */ +static int limit_blits(int n_split, struct b2r2_blt_req *user_req) +{ + if (n_split <= 1) + return n_split; + + if (user_req->dst_rect.width < 24 && user_req->dst_rect.height < 24) + return 1; + + if (user_req->src_rect.width < n_split && + user_req->src_rect.height < n_split) + return 1; + + return n_split; +} + +/** + * Check if the format inherently requires the b2r2 scaling engine to be active + */ +static bool is_scaling_fmt(enum b2r2_blt_fmt fmt) +{ + /* Plane separated formats must be treated as scaling */ + switch (fmt) { + 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: + 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 true; + default: + return false; + } +} + +/** + * Check for macroblock formats + */ +static bool is_mb_fmt(enum b2r2_blt_fmt fmt) +{ + switch (fmt) { + case B2R2_BLT_FMT_YUV420_PACKED_SEMIPLANAR_MB_STE: + case B2R2_BLT_FMT_YUV422_PACKED_SEMIPLANAR_MB_STE: + return true; + default: + return false; + } +} + +/** + * Split a request rectangle on available cores + */ +static int b2r2_blt_split_request(struct b2r2_blt_data *blt_data, + struct b2r2_blt_req *user_req, + struct b2r2_blt_request **split_requests, + struct b2r2_control_instance **ctl, + int *n_split) +{ + int sstep_x, sstep_y, dstep_x, dstep_y; + int dstart_x, dstart_y; + int bstart_x, bstart_y; + int dpos_x, dpos_y; + int bpos_x, bpos_y; + int dso_x = 1; + int dso_y = 1; + int sf_x, sf_y; + int i; + int srw, srh; + int drw, drh; + bool ssplit_x = true; + bool dsplit_x = true; + enum b2r2_blt_transform transform; + bool is_rotation = false; + bool is_scaling = false; + bool bg_blend = false; + u32 core_mask = 0; + + srw = user_req->src_rect.width; + srh = user_req->src_rect.height; + drw = user_req->dst_rect.width; + drh = user_req->dst_rect.height; + transform = user_req->transform; + + /* Early exit in the basic cases */ + if (*n_split == 0) { + return -ENOSYS; + } else if (*n_split == 1 || + (srw < *n_split && srh < *n_split) || + (drw < *n_split && drh < *n_split) || + is_mb_fmt(user_req->src_img.fmt)) { + /* Handle macroblock formats with one + * core for now since there seems to be some bug + * related to macroblock access patterns + */ + memcpy(&split_requests[0]->user_req, + user_req, + sizeof(*user_req)); + split_requests[0]->core_mask = 1; + *n_split = 1; + return 0; + } + + /* + * TODO: fix the load balancing algorithm + */ + + is_rotation = (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) != 0; + + /* Check for scaling */ + if (is_rotation) { + is_scaling = (user_req->src_rect.width != + user_req->dst_rect.height) || + (user_req->src_rect.height != + user_req->dst_rect.width); + } else { + is_scaling = (user_req->src_rect.width != + user_req->dst_rect.width) || + (user_req->src_rect.height != + user_req->dst_rect.height); + } + + is_scaling = is_scaling || + is_scaling_fmt(user_req->src_img.fmt) || + is_scaling_fmt(user_req->dst_img.fmt); + + bg_blend = ((user_req->flags & B2R2_BLT_FLAG_BG_BLEND) != 0); + + /* + * Split the request + */ + + b2r2_log_info(b2r2_blt->dev, "%s: In (t:0x%08X, f:0x%08X):\n" + "\tsrc_rect x:%d, y:%d, w:%d, h:%d src fmt:0x%x\n" + "\tdst_rect x:%d, y:%d, w:%d, h:%d dst fmt:0x%x\n", + __func__, + user_req->transform, + user_req->flags, + user_req->src_rect.x, + user_req->src_rect.y, + user_req->src_rect.width, + user_req->src_rect.height, + user_req->src_img.fmt, + user_req->dst_rect.x, + user_req->dst_rect.y, + user_req->dst_rect.width, + user_req->dst_rect.height, + user_req->dst_img.fmt); + + /* TODO: We need sub pixel precision here, + * or a better way to split rects */ + dstart_x = user_req->dst_rect.x; + dstart_y = user_req->dst_rect.y; + if (bg_blend) { + bstart_x = user_req->bg_rect.x; + bstart_y = user_req->bg_rect.y; + } + + if (srw && srh) { + if ((srw < srh) && !is_scaling) { + ssplit_x = false; + sstep_y = srh / *n_split; + /* Round up */ + if (srh % (*n_split)) + sstep_y++; + + if (srh > 16) + sstep_y = ((sstep_y + 16) >> 4) << 4; + + if (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) { + sf_y = (drw << 10) / srh; + dstep_x = (sf_y * sstep_y) >> 10; + } else { + dsplit_x = false; + sf_y = (drh << 10) / srh; + dstep_y = (sf_y * sstep_y) >> 10; + } + } else { + sstep_x = srw / *n_split; + /* Round up */ + if (srw % (*n_split)) + sstep_x++; + + if (is_scaling) { + int scale_step_size = + B2R2_RESCALE_MAX_WIDTH - 1; + int pad = (scale_step_size - + (sstep_x % scale_step_size)); + if ((sstep_x + pad) < srw) + sstep_x += pad; + } else { + /* Aim for even 16px multiples */ + if ((sstep_x & 0xF) && ((sstep_x + 16) < srw)) + sstep_x = ((sstep_x + 16) >> 4) << 4; + } + + if (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) { + dsplit_x = false; + sf_x = (drh << 10) / srw; + dstep_y = (sf_x * sstep_x) >> 10; + } else { + sf_x = (drw << 10) / srw; + dstep_x = (sf_x * sstep_x) >> 10; + } + } + + } else { + sstep_x = sstep_y = 0; + + if (drw < drh) { + dsplit_x = false; + dstep_y = drh / *n_split; + /* Round up */ + if (drh % *n_split) + dstep_y++; + + /* Aim for even 16px multiples */ + if ((dstep_y & 0xF) && ((dstep_y + 16) < drh)) + dstep_y = ((dstep_y + 16) >> 4) << 4; + } else { + dstep_x = drw / *n_split; + /* Round up */ + if (drw % *n_split) + dstep_x++; + + /* Aim for even 16px multiples */ + if ((dstep_x & 0xF) && ((dstep_x + 16) < drw)) + dstep_x = ((dstep_x + 16) >> 4) << 4; + } + } + + /* Check for flip and rotate to establish destination + * step order */ + if (transform & B2R2_BLT_TRANSFORM_FLIP_H) { + dstart_x += drw; + if (bg_blend) + bstart_x += drw; + dso_x = -1; + } + if ((transform & B2R2_BLT_TRANSFORM_FLIP_V) || + (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90)) { + dstart_y += drh; + if (bg_blend) + bstart_y += drh; + dso_y = -1; + } + + /* Set scan starting position */ + dpos_x = dstart_x; + dpos_y = dstart_y; + if (bg_blend) { + bpos_x = bstart_x; + bpos_y = bstart_y; + } + + for (i = 0; i < *n_split; i++) { + struct b2r2_blt_req *sreq = + &split_requests[i]->user_req; + + /* First mimic all */ + memcpy(sreq, user_req, sizeof(*user_req)); + + /* Then change the rects */ + if (srw && srh) { + if (ssplit_x) { + if (sstep_x > 0) { + sreq->src_rect.width = + min(sstep_x, srw); + sreq->src_rect.x += i*sstep_x; + srw -= sstep_x; + } else { + sreq->src_rect.width = srw; + } + } else { + if (sstep_y > 0) { + sreq->src_rect.y += i*sstep_y; + sreq->src_rect.height = + min(sstep_y, srh); + srh -= sstep_y; + } else { + sreq->src_rect.height = srh; + } + } + } + + if (dsplit_x) { + int sx = min(dstep_x, drw); + if (dso_x < 0) { + dpos_x += dso_x * sx; + if (bg_blend) + bpos_x += dso_x * sx; + } + sreq->dst_rect.width = sx; + sreq->dst_rect.x = dpos_x; + if (bg_blend) { + sreq->bg_rect.width = sx; + sreq->bg_rect.x = bpos_x; + } + if (dso_x > 0) { + dpos_x += dso_x * sx; + if (bg_blend) + bpos_x += dso_x * sx; + } + drw -= sx; + } else { + int sy = min(dstep_y, drh); + if (dso_y < 0) { + dpos_y += dso_y * sy; + if (bg_blend) + bpos_y += dso_y * sy; + } + sreq->dst_rect.height = sy; + sreq->dst_rect.y = dpos_y; + if (bg_blend) { + sreq->bg_rect.height = sy; + sreq->bg_rect.y = bpos_y; + } + if (dso_y > 0) { + dpos_y += dso_y * sy; + if (bg_blend) + bpos_y += dso_y * sy; + } + drh -= sy; + } + + b2r2_log_info(b2r2_blt->dev, "%s: Out:\n" + "\tsrc_rect x:%d, y:%d, w:%d, h:%d\n" + "\tdst_rect x:%d, y:%d, w:%d, h:%d\n" + "\tbg_rect x:%d, y:%d, w:%d, h:%d\n", + __func__, + sreq->src_rect.x, + sreq->src_rect.y, + sreq->src_rect.width, + sreq->src_rect.height, + sreq->dst_rect.x, + sreq->dst_rect.y, + sreq->dst_rect.width, + sreq->dst_rect.height, + sreq->bg_rect.x, + sreq->bg_rect.y, + sreq->bg_rect.width, + sreq->bg_rect.height); + + core_mask |= (1 << i); + } + + for (i = 0; i < *n_split; i++) + split_requests[i]->core_mask = core_mask; + + return 0; +} + +/** + * Get available b2r2 control instances. It will be limited + * to the number of cores available at the current point in time. + * It will also cause the cores to stay active during the time until + * release_control_instances is called. + */ +static void get_control_instances(struct b2r2_blt_data *blt_data, + struct b2r2_control_instance **ctl, int max_size, + int *count) +{ + int i; + + *count = 0; + for (i = 0; i < max_size; i++) { + struct b2r2_control_instance *ci = blt_data->ctl_instace[i]; + if (ci) { + struct b2r2_control *cont = + b2r2_blt_get_control(ci->control_id); + if (cont) { + ctl[*count] = ci; + *count += 1; + } + } + } +} + +/** + * Release b2r2 control instances. The cores allocated for the request + * are given back. + */ +static void release_control_instances(struct b2r2_control_instance **ctl, + int count) +{ + int i; + + /* Release the handles to the core controls */ + for (i = 0; i < count; i++) { + if (ctl[i]) + b2r2_blt_release_control(ctl[i]->control_id); + } +} + +/** + * Free b2r2 request + */ +static void b2r2_free_request(struct b2r2_blt_request *request) +{ + if (request) { + /* Free requests in split_requests */ + if (request->clut) + dma_free_coherent(b2r2_blt->dev, + CLUT_SIZE, + request->clut, + request->clut_phys_addr); + request->clut = NULL; + request->clut_phys_addr = 0; + kfree(request); + } +} + +/** + * Allocate internal b2r2 request based on user input. + */ +static int b2r2_alloc_request(struct b2r2_blt_req *user_req, + bool us_req, struct b2r2_blt_request **request_out) +{ + int ret = 0; + struct b2r2_blt_request *request = + kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return -ENOMEM; + + /* Initialize the structure */ + INIT_LIST_HEAD(&request->list); + + /* + * If the user specified a color look-up table, + * make a copy that the HW can use. + */ + if ((user_req->flags & + B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) { + request->clut = dma_alloc_coherent( + b2r2_blt->dev, + CLUT_SIZE, + &(request->clut_phys_addr), + GFP_DMA | GFP_KERNEL); + if (request->clut == NULL) { + b2r2_log_err(b2r2_blt->dev, + "%s CLUT allocation " + "failed.\n", __func__); + ret = -ENOMEM; + goto exit; + } + + if (us_req) { + if (copy_from_user(request->clut, + user_req->clut, CLUT_SIZE)) { + b2r2_log_err(b2r2_blt->dev, "%s: CLUT " + "copy_from_user failed\n", + __func__); + ret = -EFAULT; + goto exit; + } + } else { + memcpy(request->clut, user_req->clut, + CLUT_SIZE); + } + } + + request->profile = is_profiler_registered_approx(); + + *request_out = request; +exit: + if (ret != 0) + b2r2_free_request(request); + + return ret; +} + +/** + * Do the blit job split on available cores. + */ +static int b2r2_blt_blit_internal(int handle, + struct b2r2_blt_req *user_req, + bool us_req) +{ + int request_id; + int i; + int n_instance = 0; + int n_blit = 0; + int ret = 0; + struct b2r2_blt_data *blt_data; + struct b2r2_blt_req ureq; + + /* The requests and the designated workers */ + struct b2r2_blt_request *split_requests[B2R2_MAX_NBR_DEVICES]; + struct b2r2_control_instance *ctl[B2R2_MAX_NBR_DEVICES]; + + blt_data = get_data(handle); + if (blt_data == NULL) { + b2r2_log_warn(b2r2_blt->dev, + "%s, blitter instance not found (handle=%d)\n", + __func__, handle); + return -ENOSYS; + } + + /* Get the b2r2 core controls for the job */ + get_control_instances(blt_data, ctl, B2R2_MAX_NBR_DEVICES, &n_instance); + if (n_instance == 0) { + b2r2_log_err(b2r2_blt->dev, "%s: No b2r2 cores available.\n", + __func__); + return -ENOSYS; + } + + /* Get the user data */ + if (us_req) { + if (copy_from_user(&ureq, user_req, sizeof(ureq))) { + b2r2_log_err(b2r2_blt->dev, + "%s: copy_from_user failed\n", + __func__); + ret = -EFAULT; + goto exit; + } + } else { + memcpy(&ureq, user_req, sizeof(ureq)); + } + + /* + * B2R2 cannot handle destination clipping on buffers + * allocated close to 64MiB bank boundaries. + * recalculate src_ and dst_rect to avoid clipping. + * + * Also this is needed to ensure the request split + * operates on visible areas + */ + b2r2_recalculate_rects(b2r2_blt->dev, &ureq); + + if (!b2r2_validate_user_req(b2r2_blt->dev, &ureq)) { + b2r2_log_warn(b2r2_blt->dev, + "%s: b2r2_validate_user_req failed.\n", + __func__); + ret = -EINVAL; + goto exit; + } + + /* Don't split small requests */ + n_blit = limit_blits(n_instance, &ureq); + + /* The id needs to be universal on + * all cores */ + request_id = get_next_job_id(); + +#ifdef CONFIG_B2R2_GENERIC_ONLY + /* Limit the generic only solution to one core (for now) */ + n_blit = 1; +#endif + + for (i = 0; i < n_blit; i++) { + ret = b2r2_alloc_request(&ureq, us_req, &split_requests[i]); + if (ret < 0 || !split_requests[i]) { + b2r2_log_err(b2r2_blt->dev, "%s: Failed to alloc mem\n", + __func__); + ret = -ENOMEM; + break; + } + split_requests[i]->instance = ctl[i]; + split_requests[i]->job.job_id = request_id; + split_requests[i]->job.data = (int) ctl[i]->control->data; + } + + /* Split the request */ + if (ret >= 0) + ret = b2r2_blt_split_request(blt_data, &ureq, + &split_requests[0], &ctl[0], &n_blit); + + /* If anything failed, clean up allocated memory */ + if (ret < 0) { + for (i = 0; i < n_blit; i++) + b2r2_free_request(split_requests[i]); + b2r2_log_err(b2r2_blt->dev, + "%s: b2r2_blt_split_request failed.\n", + __func__); + goto exit; + } + +#ifdef CONFIG_B2R2_GENERIC_ONLY + if (ureq.flags & B2R2_BLT_FLAG_BG_BLEND) { + /* No support for BG BLEND in generic + * implementation yet */ + b2r2_log_warn(b2r2_blt->dev, "%s: Unsupported: " + "Background blend in b2r2_generic_blt\n", + __func__); + ret = -ENOSYS; + b2r2_free_request(split_requests[0]); + goto exit; + } + /* Use the generic path for all operations */ + ret = b2r2_generic_blt(split_requests[0]); +#else + /* Call each blitter control */ + for (i = 0; i < n_blit; i++) { + ret = b2r2_control_blt(split_requests[i]); + if (ret < 0) { + b2r2_log_warn(b2r2_blt->dev, + "%s: b2r2_control_blt failed.\n", __func__); + break; + } + } + if (ret != -ENOSYS) { + int j; + /* TODO: if one blitter fails then cancel the jobs added */ + + /* Call waitjob for successful jobs + * (synchs if specified in request) */ + if (ureq.flags & B2R2_BLT_FLAG_DRY_RUN) + goto exit; + + for (j = 0; j < i; j++) { + int rtmp; + + rtmp = b2r2_control_waitjob(split_requests[j]); + if (rtmp < 0) { + b2r2_log_err(b2r2_blt->dev, + "%s: b2r2_control_waitjob failed.\n", + __func__); + } + + /* Save just the one error */ + ret = (ret >= 0) ? rtmp : ret; + } + } +#endif +#ifdef CONFIG_B2R2_GENERIC_FALLBACK + if (ret == -ENOSYS) { + struct b2r2_blt_request *request_gen = NULL; + if (ureq.flags & B2R2_BLT_FLAG_BG_BLEND) { + /* No support for BG BLEND in generic + * implementation yet */ + b2r2_log_warn(b2r2_blt->dev, "%s: Unsupported: " + "Background blend in b2r2_generic_blt\n", + __func__); + goto exit; + } + + b2r2_log_info(b2r2_blt->dev, + "b2r2_blt=%d Going generic.\n", ret); + ret = b2r2_alloc_request(&ureq, us_req, &request_gen); + if (ret < 0 || !request_gen) { + b2r2_log_err(b2r2_blt->dev, + "%s: Failed to alloc mem for " + "request_gen\n", __func__); + ret = -ENOMEM; + goto exit; + } + + /* Initialize the structure */ + request_gen->instance = ctl[0]; + memcpy(&request_gen->user_req, &ureq, + sizeof(request_gen->user_req)); + request_gen->core_mask = 1; + request_gen->job.job_id = request_id; + request_gen->job.data = (int) ctl[0]->control->data; + + ret = b2r2_generic_blt(request_gen); + b2r2_log_info(b2r2_blt->dev, "\nb2r2_generic_blt=%d " + "Generic done.\n", ret); + } +#endif +exit: + release_control_instances(ctl, n_instance); + + ret = ret >= 0 ? request_id : ret; + + return ret; +} + +/** + * Free the memory used for the b2r2_blt device + */ +static void b2r2_blt_release(struct kref *ref) +{ + BUG_ON(b2r2_blt == NULL); + if (b2r2_blt == NULL) + return; + kfree(b2r2_blt->datas); + kfree(b2r2_blt); + b2r2_blt = NULL; +} + +int b2r2_blt_open(void) +{ + int ret = 0; + struct b2r2_blt_data *blt_data = NULL; + int i; + + if (!atomic_inc_not_zero(&blt_refcount.refcount)) + return -ENOSYS; + + /* Allocate blitter instance data structure */ + blt_data = (struct b2r2_blt_data *) + kzalloc(sizeof(*blt_data), GFP_KERNEL); + if (!blt_data) { + b2r2_log_err(b2r2_blt->dev, "%s: Failed to alloc\n", __func__); + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) { + struct b2r2_control *control = b2r2_blt_get_control(i); + if (control != NULL) { + struct b2r2_control_instance *ci; + + /* Allocate and initialize the control instance */ + ci = kzalloc(sizeof(*ci), GFP_KERNEL); + if (!ci) { + b2r2_log_err(b2r2_blt->dev, + "%s: Failed to alloc\n", + __func__); + ret = -ENOMEM; + b2r2_blt_release_control(i); + goto err; + } + ci->control_id = i; + ci->control = control; + ret = b2r2_control_open(ci); + if (ret < 0) { + b2r2_log_err(b2r2_blt->dev, + "%s: Failed to open b2r2 control %d\n", + __func__, i); + kfree(ci); + b2r2_blt_release_control(i); + goto err; + } + blt_data->ctl_instace[i] = ci; + b2r2_blt_release_control(i); + } else { + blt_data->ctl_instace[i] = NULL; + } + } + + /* TODO: Create kernel worker kthread */ + + ret = alloc_handle(blt_data); + if (ret < 0) + goto err; + + kref_put(&blt_refcount, b2r2_blt_release); + + return ret; + +err: + /* Destroy the blitter instance data structure */ + if (blt_data) { + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) + kfree(blt_data->ctl_instace[i]); + kfree(blt_data); + } + + kref_put(&blt_refcount, b2r2_blt_release); + + return ret; +} +EXPORT_SYMBOL(b2r2_blt_open); + +int b2r2_blt_close(int handle) +{ + int i; + struct b2r2_blt_data *blt_data; + int ret = 0; + + if (!atomic_inc_not_zero(&blt_refcount.refcount)) + return -ENOSYS; + + b2r2_log_info(b2r2_blt->dev, "%s\n", __func__); + + blt_data = get_data(handle); + if (blt_data == NULL) { + b2r2_log_warn(b2r2_blt->dev, + "%s, blitter data not found (handle=%d)\n", + __func__, handle); + ret = -ENOSYS; + goto exit; + } + free_handle(handle); + + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) { + struct b2r2_control_instance *ci = + blt_data->ctl_instace[i]; + if (ci != NULL) { + struct b2r2_control *cont = + b2r2_blt_get_control(ci->control_id); + if (cont) { + /* Release the instance */ + b2r2_control_release(ci); + b2r2_blt_release_control(ci->control_id); + } + kfree(ci); + } + } + kfree(blt_data); + +exit: + kref_put(&blt_refcount, b2r2_blt_release); + + return ret; +} +EXPORT_SYMBOL(b2r2_blt_close); + +int b2r2_blt_request(int handle, + struct b2r2_blt_req *user_req) +{ + int ret = 0; + + if (!atomic_inc_not_zero(&blt_refcount.refcount)) + return -ENOSYS; + + /* Exclude some currently unsupported cases */ + if ((user_req->flags & B2R2_BLT_FLAG_REPORT_WHEN_DONE) || + (user_req->flags & B2R2_BLT_FLAG_REPORT_PERFORMANCE) || + (user_req->report1 != 0)) { + b2r2_log_err(b2r2_blt->dev, + "%s No callback support in the kernel API\n", + __func__); + ret = -ENOSYS; + goto exit; + } + + ret = b2r2_blt_blit_internal(handle, user_req, false); + +exit: + kref_put(&blt_refcount, b2r2_blt_release); + + return ret; +} +EXPORT_SYMBOL(b2r2_blt_request); + +int b2r2_blt_synch(int handle, int request_id) +{ + int ret = 0; + int i; + int n_synch = 0; + struct b2r2_control_instance *ctl[B2R2_MAX_NBR_DEVICES]; + struct b2r2_blt_data *blt_data; + + if (!atomic_inc_not_zero(&blt_refcount.refcount)) + return -ENOSYS; + + b2r2_log_info(b2r2_blt->dev, "%s\n", __func__); + + blt_data = get_data(handle); + if (blt_data == NULL) { + b2r2_log_warn(b2r2_blt->dev, + "%s, blitter data not found (handle=%d)\n", + __func__, handle); + ret = -ENOSYS; + goto exit; + } + + /* Get the b2r2 core controls for the job */ + get_control_instances(blt_data, ctl, B2R2_MAX_NBR_DEVICES, &n_synch); + if (n_synch == 0) { + b2r2_log_err(b2r2_blt->dev, "%s: No b2r2 cores available.\n", + __func__); + ret = -ENOSYS; + goto exit; + } + + for (i = 0; i < n_synch; i++) { + ret = b2r2_control_synch(ctl[i], request_id); + if (ret != 0) { + b2r2_log_err(b2r2_blt->dev, + "%s: b2r2_control_synch failed.\n", + __func__); + break; + } + } + + /* Release the handles to the core controls */ + release_control_instances(ctl, n_synch); + +exit: + kref_put(&blt_refcount, b2r2_blt_release); + + b2r2_log_info(b2r2_blt->dev, + "%s, request_id=%d, returns %d\n", __func__, request_id, ret); + + return ret; +} +EXPORT_SYMBOL(b2r2_blt_synch); + +/** + * The user space API + */ + +/** + * b2r2_blt_open_us - Implements file open on the b2r2_blt device + * + * @inode: File system inode + * @filp: File pointer + * + * A b2r2_blt_data handle is created and stored in the file structure. + */ +static int b2r2_blt_open_us(struct inode *inode, struct file *filp) +{ + int ret = 0; + int handle; + + handle = b2r2_blt_open(); + if (handle < 0) { + b2r2_log_err(b2r2_blt->dev, "%s: Failed to open handle\n", + __func__); + ret = handle; + goto exit; + } + filp->private_data = (void *) handle; +exit: + return ret; +} + +/** + * b2r2_blt_release_us - Implements last close on an instance of + * the b2r2_blt device + * + * @inode: File system inode + * @filp: File pointer + * + * All active jobs are finished or cancelled and allocated data + * is released. + */ +static int b2r2_blt_release_us(struct inode *inode, struct file *filp) +{ + int ret; + ret = b2r2_blt_close((int) filp->private_data); + return ret; +} + +/** + * Query B2R2 capabilities + * + * @blt_data: The B2R2 BLT instance + * @query_cap: The structure receiving the capabilities + */ +static int b2r2_blt_query_cap(struct b2r2_blt_data *blt_data, + struct b2r2_blt_query_cap *query_cap) +{ + /* FIXME: Not implemented yet */ + return -ENOSYS; +} + +/** + * b2r2_blt_ioctl_us - This routine implements b2r2_blt ioctl interface + * + * @file: file pointer. + * @cmd :ioctl command. + * @arg: input argument for ioctl. + * + * Returns 0 if OK else negative error code + */ +static long b2r2_blt_ioctl_us(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + int handle = (int) file->private_data; + + /** Process actual ioctl */ + b2r2_log_info(b2r2_blt->dev, "%s\n", __func__); + + /* Get the instance from the file structure */ + switch (cmd) { + case B2R2_BLT_IOC: { + /* arg is user pointer to struct b2r2_blt_request */ + ret = b2r2_blt_blit_internal(handle, + (struct b2r2_blt_req *) arg, true); + break; + } + + case B2R2_BLT_SYNCH_IOC: + /* arg is request_id */ + ret = b2r2_blt_synch(handle, (int) arg); + break; + + case B2R2_BLT_QUERY_CAP_IOC: { + /* Arg is struct b2r2_blt_query_cap */ + struct b2r2_blt_query_cap query_cap; + struct b2r2_blt_data *blt_data = get_data(handle); + + /* Get the user data */ + if (copy_from_user(&query_cap, (void *)arg, + sizeof(query_cap))) { + b2r2_log_err(b2r2_blt->dev, + "%s: copy_from_user failed\n", + __func__); + ret = -EFAULT; + goto exit; + } + + /* Fill in our capabilities */ + ret = b2r2_blt_query_cap(blt_data, &query_cap); + + /* Return data to user */ + if (copy_to_user((void *)arg, &query_cap, + sizeof(query_cap))) { + b2r2_log_err(b2r2_blt->dev, + "%s: copy_to_user failed\n", + __func__); + ret = -EFAULT; + goto exit; + } + break; + } + + default: + /* Unknown command */ + b2r2_log_err(b2r2_blt->dev, "%s: Unknown cmd %d\n", + __func__, cmd); + ret = -EINVAL; + break; + + } + +exit: + if (ret < 0) + b2r2_log_err(b2r2_blt->dev, "%s: Return with error %d!\n", + __func__, -ret); + + return ret; +} + +/** + * b2r2_blt_poll - Support for user-space poll, select & epoll. + * Used for user-space callback + * + * @filp: File to poll on + * @wait: Poll table to wait on + * + * This function checks if there are anything to read + */ +static unsigned b2r2_blt_poll_us(struct file *filp, poll_table *wait) +{ + struct b2r2_blt_data *blt_data = + (struct b2r2_blt_data *) filp->private_data; + struct b2r2_control_instance *ctl[B2R2_MAX_NBR_DEVICES]; + unsigned int ret = POLLIN | POLLRDNORM; + int n_poll = 0; + int i; + + b2r2_log_info(b2r2_blt->dev, "%s\n", __func__); + + /* Get the b2r2 core controls for the job */ + get_control_instances(blt_data, ctl, B2R2_MAX_NBR_DEVICES, &n_poll); + if (n_poll == 0) { + b2r2_log_err(b2r2_blt->dev, "%s: No b2r2 cores available.\n", + __func__); + ret = -ENOSYS; + goto exit; + } + + /* Poll each core control instance */ + for (i = 0; i < n_poll && ret != 0; i++) { + poll_wait(filp, &ctl[i]->report_list_waitq, wait); + mutex_lock(&ctl[i]->lock); + if (list_empty(&ctl[i]->report_list)) + ret = 0; /* No reports */ + mutex_unlock(&ctl[i]->lock); + } + + /* Release the handles to the core controls */ + release_control_instances(ctl, n_poll); + +exit: + b2r2_log_info(b2r2_blt->dev, "%s: returns %d, n_poll: %d\n", + __func__, ret, n_poll); + + return ret; +} + +/** + * b2r2_blt_read - Read report data, user for user-space callback + * + * @filp: File pointer + * @buf: User space buffer + * @count: Number of bytes to read + * @f_pos: File position + * + * Returns number of bytes read or negative error code + */ +static ssize_t b2r2_blt_read_us(struct file *filp, + char __user *buf, size_t count, loff_t *f_pos) +{ + int ret = 0; + int n_read = 0; + int i; + int first_index = 0; + struct b2r2_blt_report report; + struct b2r2_blt_request *requests[B2R2_MAX_NBR_DEVICES]; + struct b2r2_blt_data *blt_data = + (struct b2r2_blt_data *) filp->private_data; + struct b2r2_control_instance *ctl[B2R2_MAX_NBR_DEVICES]; + struct b2r2_control_instance *first = NULL; + bool block = ((filp->f_flags & O_NONBLOCK) == 0); + u32 core_mask = 0; + + b2r2_log_info(b2r2_blt->dev, "%s\n", __func__); + + /* + * We return only complete report records, one at a time. + * Might be more efficient to support read of many. + */ + count = (count / sizeof(struct b2r2_blt_report)) * + sizeof(struct b2r2_blt_report); + if (count > sizeof(struct b2r2_blt_report)) + count = sizeof(struct b2r2_blt_report); + if (count == 0) + return count; + + memset(ctl, 0, sizeof(*ctl) * B2R2_MAX_NBR_DEVICES); + /* Get the b2r2 core controls for the job */ + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) { + struct b2r2_control_instance *ci = blt_data->ctl_instace[i]; + if (ci) { + struct b2r2_control *cont = + b2r2_blt_get_control(ci->control_id); + if (cont) { + ctl[i] = ci; + n_read++; + } + } + } + if (n_read == 0) { + b2r2_log_err(b2r2_blt->dev, "%s: No b2r2 cores available.\n", + __func__); + return -ENOSYS; + } + + /* Find which control to ask for a report first */ + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) { + if (ctl[i] != NULL) { + first = ctl[i]; + first_index = i; + break; + } + } + if (!first) { + b2r2_log_err(b2r2_blt->dev, "%s: Internal error.\n", + __func__); + return -ENOSYS; + } + + memset(requests, 0, sizeof(*requests) * B2R2_MAX_NBR_DEVICES); + /* Read report from core 0 */ + ret = b2r2_control_read(first, &requests[first_index], block); + if (ret <= 0 || requests[0] == NULL) { + b2r2_log_err(b2r2_blt->dev, "%s: b2r2_control_read failed.\n", + __func__); + ret = -EFAULT; + goto exit; + } + core_mask = requests[first_index]->core_mask >> 1; + core_mask &= ~(1 << first_index); + + /* If there are any more cores, try reading the report + * with the specific ID from the other cores */ + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) { + if ((core_mask & 1) && (ctl[i] != NULL)) { + /* TODO: Do we need to wait here? */ + ret = b2r2_control_read_id(ctl[i], &requests[i], block, + requests[first_index]->request_id); + if (ret <= 0 || requests[i] == NULL) { + b2r2_log_err(b2r2_blt->dev, + "%s: b2r2_control_read failed.\n", + __func__); + break; + } + } + core_mask = core_mask >> 1; + } + + if (ret > 0) { + /* Construct a report and copy to userland */ + report.request_id = requests[0]->request_id; + report.report1 = requests[0]->user_req.report1; + report.report2 = requests[0]->user_req.report2; + report.usec_elapsed = 0; /* TBD */ + + if (copy_to_user(buf, &report, sizeof(report))) { + b2r2_log_err(b2r2_blt->dev, + "%s: copy_to_user failed.\n", + __func__); + ret = -EFAULT; + } + } + + if (ret > 0) { + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) + /* + * Release matching the addref when the job was put + * into the report list + */ + if (requests[i] != NULL) + b2r2_core_job_release(&requests[i]->job, + __func__); + } else { + /* We failed at one core or copy to user failed */ + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) + if (requests[i] != NULL) + list_add(&requests[i]->list, + &ctl[i]->report_list); + goto exit; + } + + ret = count; + +exit: + /* Release the handles to the core controls */ + release_control_instances(ctl, n_read); + + return ret; +} + +/** + * b2r2_blt_fops - File operations for b2r2_blt + */ +static const struct file_operations b2r2_blt_fops = { + .owner = THIS_MODULE, + .open = b2r2_blt_open_us, + .release = b2r2_blt_release_us, + .unlocked_ioctl = b2r2_blt_ioctl_us, + .poll = b2r2_blt_poll_us, + .read = b2r2_blt_read_us, +}; + + +/** + * b2r2_probe() - This routine loads the B2R2 core driver + * + * @pdev: platform device. + */ +static int b2r2_blt_probe(struct platform_device *pdev) +{ + int ret = 0; + int i; + + BUG_ON(pdev == NULL); + + dev_info(&pdev->dev, "%s start.\n", __func__); + + if (!b2r2_blt) { + b2r2_blt = kzalloc(sizeof(*b2r2_blt), GFP_KERNEL); + if (!b2r2_blt) { + dev_err(&pdev->dev, "b2r2_blt alloc failed\n"); + ret = -EINVAL; + goto error_exit; + } + + /* Init b2r2 core control reference counters */ + for (i = 0; i < B2R2_MAX_NBR_DEVICES; i++) + spin_lock_init(&b2r2_controls[i].lock); + } + + mutex_init(&b2r2_blt->datas_lock); + spin_lock_init(&b2r2_blt->lock); + b2r2_blt->dev = &pdev->dev; + + /* Register b2r2 driver */ + b2r2_blt->miscdev.parent = b2r2_blt->dev; + b2r2_blt->miscdev.minor = MISC_DYNAMIC_MINOR; + b2r2_blt->miscdev.name = "b2r2_blt"; + b2r2_blt->miscdev.fops = &b2r2_blt_fops; + + ret = misc_register(&b2r2_blt->miscdev); + if (ret != 0) { + printk(KERN_WARNING "%s: registering misc device fails\n", + __func__); + goto error_exit; + } + + b2r2_blt->dev = b2r2_blt->miscdev.this_device; + b2r2_blt->dev->coherent_dma_mask = 0xFFFFFFFF; + + kref_init(&blt_refcount); + + dev_info(&pdev->dev, "%s done.\n", __func__); + + return ret; + +/** Recover from any error if something fails */ +error_exit: + + kfree(b2r2_blt); + + dev_info(&pdev->dev, "%s done with errors (%d).\n", __func__, ret); + + return ret; +} + +/** + * b2r2_blt_remove - This routine unloads b2r2_blt driver + * + * @pdev: platform device. + */ +static int b2r2_blt_remove(struct platform_device *pdev) +{ + BUG_ON(pdev == NULL); + dev_info(&pdev->dev, "%s started.\n", __func__); + misc_deregister(&b2r2_blt->miscdev); + kref_put(&blt_refcount, b2r2_blt_release); + return 0; +} + +/** + * b2r2_blt_suspend() - This routine puts the B2R2 blitter in to sustend state. + * @pdev: platform device. + * + * This routine stores the current state of the b2r2 device and puts in to + * suspend state. + * + */ +int b2r2_blt_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +/** + * b2r2_blt_resume() - This routine resumes the B2R2 blitter from sustend state. + * @pdev: platform device. + * + * This routine restore back the current state of the b2r2 device resumes. + * + */ +int b2r2_blt_resume(struct platform_device *pdev) +{ + return 0; +} + +/** + * struct platform_b2r2_driver - Platform driver configuration for the + * B2R2 core driver + */ +static struct platform_driver platform_b2r2_blt_driver = { + .remove = b2r2_blt_remove, + .driver = { + .name = "b2r2_blt", + }, + .suspend = b2r2_blt_suspend, + .resume = b2r2_blt_resume, +}; + +/** + * b2r2_init() - Module init function for the B2R2 core module + */ +static int __init b2r2_blt_init(void) +{ + printk(KERN_INFO "%s\n", __func__); + return platform_driver_probe(&platform_b2r2_blt_driver, b2r2_blt_probe); +} +module_init(b2r2_blt_init); + +/** + * b2r2_exit() - Module exit function for the B2R2 core module + */ +static void __exit b2r2_blt_exit(void) +{ + printk(KERN_INFO "%s\n", __func__); + platform_driver_unregister(&platform_b2r2_blt_driver); + return; +} +module_exit(b2r2_blt_exit); + +MODULE_AUTHOR("Robert Fekete <robert.fekete@stericsson.com>"); +MODULE_DESCRIPTION("ST-Ericsson B2R2 Blitter module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/b2r2/b2r2_blt_main.c b/drivers/video/b2r2/b2r2_blt_main.c index 919de1ef0ee..3727f742bf1 100644 --- a/drivers/video/b2r2/b2r2_blt_main.c +++ b/drivers/video/b2r2/b2r2_blt_main.c @@ -36,6 +36,7 @@ #include <linux/hwmem.h> #include "b2r2_internal.h" +#include "b2r2_control.h" #include "b2r2_node_split.h" #include "b2r2_generic.h" #include "b2r2_mem_alloc.h" @@ -60,28 +61,11 @@ * Support read of many report records at once. */ -/** - * b2r2_blt_dev - Our device(s), /dev/b2r2_blt - */ -static struct b2r2_control *b2r2_ctl[B2R2_MAX_NBR_DEVICES]; - -/* Debug file system support */ -#ifdef CONFIG_DEBUG_FS -static int sprintf_req(struct b2r2_blt_request *request, char *buf, int size); -#endif - /* Local functions */ static void inc_stat(struct b2r2_control *cont, unsigned long *stat); static void dec_stat(struct b2r2_control *cont, unsigned long *stat); -static int b2r2_blt_synch(struct b2r2_blt_instance *instance, - int request_id); -static int b2r2_blt_query_cap(struct b2r2_blt_instance *instance, - struct b2r2_blt_query_cap *query_cap); #ifndef CONFIG_B2R2_GENERIC_ONLY -static int b2r2_blt(struct b2r2_blt_instance *instance, - struct b2r2_blt_request *request); - static void job_callback(struct b2r2_core_job *job); static void job_release(struct b2r2_core_job *job); static int job_acquire_resources(struct b2r2_core_job *job, bool atomic); @@ -89,9 +73,6 @@ static void job_release_resources(struct b2r2_core_job *job, bool atomic); #endif #ifdef CONFIG_B2R2_GENERIC -static int b2r2_generic_blt(struct b2r2_blt_instance *instance, - struct b2r2_blt_request *request); - static void job_callback_gen(struct b2r2_core_job *job); static void job_release_gen(struct b2r2_core_job *job); static int job_acquire_resources_gen(struct b2r2_core_job *job, bool atomic); @@ -109,8 +90,8 @@ static void unresolve_buf(struct b2r2_control *cont, static void sync_buf(struct b2r2_control *cont, struct b2r2_blt_img *img, struct b2r2_resolved_buf *resolved, bool is_dst, struct b2r2_blt_rect *rect); -static bool is_report_list_empty(struct b2r2_blt_instance *instance); -static bool is_synching(struct b2r2_blt_instance *instance); +static bool is_report_list_empty(struct b2r2_control_instance *instance); +static bool is_synching(struct b2r2_control_instance *instance); static void get_actual_dst_rect(struct b2r2_blt_req *req, struct b2r2_blt_rect *actual_dst_rect); static void set_up_hwmem_region(struct b2r2_control *cont, @@ -196,39 +177,18 @@ static void clean_l1_cache_range_all_cpus(struct sync_args *sa) * * A B2R2 BLT instance is created and stored in the file structure. */ -static int b2r2_blt_open(struct inode *inode, struct file *filp) +int b2r2_control_open(struct b2r2_control_instance *instance) { int ret = 0; - struct b2r2_blt_instance *instance; - struct b2r2_control *cont = filp->private_data; + struct b2r2_control *cont = instance->control; b2r2_log_info(cont->dev, "%s\n", __func__); - inc_stat(cont, &cont->stat_n_in_open); - /* Allocate and initialize the instance */ - instance = (struct b2r2_blt_instance *) - kmalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) { - b2r2_log_err(cont->dev, "%s: Failed to alloc\n", __func__); - goto instance_alloc_failed; - } - memset(instance, 0, sizeof(*instance)); INIT_LIST_HEAD(&instance->report_list); mutex_init(&instance->lock); init_waitqueue_head(&instance->report_list_waitq); init_waitqueue_head(&instance->synch_done_waitq); - instance->control = cont; - - /* - * Remember the instance so that we can retrieve it in - * other functions - */ - filp->private_data = instance; - goto out; - -instance_alloc_failed: -out: dec_stat(cont, &cont->stat_n_in_open); return ret; @@ -244,11 +204,9 @@ out: * All active jobs are finished or cancelled and allocated data * is released. */ -static int b2r2_blt_release(struct inode *inode, struct file *filp) +int b2r2_control_release(struct b2r2_control_instance *instance) { int ret; - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) filp->private_data; struct b2r2_control *cont = instance->control; b2r2_log_info(cont->dev, "%s\n", __func__); @@ -256,7 +214,7 @@ static int b2r2_blt_release(struct inode *inode, struct file *filp) inc_stat(cont, &cont->stat_n_in_release); /* Finish all outstanding requests */ - ret = b2r2_blt_synch(instance, 0); + ret = b2r2_control_synch(instance, 0); if (ret < 0) b2r2_log_warn(cont->dev, "%s: b2r2_blt_sync failed with %d\n", __func__, ret); @@ -301,296 +259,68 @@ static int b2r2_blt_release(struct inode *inode, struct file *filp) } mutex_unlock(&instance->lock); - /* Release our instance */ - kfree(instance); - dec_stat(cont, &cont->stat_n_in_release); return 0; } -/** - * b2r2_blt_ioctl - This routine implements b2r2_blt ioctl interface - * - * @file: file pointer. - * @cmd :ioctl command. - * @arg: input argument for ioctl. - * - * Returns 0 if OK else negative error code - */ -static long b2r2_blt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +size_t b2r2_control_read(struct b2r2_control_instance *instance, + struct b2r2_blt_request **request_out, bool block) { - int ret = 0; - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) file->private_data; + struct b2r2_blt_request *request = NULL; +#ifdef CONFIG_B2R2_DEBUG struct b2r2_control *cont = instance->control; - - /** Process actual ioctl */ - b2r2_log_info(cont->dev, "%s\n", __func__); - - /* Get the instance from the file structure */ - switch (cmd) { - case B2R2_BLT_IOC: { - /* This is the "blit" command */ - - /* arg is user pointer to struct b2r2_blt_request */ - struct b2r2_blt_request *request = - kmalloc(sizeof(*request), GFP_KERNEL); - if (!request) { - b2r2_log_err(cont->dev, "%s: Failed to alloc mem\n", - __func__); - return -ENOMEM; - } - - /* Initialize the structure */ - memset(request, 0, sizeof(*request)); - INIT_LIST_HEAD(&request->list); - request->instance = instance; - - /* - * The user request is a sub structure of the - * kernel request structure. - */ - - /* Get the user data */ - if (copy_from_user(&request->user_req, (void *)arg, - sizeof(request->user_req))) { - b2r2_log_err(cont->dev, "%s: copy_from_user failed\n", - __func__); - kfree(request); - return -EFAULT; - } - - if (!b2r2_validate_user_req(cont, &request->user_req)) { - kfree(request); - return -EINVAL; - } - - request->profile = is_profiler_registered_approx(); - - /* - * If the user specified a color look-up table, - * make a copy that the HW can use. - */ - if ((request->user_req.flags & - B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) != 0) { - request->clut = dma_alloc_coherent(cont->dev, - CLUT_SIZE, &(request->clut_phys_addr), - GFP_DMA | GFP_KERNEL); - if (request->clut == NULL) { - b2r2_log_err(cont->dev, "%s CLUT allocation " - "failed.\n", __func__); - kfree(request); - return -ENOMEM; - } - - if (copy_from_user(request->clut, - request->user_req.clut, CLUT_SIZE)) { - b2r2_log_err(cont->dev, "%s: CLUT " - "copy_from_user failed\n", - __func__); - dma_free_coherent(cont->dev, CLUT_SIZE, - request->clut, - request->clut_phys_addr); - request->clut = NULL; - request->clut_phys_addr = 0; - kfree(request); - return -EFAULT; - } - } - - /* Perform the blit */ - -#ifdef CONFIG_B2R2_GENERIC_ONLY - /* Use the generic path for all operations */ - ret = b2r2_generic_blt(instance, request); -#else - /* Use the optimized path */ - ret = b2r2_blt(instance, request); #endif -#ifdef CONFIG_B2R2_GENERIC_FALLBACK - /* Fall back to generic path if operation was not supported */ - if (ret == -ENOSYS) { - struct b2r2_blt_request *request_gen; - - if (request->user_req.flags & B2R2_BLT_FLAG_BG_BLEND) { - /* No support for BG BLEND in generic - * implementation yet */ - b2r2_log_warn(cont->dev, "%s: Unsupported: " - "Background blend in b2r2_generic_blt\n", - __func__); - return ret; - } - - b2r2_log_info(cont->dev, - "b2r2_blt=%d Going generic.\n", ret); - request_gen = kmalloc(sizeof(*request_gen), GFP_KERNEL); - if (!request_gen) { - b2r2_log_err(cont->dev, - "%s: Failed to alloc mem for " - "request_gen\n", __func__); - return -ENOMEM; - } - - /* Initialize the structure */ - memset(request_gen, 0, sizeof(*request_gen)); - INIT_LIST_HEAD(&request_gen->list); - request_gen->instance = instance; - - /* - * The user request is a sub structure of the - * kernel request structure. - */ - - /* Get the user data */ - if (copy_from_user(&request_gen->user_req, (void *)arg, - sizeof(request_gen->user_req))) { - b2r2_log_err(cont->dev, "%s: copy_from_user " - "failed\n", __func__); - kfree(request_gen); - return -EFAULT; - } + b2r2_log_info(cont->dev, "%s\n", __func__); - /* - * If the user specified a color look-up table, - * make a copy that the HW can use. - */ - if ((request_gen->user_req.flags & - B2R2_BLT_FLAG_CLUT_COLOR_CORRECTION) - != 0) { - request_gen->clut = dma_alloc_coherent( - cont->dev, CLUT_SIZE, - &(request_gen->clut_phys_addr), - GFP_DMA | GFP_KERNEL); - if (request_gen->clut == NULL) { - b2r2_log_err(cont->dev, "%s CLUT " - "allocation failed.\n", - __func__); - kfree(request_gen); - return -ENOMEM; - } - - if (copy_from_user(request_gen->clut, - request_gen->user_req.clut, - CLUT_SIZE)) { - b2r2_log_err(cont->dev, "%s: CLUT" - " copy_from_user failed\n", - __func__); - dma_free_coherent(cont->dev, CLUT_SIZE, - request_gen->clut, - request_gen->clut_phys_addr); - request_gen->clut = NULL; - request_gen->clut_phys_addr = 0; - kfree(request_gen); - return -EFAULT; - } - } + /* + * Loop and wait here until we have anything to return or + * until interrupted + */ + mutex_lock(&instance->lock); + while (list_empty(&instance->report_list)) { + mutex_unlock(&instance->lock); - request_gen->profile = is_profiler_registered_approx(); + /* Return if non blocking read */ + if (!block) + return -EAGAIN; - ret = b2r2_generic_blt(instance, request_gen); - b2r2_log_info(cont->dev, "\nb2r2_generic_blt=%d " - "Generic done.\n", ret); - } -#endif /* CONFIG_B2R2_GENERIC_FALLBACK */ + b2r2_log_info(cont->dev, "%s - Going to sleep\n", __func__); + if (wait_event_interruptible( + instance->report_list_waitq, + !is_report_list_empty(instance))) + /* signal: tell the fs layer to handle it */ + return -ERESTARTSYS; - break; + /* Otherwise loop, but first reaquire the lock */ + mutex_lock(&instance->lock); } - case B2R2_BLT_SYNCH_IOC: - /* arg is request_id */ - ret = b2r2_blt_synch(instance, (int) arg); - break; - - case B2R2_BLT_QUERY_CAP_IOC: - { - /* Arg is struct b2r2_blt_query_cap */ - struct b2r2_blt_query_cap query_cap; - - /* Get the user data */ - if (copy_from_user(&query_cap, (void *)arg, - sizeof(query_cap))) { - b2r2_log_err(cont->dev, "%s: copy_from_user failed\n", - __func__); - return -EFAULT; - } - - /* Fill in our capabilities */ - ret = b2r2_blt_query_cap(instance, &query_cap); - - /* Return data to user */ - if (copy_to_user((void *)arg, &query_cap, - sizeof(query_cap))) { - b2r2_log_err(cont->dev, "%s: copy_to_user failed\n", - __func__); - return -EFAULT; - } - break; - } + if (!list_empty(&instance->report_list)) + request = list_first_entry( + &instance->report_list, struct b2r2_blt_request, list); - default: - /* Unknown command */ - b2r2_log_err(cont->dev, "%s: Unknown cmd %d\n", __func__, cmd); - ret = -EINVAL; - break; + if (request) { + /* Remove from list to avoid reading twice */ + list_del_init(&request->list); + *request_out = request; } - - if (ret < 0) - b2r2_log_err(cont->dev, "EC %d OK!\n", -ret); - - return ret; -} - -/** - * b2r2_blt_poll - Support for user-space poll, select & epoll. - * Used for user-space callback - * - * @filp: File to poll on - * @wait: Poll table to wait on - * - * This function checks if there are anything to read - */ -static unsigned b2r2_blt_poll(struct file *filp, poll_table *wait) -{ - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) filp->private_data; - unsigned int mask = 0; -#ifdef CONFIG_B2R2_DEBUG - struct b2r2_control *cont = instance->control; -#endif - - b2r2_log_info(cont->dev, "%s\n", __func__); - - poll_wait(filp, &instance->report_list_waitq, wait); - mutex_lock(&instance->lock); - if (!list_empty(&instance->report_list)) - mask |= POLLIN | POLLRDNORM; mutex_unlock(&instance->lock); - return mask; + if (request) + return 1; + + /* No report returned */ + return 0; } -/** - * b2r2_blt_read - Read report data, user for user-space callback - * - * @filp: File pointer - * @buf: User space buffer - * @count: Number of bytes to read - * @f_pos: File position - * - * Returns number of bytes read or negative error code - */ -static ssize_t b2r2_blt_read(struct file *filp, char __user *buf, size_t count, - loff_t *f_pos) +size_t b2r2_control_read_id(struct b2r2_control_instance *instance, + struct b2r2_blt_request **request_out, bool block, + int request_id) { - int ret = 0; struct b2r2_blt_request *request = NULL; - struct b2r2_blt_report report; - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) filp->private_data; #ifdef CONFIG_B2R2_DEBUG struct b2r2_control *cont = instance->control; #endif @@ -598,17 +328,6 @@ static ssize_t b2r2_blt_read(struct file *filp, char __user *buf, size_t count, b2r2_log_info(cont->dev, "%s\n", __func__); /* - * We return only complete report records, one at a time. - * Might be more efficient to support read of many. - */ - count = (count / sizeof(struct b2r2_blt_report)) * - sizeof(struct b2r2_blt_report); - if (count > sizeof(struct b2r2_blt_report)) - count = sizeof(struct b2r2_blt_report); - if (count == 0) - return count; - - /* * Loop and wait here until we have anything to return or * until interrupted */ @@ -617,7 +336,7 @@ static ssize_t b2r2_blt_read(struct file *filp, char __user *buf, size_t count, mutex_unlock(&instance->lock); /* Return if non blocking read */ - if (filp->f_flags & O_NONBLOCK) + if (!block) return -EAGAIN; b2r2_log_info(cont->dev, "%s - Going to sleep\n", __func__); @@ -631,55 +350,28 @@ static ssize_t b2r2_blt_read(struct file *filp, char __user *buf, size_t count, mutex_lock(&instance->lock); } - if (!list_empty(&instance->report_list)) - request = list_first_entry( - &instance->report_list, struct b2r2_blt_request, list); + if (!list_empty(&instance->report_list)) { + struct b2r2_blt_request *pos; + list_for_each_entry(pos, &instance->report_list, list) { + if (pos->request_id) + request = pos; + } + } if (request) { /* Remove from list to avoid reading twice */ list_del_init(&request->list); - - report.request_id = request->request_id; - report.report1 = request->user_req.report1; - report.report2 = request->user_req.report2; - report.usec_elapsed = 0; /* TBD */ - - mutex_unlock(&instance->lock); - if (copy_to_user(buf, &report, sizeof(report))) - ret = -EFAULT; - mutex_lock(&instance->lock); - - if (ret < 0) { - /* copy to user failed, re-insert into list */ - list_add(&request->list, - &request->instance->report_list); - request = NULL; - } + *request_out = request; } mutex_unlock(&instance->lock); if (request) - /* - * Release matching the addref when the job was put into - * the report list - */ - b2r2_core_job_release(&request->job, __func__); + return 1; - return count; + /* No report returned */ + return 0; } -/** - * b2r2_blt_fops - File operations for b2r2_blt - */ -static const struct file_operations b2r2_blt_fops = { - .owner = THIS_MODULE, - .open = b2r2_blt_open, - .release = b2r2_blt_release, - .unlocked_ioctl = b2r2_blt_ioctl, - .poll = b2r2_blt_poll, - .read = b2r2_blt_read, -}; - #ifndef CONFIG_B2R2_GENERIC_ONLY /** * b2r2_blt - Implementation of the B2R2 blit request @@ -687,14 +379,14 @@ static const struct file_operations b2r2_blt_fops = { * @instance: The B2R2 BLT instance * @request; The request to perform */ -static int b2r2_blt(struct b2r2_blt_instance *instance, - struct b2r2_blt_request *request) +int b2r2_control_blt(struct b2r2_blt_request *request) { int ret = 0; struct b2r2_blt_rect actual_dst_rect; int request_id = 0; struct b2r2_node *last_node = request->first_node; int node_count; + struct b2r2_control_instance *instance = request->instance; struct b2r2_control *cont = instance->control; u32 thread_runtime_at_start = 0; @@ -918,6 +610,7 @@ static int b2r2_blt(struct b2r2_blt_instance *instance, last_node = last_node->next; request->job.tag = (int) instance; + request->job.data = (int) cont->data; request->job.prio = request->user_req.prio; request->job.first_node_address = request->first_node->physical_address; @@ -1010,33 +703,6 @@ static int b2r2_blt(struct b2r2_blt_instance *instance, instance->no_of_active_requests++; mutex_unlock(&instance->lock); - /* Wait for the job to be done if synchronous */ - if ((request->user_req.flags & B2R2_BLT_FLAG_ASYNCH) == 0) { - b2r2_log_info(cont->dev, "%s: Synchronous, waiting\n", - __func__); - - inc_stat(cont, &cont->stat_n_in_blt_wait); - - ret = b2r2_core_job_wait(&request->job); - - dec_stat(cont, &cont->stat_n_in_blt_wait); - - if (ret < 0 && ret != -ENOENT) - b2r2_log_warn(cont->dev, "%s: Failed to wait job," - " ret = %d\n", __func__, ret); - else - b2r2_log_info(cont->dev, "%s: Synchronous wait done\n", - __func__); - ret = 0; - } - - /* - * Release matching the addref in b2r2_core_job_add, - * the request must not be accessed after this call - */ - b2r2_core_job_release(&request->job, __func__); - dec_stat(cont, &cont->stat_n_in_blt); - return ret >= 0 ? request_id : ret; job_add_failed: @@ -1057,17 +723,52 @@ resolve_bg_buf_failed: &request->src_resolved); resolve_src_buf_failed: synch_interrupted: - job_release(&request->job); - dec_stat(cont, &cont->stat_n_jobs_released); if ((request->user_req.flags & B2R2_BLT_FLAG_DRY_RUN) == 0 || ret) b2r2_log_warn(cont->dev, "%s returns with error %d\n", __func__, ret); + job_release(&request->job); + dec_stat(cont, &cont->stat_n_jobs_released); dec_stat(cont, &cont->stat_n_in_blt); return ret; } +int b2r2_control_waitjob(struct b2r2_blt_request *request) +{ + int ret = 0; + struct b2r2_control_instance *instance = request->instance; + struct b2r2_control *cont = instance->control; + + /* Wait for the job to be done if synchronous */ + if ((request->user_req.flags & B2R2_BLT_FLAG_ASYNCH) == 0) { + b2r2_log_info(cont->dev, "%s: Synchronous, waiting\n", + __func__); + + inc_stat(cont, &cont->stat_n_in_blt_wait); + + ret = b2r2_core_job_wait(&request->job); + + dec_stat(cont, &cont->stat_n_in_blt_wait); + + if (ret < 0 && ret != -ENOENT) + b2r2_log_warn(cont->dev, "%s: Failed to wait job," + " ret = %d\n", __func__, ret); + else + b2r2_log_info(cont->dev, "%s: Synchronous wait done\n", + __func__); + } + + /* + * Release matching the addref in b2r2_core_job_add, + * the request must not be accessed after this call + */ + b2r2_core_job_release(&request->job, __func__); + dec_stat(cont, &cont->stat_n_in_blt); + + return ret; +} + /** * Called when job is done or cancelled * @@ -1075,9 +776,13 @@ synch_interrupted: */ static void job_callback(struct b2r2_core_job *job) { - struct b2r2_blt_request *request = - container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_blt_request *request = NULL; + struct b2r2_core *core = NULL; + struct b2r2_control *cont = NULL; + + request = container_of(job, struct b2r2_blt_request, job); + core = (struct b2r2_core *) job->data; + cont = core->control; if (cont->dev) b2r2_log_info(cont->dev, "%s\n", __func__); @@ -1098,6 +803,8 @@ static void job_callback(struct b2r2_core_job *job) /* Move to report list if the job shall be reported */ /* FIXME: Use a smaller struct? */ + /* TODO: In the case of kernel API call, feed an asynch task to the + * instance worker (kthread) instead of polling for a report */ mutex_lock(&request->instance->lock); if (request->user_req.flags & B2R2_BLT_FLAG_REPORT_WHEN_DONE) { /* Move job to report list */ @@ -1163,9 +870,13 @@ static void job_callback(struct b2r2_core_job *job) */ static void job_release(struct b2r2_core_job *job) { - struct b2r2_blt_request *request = - container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_blt_request *request = NULL; + struct b2r2_core *core = NULL; + struct b2r2_control *cont = NULL; + + request = container_of(job, struct b2r2_blt_request, job); + core = (struct b2r2_core *) job->data; + cont = core->control; inc_stat(cont, &cont->stat_n_jobs_released); @@ -1206,7 +917,8 @@ static int job_acquire_resources(struct b2r2_core_job *job, bool atomic) { struct b2r2_blt_request *request = container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_core *core = (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; int ret; int i; @@ -1280,7 +992,8 @@ static void job_release_resources(struct b2r2_core_job *job, bool atomic) { struct b2r2_blt_request *request = container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_core *core = (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; int i; b2r2_log_info(cont->dev, "%s\n", __func__); @@ -1323,9 +1036,9 @@ static void job_release_resources(struct b2r2_core_job *job, bool atomic) static void tile_job_callback_gen(struct b2r2_core_job *job) { #ifdef CONFIG_B2R2_DEBUG - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) job->tag; - struct b2r2_control *cont = instance->control; + struct b2r2_core *core = + (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; #endif b2r2_log_info(cont->dev, "%s\n", __func__); @@ -1355,7 +1068,8 @@ static void job_callback_gen(struct b2r2_core_job *job) { struct b2r2_blt_request *request = container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_core *core = (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; b2r2_log_info(cont->dev, "%s\n", __func__); @@ -1372,8 +1086,9 @@ static void job_callback_gen(struct b2r2_core_job *job) /* Move to report list if the job shall be reported */ /* FIXME: Use a smaller struct? */ + /* TODO: In the case of kernel API call, feed an asynch task to the + * instance worker (kthread) instead of polling for a report */ mutex_lock(&request->instance->lock); - if (request->user_req.flags & B2R2_BLT_FLAG_REPORT_WHEN_DONE) { /* Move job to report list */ list_add_tail(&request->list, @@ -1398,7 +1113,7 @@ static void job_callback_gen(struct b2r2_core_job *job) BUG_ON(request->instance->no_of_active_requests == 0); request->instance->no_of_active_requests--; if (request->instance->synching && - request->instance->no_of_active_requests == 0) { + request->instance->no_of_active_requests == 0) { request->instance->synching = false; /* Wake up all syncing */ @@ -1438,9 +1153,9 @@ static void job_callback_gen(struct b2r2_core_job *job) static void tile_job_release_gen(struct b2r2_core_job *job) { - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) job->tag; - struct b2r2_control *cont = instance->control; + struct b2r2_core *core = + (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; inc_stat(cont, &cont->stat_n_jobs_released); @@ -1462,7 +1177,8 @@ static void job_release_gen(struct b2r2_core_job *job) { struct b2r2_blt_request *request = container_of(job, struct b2r2_blt_request, job); - struct b2r2_control *cont = request->instance->control; + struct b2r2_core *core = (struct b2r2_core *) job->data; + struct b2r2_control *cont = core->control; inc_stat(cont, &cont->stat_n_jobs_released); @@ -1503,11 +1219,9 @@ static void job_release_resources_gen(struct b2r2_core_job *job, bool atomic) /** * b2r2_generic_blt - Generic implementation of the B2R2 blit request * - * @instance: The B2R2 BLT instance * @request; The request to perform */ -static int b2r2_generic_blt(struct b2r2_blt_instance *instance, - struct b2r2_blt_request *request) +int b2r2_generic_blt(struct b2r2_blt_request *request) { int ret = 0; struct b2r2_blt_rect actual_dst_rect; @@ -1527,6 +1241,7 @@ static int b2r2_generic_blt(struct b2r2_blt_instance *instance, struct b2r2_work_buf work_bufs[4]; struct b2r2_blt_rect dst_rect_tile; int i; + struct b2r2_control_instance *instance = request->instance; struct b2r2_control *cont = instance->control; u32 thread_runtime_at_start = 0; @@ -1754,6 +1469,7 @@ static int b2r2_generic_blt(struct b2r2_blt_instance *instance, last_node = last_node->next; request->job.tag = (int) instance; + request->job.data = (int) cont->data; request->job.prio = request->user_req.prio; request->job.first_node_address = request->first_node->physical_address; @@ -1860,7 +1576,9 @@ static int b2r2_generic_blt(struct b2r2_blt_instance *instance, "(%d, %d)\n", __func__, x, y); continue; } + tile_job->job_id = request->job.job_id; tile_job->tag = request->job.tag; + tile_job->data = request->job.data; tile_job->prio = request->job.prio; tile_job->first_node_address = request->job.first_node_address; @@ -1976,7 +1694,9 @@ static int b2r2_generic_blt(struct b2r2_blt_instance *instance, "(%d, %d)\n", __func__, x, y); continue; } + tile_job->job_id = request->job.job_id; tile_job->tag = request->job.tag; + tile_job->data = request->job.data; tile_job->prio = request->job.prio; tile_job->first_node_address = request->job.first_node_address; @@ -2177,7 +1897,7 @@ zero_blt: * @request_id: If 0, wait for all requests on this instance to finish. * Else wait for request with given request id to finish. */ -static int b2r2_blt_synch(struct b2r2_blt_instance *instance, +int b2r2_control_synch(struct b2r2_control_instance *instance, int request_id) { int ret = 0; @@ -2223,19 +1943,6 @@ static int b2r2_blt_synch(struct b2r2_blt_instance *instance, return ret; } -/** - * Query B2R2 capabilities - * - * @instance: The B2R2 BLT instance - * @query_cap: The structure receiving the capabilities - */ -static int b2r2_blt_query_cap(struct b2r2_blt_instance *instance, - struct b2r2_blt_query_cap *query_cap) -{ - /* FIXME: Not implemented yet */ - return -ENOSYS; -} - static void get_actual_dst_rect(struct b2r2_blt_req *req, struct b2r2_blt_rect *actual_dst_rect) { @@ -2261,12 +1968,12 @@ static void set_up_hwmem_region(struct b2r2_control *cont, if (b2r2_is_zero_area_rect(rect)) return; - img_size = b2r2_get_img_size(cont, img); + img_size = b2r2_get_img_size(cont->dev, img); if (b2r2_is_single_plane_fmt(img->fmt) && b2r2_is_independent_pixel_fmt(img->fmt)) { - int img_fmt_bpp = b2r2_get_fmt_bpp(cont, img->fmt); - u32 img_pitch = b2r2_get_img_pitch(cont, img); + int img_fmt_bpp = b2r2_get_fmt_bpp(cont->dev, img->fmt); + u32 img_pitch = b2r2_get_img_pitch(cont->dev, img); region->offset = (u32)(img->buf.offset + (rect->y * img_pitch)); @@ -2319,8 +2026,9 @@ static int resolve_hwmem(struct b2r2_control *cont, required_access = (is_dst ? HWMEM_ACCESS_WRITE : HWMEM_ACCESS_READ) | HWMEM_ACCESS_IMPORT; if ((required_access & access) != required_access) { - b2r2_log_info(cont->dev, "%s: Insufficient access to hwmem " - "buffer.\n", __func__); + b2r2_log_info(cont->dev, + "%s: Insufficient access to hwmem (%d, requires %d)" + "buffer.\n", __func__, access, required_access); return_value = -EACCES; goto access_check_failed; } @@ -2333,11 +2041,12 @@ static int resolve_hwmem(struct b2r2_control *cont, } if (resolved_buf->file_len < - img->buf.offset + (__u32)b2r2_get_img_size(cont, img)) { + img->buf.offset + + (__u32)b2r2_get_img_size(cont->dev, img)) { b2r2_log_info(cont->dev, "%s: Hwmem buffer too small. (%d < " "%d)\n", __func__, resolved_buf->file_len, img->buf.offset + - (__u32)b2r2_get_img_size(cont, img)); + (__u32)b2r2_get_img_size(cont->dev, img)); return_value = -EINVAL; goto size_check_failed; } @@ -2714,7 +2423,7 @@ static void sync_buf(struct b2r2_control *cont, * * @instance: The B2R2 BLT instance */ -static bool is_report_list_empty(struct b2r2_blt_instance *instance) +static bool is_report_list_empty(struct b2r2_control_instance *instance) { bool is_empty; @@ -2730,7 +2439,7 @@ static bool is_report_list_empty(struct b2r2_blt_instance *instance) * * @instance: The B2R2 BLT instance */ -static bool is_synching(struct b2r2_blt_instance *instance) +static bool is_synching(struct b2r2_control_instance *instance) { bool is_synching; @@ -2768,213 +2477,6 @@ static void dec_stat(struct b2r2_control *cont, unsigned long *stat) #ifdef CONFIG_DEBUG_FS /** - * sprintf_req() - Builds a string representing the request, for debug - * - * @request:Request that should be encoded into a string - * @buf: Receiving buffer - * @size: Size of receiving buffer - * - * Returns number of characters in string, excluding null terminator - */ -static int sprintf_req(struct b2r2_blt_request *request, char *buf, int size) -{ - size_t dev_size = 0; - - /* generic request info */ - dev_size += sprintf(buf + dev_size, - "instance : 0x%08lX\n", - (unsigned long) request->instance); - dev_size += sprintf(buf + dev_size, - "size : %d bytes\n", request->user_req.size); - dev_size += sprintf(buf + dev_size, - "flags : 0x%08lX\n", - (unsigned long) request->user_req.flags); - dev_size += sprintf(buf + dev_size, - "transform : %d\n", - (int) request->user_req.transform); - dev_size += sprintf(buf + dev_size, - "prio : %d\n", request->user_req.transform); - dev_size += sprintf(buf + dev_size, - "global_alpha : %d\n", - (int) request->user_req.global_alpha); - dev_size += sprintf(buf + dev_size, - "report1 : 0x%08lX\n", - (unsigned long) request->user_req.report1); - dev_size += sprintf(buf + dev_size, - "report2 : 0x%08lX\n", - (unsigned long) request->user_req.report2); - dev_size += sprintf(buf + dev_size, - "request_id : 0x%08lX\n\n", - (unsigned long) request->request_id); - - /* src info */ - dev_size += sprintf(buf + dev_size, - "src_img.fmt : %#010x\n", - request->user_req.src_img.fmt); - dev_size += sprintf(buf + dev_size, - "src_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d, " - "offset=%d, len=%d}\n", - request->user_req.src_img.buf.type, - request->user_req.src_img.buf.hwmem_buf_name, - request->user_req.src_img.buf.fd, - request->user_req.src_img.buf.offset, - request->user_req.src_img.buf.len); - dev_size += sprintf(buf + dev_size, - "src_img : {width=%d, height=%d, pitch=%d}\n", - request->user_req.src_img.width, - request->user_req.src_img.height, - request->user_req.src_img.pitch); - dev_size += sprintf(buf + dev_size, - "src_mask.fmt : %#010x\n", - request->user_req.src_mask.fmt); - dev_size += sprintf(buf + dev_size, - "src_mask.buf : {type=%d, hwmem_buf_name=%d, fd=%d," - " offset=%d, len=%d}\n", - request->user_req.src_mask.buf.type, - request->user_req.src_mask.buf.hwmem_buf_name, - request->user_req.src_mask.buf.fd, - request->user_req.src_mask.buf.offset, - request->user_req.src_mask.buf.len); - dev_size += sprintf(buf + dev_size, - "src_mask : {width=%d, height=%d, pitch=%d}\n", - request->user_req.src_mask.width, - request->user_req.src_mask.height, - request->user_req.src_mask.pitch); - dev_size += sprintf(buf + dev_size, - "src_rect : {x=%d, y=%d, width=%d, height=%d}\n", - request->user_req.src_rect.x, - request->user_req.src_rect.y, - request->user_req.src_rect.width, - request->user_req.src_rect.height); - dev_size += sprintf(buf + dev_size, - "src_color : 0x%08lX\n\n", - (unsigned long) request->user_req.src_color); - - /* bg info */ - dev_size += sprintf(buf + dev_size, - "bg_img.fmt : %#010x\n", - request->user_req.bg_img.fmt); - dev_size += sprintf(buf + dev_size, - "bg_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d," - " offset=%d, len=%d}\n", - request->user_req.bg_img.buf.type, - request->user_req.bg_img.buf.hwmem_buf_name, - request->user_req.bg_img.buf.fd, - request->user_req.bg_img.buf.offset, - request->user_req.bg_img.buf.len); - dev_size += sprintf(buf + dev_size, - "bg_img : {width=%d, height=%d, pitch=%d}\n", - request->user_req.bg_img.width, - request->user_req.bg_img.height, - request->user_req.bg_img.pitch); - dev_size += sprintf(buf + dev_size, - "bg_rect : {x=%d, y=%d, width=%d, height=%d}\n\n", - request->user_req.bg_rect.x, - request->user_req.bg_rect.y, - request->user_req.bg_rect.width, - request->user_req.bg_rect.height); - - /* dst info */ - dev_size += sprintf(buf + dev_size, - "dst_img.fmt : %#010x\n", - request->user_req.dst_img.fmt); - dev_size += sprintf(buf + dev_size, - "dst_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d," - " offset=%d, len=%d}\n", - request->user_req.dst_img.buf.type, - request->user_req.dst_img.buf.hwmem_buf_name, - request->user_req.dst_img.buf.fd, - request->user_req.dst_img.buf.offset, - request->user_req.dst_img.buf.len); - dev_size += sprintf(buf + dev_size, - "dst_img : {width=%d, height=%d, pitch=%d}\n", - request->user_req.dst_img.width, - request->user_req.dst_img.height, - request->user_req.dst_img.pitch); - dev_size += sprintf(buf + dev_size, - "dst_rect : {x=%d, y=%d, width=%d, height=%d}\n", - request->user_req.dst_rect.x, - request->user_req.dst_rect.y, - request->user_req.dst_rect.width, - request->user_req.dst_rect.height); - dev_size += sprintf(buf + dev_size, - "dst_clip_rect : {x=%d, y=%d, width=%d, height=%d}\n", - request->user_req.dst_clip_rect.x, - request->user_req.dst_clip_rect.y, - request->user_req.dst_clip_rect.width, - request->user_req.dst_clip_rect.height); - dev_size += sprintf(buf + dev_size, - "dst_color : 0x%08lX\n\n", - (unsigned long) request->user_req.dst_color); - - dev_size += sprintf(buf + dev_size, - "src_resolved.physical : 0x%08lX\n", - (unsigned long) request->src_resolved. - physical_address); - dev_size += sprintf(buf + dev_size, - "src_resolved.virtual : 0x%08lX\n", - (unsigned long) request->src_resolved.virtual_address); - dev_size += sprintf(buf + dev_size, - "src_resolved.filep : 0x%08lX\n", - (unsigned long) request->src_resolved.filep); - dev_size += sprintf(buf + dev_size, - "src_resolved.filep_physical_start : 0x%08lX\n", - (unsigned long) request->src_resolved. - file_physical_start); - dev_size += sprintf(buf + dev_size, - "src_resolved.filep_virtual_start : 0x%08lX\n", - (unsigned long) request->src_resolved.file_virtual_start); - dev_size += sprintf(buf + dev_size, - "src_resolved.file_len : %d\n\n", - request->src_resolved.file_len); - - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.physical : 0x%08lX\n", - (unsigned long) request->src_mask_resolved. - physical_address); - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.virtual : 0x%08lX\n", - (unsigned long) request->src_mask_resolved.virtual_address); - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.filep : 0x%08lX\n", - (unsigned long) request->src_mask_resolved.filep); - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.filep_physical_start : 0x%08lX\n", - (unsigned long) request->src_mask_resolved. - file_physical_start); - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.filep_virtual_start : 0x%08lX\n", - (unsigned long) request->src_mask_resolved. - file_virtual_start); - dev_size += sprintf(buf + dev_size, - "src_mask_resolved.file_len : %d\n\n", - request->src_mask_resolved.file_len); - - dev_size += sprintf(buf + dev_size, - "dst_resolved.physical : 0x%08lX\n", - (unsigned long) request->dst_resolved. - physical_address); - dev_size += sprintf(buf + dev_size, - "dst_resolved.virtual : 0x%08lX\n", - (unsigned long) request->dst_resolved.virtual_address); - dev_size += sprintf(buf + dev_size, - "dst_resolved.filep : 0x%08lX\n", - (unsigned long) request->dst_resolved.filep); - dev_size += sprintf(buf + dev_size, - "dst_resolved.filep_physical_start : 0x%08lX\n", - (unsigned long) request->dst_resolved. - file_physical_start); - dev_size += sprintf(buf + dev_size, - "dst_resolved.filep_virtual_start : 0x%08lX\n", - (unsigned long) request->dst_resolved.file_virtual_start); - dev_size += sprintf(buf + dev_size, - "dst_resolved.file_len : %d\n\n", - request->dst_resolved.file_len); - - return dev_size; -} - -/** * debugfs_b2r2_blt_request_read() - Implements debugfs read for B2R2 register * * @filp: File pointer @@ -3246,27 +2748,12 @@ static void destroy_tmp_bufs(struct b2r2_control *cont) * * Returns 0 if OK else negative error code */ -int b2r2_blt_module_init(struct b2r2_control *cont) +int b2r2_control_init(struct b2r2_control *cont) { int ret; mutex_init(&cont->stat_lock); - /* Register b2r2 driver */ - cont->miscdev.minor = MISC_DYNAMIC_MINOR; - cont->miscdev.name = cont->name; - cont->miscdev.fops = &b2r2_blt_fops; - - ret = misc_register(&cont->miscdev); - if (ret) { - printk(KERN_WARNING "%s: registering misc device fails\n", - __func__); - goto b2r2_misc_register_fail; - } - - cont->dev = cont->miscdev.this_device; - dev_set_drvdata(cont->dev, cont); - #ifdef CONFIG_B2R2_GENERIC /* Initialize generic path */ b2r2_generic_init(cont); @@ -3280,11 +2767,6 @@ int b2r2_blt_module_init(struct b2r2_control *cont) b2r2_log_info(cont->dev, "%s: device registered\n", __func__); - /* - * FIXME: This stuff should be done before the first requests i.e. - * before misc_register, but they need the device which is not - * available until after misc_register. - */ cont->dev->coherent_dma_mask = 0xFFFFFFFF; init_tmp_bufs(cont); ret = b2r2_filters_init(cont); @@ -3315,7 +2797,6 @@ int b2r2_blt_module_init(struct b2r2_control *cont) } #endif - b2r2_ctl[cont->id] = cont; b2r2_log_info(cont->dev, "%s: done\n", __func__); return ret; @@ -3328,15 +2809,13 @@ b2r2_node_split_init_fail: #ifdef CONFIG_B2R2_GENERIC b2r2_generic_exit(cont); #endif - misc_deregister(&cont->miscdev); -b2r2_misc_register_fail: return ret; } /** - * b2r2_module_exit() - Module exit function + * b2r2_control_exit() - Module exit function */ -void b2r2_blt_module_exit(struct b2r2_control *cont) +void b2r2_control_exit(struct b2r2_control *cont) { if (cont) { b2r2_log_info(cont->dev, "%s\n", __func__); @@ -3348,8 +2827,6 @@ void b2r2_blt_module_exit(struct b2r2_control *cont) #endif b2r2_mem_exit(cont); destroy_tmp_bufs(cont); - b2r2_ctl[cont->id] = NULL; - misc_deregister(&cont->miscdev); b2r2_node_split_exit(cont); #if defined(CONFIG_B2R2_GENERIC) b2r2_generic_exit(cont); diff --git a/drivers/video/b2r2/b2r2_control.h b/drivers/video/b2r2/b2r2_control.h new file mode 100644 index 00000000000..d13d2188618 --- /dev/null +++ b/drivers/video/b2r2/b2r2_control.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * ST-Ericsson B2R2 internal definitions + * + * Author: Jorgen Nilsson <jorgen.nilsson@stericsson.com> + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef _LINUX_DRIVERS_VIDEO_B2R2_CONTROL_H_ +#define _LINUX_DRIVERS_VIDEO_B2R2_CONTROL_H_ + +#include "b2r2_internal.h" + +int b2r2_control_init(struct b2r2_control *cont); +void b2r2_control_exit(struct b2r2_control *cont); +int b2r2_control_open(struct b2r2_control_instance *instance); +int b2r2_control_release(struct b2r2_control_instance *instance); + +int b2r2_control_blt(struct b2r2_blt_request *request); +int b2r2_generic_blt(struct b2r2_blt_request *request); +int b2r2_control_waitjob(struct b2r2_blt_request *request); +int b2r2_control_synch(struct b2r2_control_instance *instance, + int request_id); +size_t b2r2_control_read(struct b2r2_control_instance *instance, + struct b2r2_blt_request **request_out, bool block); +size_t b2r2_control_read_id(struct b2r2_control_instance *instance, + struct b2r2_blt_request **request_out, bool block, + int request_id); + +#endif diff --git a/drivers/video/b2r2/b2r2_core.c b/drivers/video/b2r2/b2r2_core.c index ed5f52598da..02071d5f989 100644 --- a/drivers/video/b2r2/b2r2_core.c +++ b/drivers/video/b2r2/b2r2_core.c @@ -48,73 +48,18 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/kref.h> #include "b2r2_internal.h" #include "b2r2_core.h" #include "b2r2_global.h" #include "b2r2_structures.h" -#include "b2r2_internal.h" +#include "b2r2_control.h" #include "b2r2_profiler_api.h" #include "b2r2_timing.h" #include "b2r2_debug.h" /** - * B2R2_DRIVER_TIMEOUT_VALUE - Busy loop timeout after soft reset - */ -#define B2R2_DRIVER_TIMEOUT_VALUE (1500) - -/** - * B2R2_CLK_FLAG - Value to write into clock reg to turn clock on - */ -#define B2R2_CLK_FLAG (0x125) - -/** - * DEBUG_CHECK_ADDREF_RELEASE - Define this to enable addref / release debug - */ -#define DEBUG_CHECK_ADDREF_RELEASE 1 - -#ifdef CONFIG_DEBUG_FS -/** - * HANDLE_TIMEOUTED_JOBS - Define this to check jobs for timeout and cancel them - */ -#define HANDLE_TIMEOUTED_JOBS 1 -#endif - -/** - * B2R2_CLOCK_ALWAYS_ON - Define this to disable power save clock turn off - */ -/* #define B2R2_CLOCK_ALWAYS_ON 1 */ - -/** - * START_SENTINEL - Watch guard to detect job overwrites - */ -#define START_SENTINEL 0xBABEDEEA - -/** - * STOP_SENTINEL - Watch guard to detect job overwrites - */ -#define END_SENTINEL 0xDADBDCDD - -/** - * B2R2_CORE_LOWEST_PRIO - Lowest prio allowed - */ -#define B2R2_CORE_LOWEST_PRIO -19 -/** - * B2R2_CORE_HIGHEST_PRIO - Highest prio allowed - */ -#define B2R2_CORE_HIGHEST_PRIO 20 - -/** - * B2R2_DOMAIN_DISABLE - - */ -#define B2R2_DOMAIN_DISABLE_TIMEOUT (HZ/100) - -/** - * B2R2_REGULATOR_RETRY_COUNT - - */ -#define B2R2_REGULATOR_RETRY_COUNT 10 - -/** * B2R2 Hardware defines below */ @@ -179,134 +124,6 @@ #define B2R2BLT_STA1AccessType (INITIAL_TEST) #define B2R2BLT_STA1 (0xa08) - -#ifdef DEBUG_CHECK_ADDREF_RELEASE - -/** - * struct addref_release - Represents one addref or release. Used - * to debug addref / release problems - * - * @addref: true if this represents an addref else it represents - * a release. - * @job: The job that was referenced - * @caller: The caller of the addref or release - * @ref_count: The job reference count after addref / release - */ -struct addref_release { - bool addref; - struct b2r2_core_job *job; - const char *caller; - int ref_count; -}; - -#endif - -/** - * struct b2r2_core - Administration data for B2R2 core - * - * @lock: Spin lock protecting the b2r2_core structure and the B2R2 HW - * @hw: B2R2 registers memory mapped - * @pmu_b2r2_clock: Control of B2R2 clock - * @log_dev: Device used for logging via dev_... functions - * - * @prio_queue: Queue of jobs sorted in priority order - * @active_jobs: Array containing pointer to zero or one job per queue - * @n_active_jobs: Number of active jobs - * @jiffies_last_active: jiffie value when adding last active job - * @jiffies_last_irq: jiffie value when last irq occured - * @timeout_work: Work structure for timeout work - * - * @next_job_id: Contains the job id that will be assigned to the next - * added job. - * - * @clock_request_count: When non-zero, clock is on - * @clock_off_timer: Kernel timer to handle delayed turn off of clock - * - * @work_queue: Work queue to handle done jobs (callbacks) and timeouts in - * non-interrupt context. - * - * @stat_n_irq: Number of interrupts (statistics) - * @stat_n_jobs_added: Number of jobs added (statistics) - * @stat_n_jobs_removed: Number of jobs removed (statistics) - * @stat_n_jobs_in_prio_list: Number of jobs in prio list (statistics) - * - * @debugfs_root_dir: Root directory for B2R2 debugfs - * - * @ar: Circular array of addref / release debug structs - * @ar_write: Where next write will occur - * @ar_read: First valid place to read. When ar_read == ar_write then - * the array is empty. - */ -struct b2r2_core { - spinlock_t lock; - - struct b2r2_memory_map *hw; - - u8 op_size; - u8 ch_size; - u8 pg_size; - u8 mg_size; - u16 min_req_time; - int irq; - - char name[16]; - struct device *dev; - - struct list_head prio_queue; - - struct b2r2_core_job *active_jobs[B2R2_CORE_QUEUE_NO_OF]; - unsigned long n_active_jobs; - - unsigned long jiffies_last_active; - unsigned long jiffies_last_irq; -#ifdef HANDLE_TIMEOUTED_JOBS - struct delayed_work timeout_work; -#endif - int next_job_id; - - unsigned long clock_request_count; - struct timer_list clock_off_timer; - - struct workqueue_struct *work_queue; - - /* Statistics */ - unsigned long stat_n_irq; - unsigned long stat_n_jobs_added; - unsigned long stat_n_jobs_removed; - - unsigned long stat_n_jobs_in_prio_list; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_root_dir; - struct dentry *debugfs_core_root_dir; - struct dentry *debugfs_regs_dir; -#endif - -#ifdef DEBUG_CHECK_ADDREF_RELEASE - /* Tracking release bug...*/ - struct addref_release ar[100]; - int ar_write; - int ar_read; -#endif - - /* Power management variables */ - struct mutex domain_lock; - struct delayed_work domain_disable_work; - - /* - * We need to keep track of both the number of domain_enable/disable() - * calls and whether the power was actually turned off, since the - * power off is done in a delayed job. - */ - bool domain_enabled; - int domain_request_count; - - struct clk *b2r2_clock; - struct regulator *b2r2_reg; - - struct b2r2_control *control; -}; - /** * b2r2_core - Quick link to administration data for B2R2 */ @@ -318,7 +135,6 @@ static void clear_interrupts(struct b2r2_core *core); static void trigger_job(struct b2r2_core *core, struct b2r2_core_job *job); static void exit_job_list(struct b2r2_core *core, struct list_head *job_list); -static int get_next_job_id(struct b2r2_core *core); static void job_work_function(struct work_struct *ptr); static void init_job(struct b2r2_core_job *job); static void insert_into_prio_list(struct b2r2_core *core, @@ -439,19 +255,21 @@ static void internal_job_addref(struct b2r2_core *core, { u32 ref_count; - b2r2_log_info(core->dev, "%s (%p, %p) (from %s)\n", - __func__, core, job, caller); - /* Sanity checks */ + BUG_ON(core == NULL); BUG_ON(job == NULL); + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (from %s)\n", + __func__, core, job, caller); + + if (job->start_sentinel != START_SENTINEL || job->end_sentinel != END_SENTINEL || job->ref_count == 0 || job->ref_count > 10) { - b2r2_log_info(core->dev, "%s: (%p, %p) start=%X end=%X " - "ref_count=%d\n", __func__, core, job, - job->start_sentinel, job->end_sentinel, - job->ref_count); + b2r2_log_info(core->dev, "%s: (core: %p, job: %p) " + "start=%X end=%X ref_count=%d\n", + __func__, core, job, job->start_sentinel, + job->end_sentinel, job->ref_count); /* Something is wrong, print the addref / release array */ #ifdef DEBUG_CHECK_ADDREF_RELEASE @@ -471,8 +289,8 @@ static void internal_job_addref(struct b2r2_core *core, ar_add(core, job, caller, true); #endif - b2r2_log_info(core->dev, "%s called from %s (%p, %p): Ref Count is " - "%d\n", __func__, caller, core, job, job->ref_count); + b2r2_log_info(core->dev, "%s called from %s (core: %p, job: %p): Ref " + "Count is %d\n", __func__, caller, core, job, job->ref_count); } /** @@ -496,14 +314,14 @@ static bool internal_job_release(struct b2r2_core *core, /* Sanity checks */ BUG_ON(job == NULL); - b2r2_log_info(core->dev, "%s (%p, %p) (from %s)\n", + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (from %s)\n", __func__, core, job, caller); if (job->start_sentinel != START_SENTINEL || job->end_sentinel != END_SENTINEL || job->ref_count == 0 || job->ref_count > 10) { - b2r2_log_info(core->dev, "%s: (%p, %p) start=%X end=%X " - "ref_count=%d\n", __func__, core, job, + b2r2_log_info(core->dev, "%s: (core: %p, job: %p) start=%X " + "end=%X ref_count=%d\n", __func__, core, job, job->start_sentinel, job->end_sentinel, job->ref_count); @@ -521,8 +339,8 @@ static bool internal_job_release(struct b2r2_core *core, #ifdef DEBUG_CHECK_ADDREF_RELEASE ar_add(core, job, caller, false); #endif - b2r2_log_info(core->dev, "%s called from %s (%p, %p) Ref Count is " - "%d\n", __func__, caller, core, job, ref_count); + b2r2_log_info(core->dev, "%s called from %s (core: %p, job: %p) " + "Ref Count is %d\n", __func__, caller, core, job, ref_count); if (!ref_count && job->release) { call_release = true; @@ -543,11 +361,10 @@ static bool internal_job_release(struct b2r2_core *core, void b2r2_core_job_addref(struct b2r2_core_job *job, const char *caller) { unsigned long flags; - struct b2r2_blt_instance *instance; struct b2r2_core *core; - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + BUG_ON(job == NULL || job->data == 0); + core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); internal_job_addref(core, job, caller); @@ -561,11 +378,10 @@ void b2r2_core_job_release(struct b2r2_core_job *job, const char *caller) { unsigned long flags; bool call_release = false; - struct b2r2_blt_instance *instance; struct b2r2_core *core; - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + BUG_ON(job == NULL || job->data == 0); + core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); call_release = internal_job_release(core, job, caller); @@ -584,12 +400,19 @@ int b2r2_core_job_add(struct b2r2_control *control, unsigned long flags; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %p)\n", __func__, control, job); + b2r2_log_info(core->dev, "%s (core: %p, job: %p)\n", + __func__, core, job); /* Enable B2R2 */ domain_enable(core); spin_lock_irqsave(&core->lock, flags); + /* Check that we have not been powered down */ + if (!core->domain_enabled) { + spin_unlock_irqrestore(&core->lock, flags); + return -ENOSYS; + } + core->stat_n_jobs_added++; /* Initialise internal job data */ @@ -605,7 +428,7 @@ int b2r2_core_job_add(struct b2r2_control *control, check_prio_list(core, false); spin_unlock_irqrestore(&core->lock, flags); - return 0; + return job->job_id; } /** @@ -618,7 +441,8 @@ struct b2r2_core_job *b2r2_core_job_find(struct b2r2_control *control, struct b2r2_core_job *job; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %d)\n", __func__, control, job_id); + b2r2_log_info(core->dev, "%s (core: %p, job_id: %d)\n", + __func__, core, job_id); spin_lock_irqsave(&core->lock, flags); /* Look through prio queue */ @@ -642,7 +466,8 @@ struct b2r2_core_job *b2r2_core_job_find_first_with_tag( struct b2r2_core_job *job; struct b2r2_core *core = control->data; - b2r2_log_info(core->dev, "%s (%p, %d)\n", __func__, control, tag); + b2r2_log_info(core->dev, + "%s (core: %p, tag: %d)\n", __func__, core, tag); spin_lock_irqsave(&core->lock, flags); /* Look through prio queue */ @@ -669,11 +494,7 @@ static bool is_job_done(struct b2r2_core_job *job) { unsigned long flags; bool job_is_done; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; spin_lock_irqsave(&core->lock, flags); job_is_done = @@ -694,13 +515,12 @@ static bool is_job_done(struct b2r2_core_job *job) int b2r2_core_job_wait(struct b2r2_core_job *job) { int ret = 0; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; +#ifdef CONFIG_B2R2_DEBUG + struct b2r2_core *core = (struct b2r2_core *) job->data; +#endif - b2r2_log_info(core->dev, "%s (%p)\n", __func__, job); + b2r2_log_info(core->dev, "%s (core: %p, job: %p)\n", + __func__, core, job); /* Check that we have the job */ if (job->job_state == B2R2_CORE_JOB_IDLE) { /* Never or not queued */ @@ -779,14 +599,10 @@ int b2r2_core_job_cancel(struct b2r2_core_job *job) { unsigned long flags; int ret = 0; - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; - b2r2_log_info(core->dev, "%s (%p) (%d)\n", - __func__, job, job->job_state); + b2r2_log_info(core->dev, "%s (core: %p, job: %p) (st: %d)\n", + __func__, core, job, job->job_state); /* Check that we have the job */ if (job->job_state == B2R2_CORE_JOB_IDLE) { /* Never or not queued */ @@ -819,6 +635,7 @@ static void domain_disable_work_function(struct work_struct *work) return; if (core->domain_request_count == 0) { + core->valid = false; exit_hw(core); clk_disable(core->b2r2_clock); regulator_disable(core->b2r2_reg); @@ -853,10 +670,16 @@ again: else if (ret < 0) goto regulator_enable_failed; - clk_enable(core->b2r2_clock); + ret = clk_enable(core->b2r2_clock); + if (ret < 0) { + b2r2_log_err(core->dev, + "%s: Could not enable clock\n", __func__); + goto enable_clk_failed; + } if (init_hw(core) < 0) goto init_hw_failed; core->domain_enabled = true; + core->valid = true; } mutex_unlock(&core->domain_lock); @@ -866,9 +689,9 @@ again: init_hw_failed: b2r2_log_err(core->dev, "%s: Could not initialize hardware!\n", __func__); - clk_disable(core->b2r2_clock); +enable_clk_failed: if (regulator_disable(core->b2r2_reg) < 0) b2r2_log_err(core->dev, "%s: regulator_disable failed!\n", __func__); @@ -946,22 +769,6 @@ static void exit_job_list(struct b2r2_core *core, } /** - * get_next_job_id() - Return a new job id. - * - * @core: The b2r2 core entity - */ -static int get_next_job_id(struct b2r2_core *core) -{ - int job_id; - - if (core->next_job_id < 1) - core->next_job_id = 1; - job_id = core->next_job_id++; - - return job_id; -} - -/** * job_work_function() - Work queue function that calls callback(s) and * checks if B2R2 can accept a new job * @@ -972,11 +779,7 @@ static void job_work_function(struct work_struct *ptr) unsigned long flags; struct b2r2_core_job *job = container_of(ptr, struct b2r2_core_job, work); - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; /* Disable B2R2 */ domain_disable(core); @@ -1025,10 +828,12 @@ static void timeout_work_function(struct work_struct *ptr) if (core->n_active_jobs > 0) { unsigned long diff = (long) jiffies - (long) core->jiffies_last_irq; - if (diff > HZ/2) { + if (diff > JOB_TIMEOUT) { /* Active jobs and more than a second since last irq! */ int i; + b2r2_core_print_stats(core); + /* Look for timeout:ed jobs and put them in tmp list. * It's important that the application queues are * killed in order of decreasing priority */ @@ -1077,7 +882,7 @@ static void timeout_work_function(struct work_struct *ptr) if (core->n_active_jobs) queue_delayed_work( core->work_queue, - &core->timeout_work, HZ/2); + &core->timeout_work, JOB_TIMEOUT); spin_unlock_irqrestore(&core->lock, flags); } @@ -1194,18 +999,10 @@ static void stop_hw_timer(struct b2r2_core *core, struct b2r2_core_job *job) */ static void init_job(struct b2r2_core_job *job) { - struct b2r2_blt_instance *instance; - struct b2r2_core *core; - - instance = (struct b2r2_blt_instance *) job->tag; - core = instance->control->data; job->start_sentinel = START_SENTINEL; job->end_sentinel = END_SENTINEL; - /* Get a job id*/ - job->job_id = get_next_job_id(core); - /* Job is idle, never queued */ job->job_state = B2R2_CORE_JOB_IDLE; @@ -1361,7 +1158,7 @@ static void check_prio_list(struct b2r2_core *core, bool atomic) #ifdef HANDLE_TIMEOUTED_JOBS /* Check in one half second if it hangs */ queue_delayed_work(core->work_queue, - &core->timeout_work, HZ/2); + &core->timeout_work, JOB_TIMEOUT); #endif } else { /* No resources */ @@ -1395,9 +1192,7 @@ static struct b2r2_core_job *find_job_in_list(int job_id, struct b2r2_core_job *job = list_entry( ptr, struct b2r2_core_job, list); if (job->job_id == job_id) { - struct b2r2_blt_instance *instance = - (struct b2r2_blt_instance *) job->tag; - struct b2r2_core *core = instance->control->data; + struct b2r2_core *core = (struct b2r2_core *) job->data; /* Increase reference count, should be released by the caller of b2r2_core_job_find */ internal_job_addref(core, job, __func__); @@ -1503,7 +1298,7 @@ static struct b2r2_core_job *find_tag_in_active_jobs(struct b2r2_core *core, */ static int hw_reset(struct b2r2_core *core) { - u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE; + u32 uTimeOut = B2R2_RESET_TIMEOUT_VALUE; /* Tell B2R2 to reset */ writel(readl(&core->hw->BLT_CTL) | B2R2BLT_CTLGLOBAL_soft_reset, @@ -1761,17 +1556,23 @@ static void process_events(struct b2r2_core *core, u32 status) * b2r2_irq_handler() - B2R2 interrupt handler * * @irq: Interrupt number (not used) - * @x: ??? (Not used) + * @dev_id: A pointer to the b2r2 core entity */ -static irqreturn_t b2r2_irq_handler(int irq, void *x) +static irqreturn_t b2r2_irq_handler(int irq, void *dev_id) { unsigned long flags; - struct b2r2_core* core = (struct b2r2_core *) x; + struct b2r2_core* core = (struct b2r2_core *) dev_id; /* Spin lock is need in irq handler (SMP) */ spin_lock_irqsave(&core->lock, flags); - /* Make sure that we have a clock */ + /* Make a quick exit if this device was not interrupting */ + if (!core->valid || + ((readl(&core->hw->BLT_ITS) & B2R2_ITS_MASK) == 0)) { + core->stat_n_irq_skipped++; + spin_unlock_irqrestore(&core->lock, flags); + return IRQ_NONE; + } /* Remember time for last irq (for timeout mgmt) */ core->jiffies_last_irq = jiffies; @@ -1783,6 +1584,8 @@ static irqreturn_t b2r2_irq_handler(int irq, void *x) /* Check if we can dispatch new jobs */ check_prio_list(core, true); + core->stat_n_irq_exit++; + spin_unlock_irqrestore(&core->lock, flags); return IRQ_HANDLED; @@ -2032,9 +1835,9 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, size_t dev_size; int ret = 0; unsigned long value; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } @@ -2044,7 +1847,7 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, filp->f_dentry->d_inode->i_private); /* Build the string */ - dev_size = sprintf(Buf, "%8lX\n", value); + dev_size = sprintf(tmpbuf, "%8lX\n", value); /* No more to read if offset != 0 */ if (*f_pos > dev_size) @@ -2054,14 +1857,14 @@ static int debugfs_b2r2_reg_read(struct file *filp, char __user *buf, count = dev_size - *f_pos; /* Return it to user space */ - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2078,19 +1881,19 @@ out: static int debugfs_b2r2_reg_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { - char Buf[80]; + char tmpbuf[80]; u32 reg_value; int ret = 0; /* Adjust count */ - if (count >= sizeof(Buf)) - count = sizeof(Buf) - 1; + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; /* Get it from user space */ - if (copy_from_user(Buf, buf, count)) + if (copy_from_user(tmpbuf, buf, count)) return -EINVAL; - Buf[count] = 0; + tmpbuf[count] = 0; /* Convert from hex string */ - if (sscanf(Buf, "%8lX", (unsigned long *) ®_value) != 1) + if (sscanf(tmpbuf, "%8lX", (unsigned long *) ®_value) != 1) return -EINVAL; writel(reg_value, (u32 *) @@ -2127,9 +1930,9 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, size_t dev_size = 0; int ret = 0; int i; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } @@ -2140,7 +1943,7 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, readl((u32 *) (((u8 *) filp->f_dentry->d_inode->i_private) + debugfs_regs[i].offset)); - dev_size += sprintf(Buf + dev_size, "%s: %08lX\n", + dev_size += sprintf(tmpbuf + dev_size, "%s: %08lX\n", debugfs_regs[i].name, value); } @@ -2152,14 +1955,14 @@ static int debugfs_b2r2_regs_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2187,30 +1990,30 @@ static int debugfs_b2r2_stat_read(struct file *filp, char __user *buf, size_t dev_size = 0; int ret = 0; int i = 0; - char *Buf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); + char *tmpbuf = kmalloc(sizeof(char) * 4096, GFP_KERNEL); struct b2r2_core *core = filp->f_dentry->d_inode->i_private; - if (Buf == NULL) { + if (tmpbuf == NULL) { ret = -ENOMEM; goto out; } /* Build a string containing all statistics */ - dev_size += sprintf(Buf + dev_size, "Interrupts : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Interrupts : %lu\n", core->stat_n_irq); - dev_size += sprintf(Buf + dev_size, "Added jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Added jobs : %lu\n", core->stat_n_jobs_added); - dev_size += sprintf(Buf + dev_size, "Removed jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Removed jobs : %lu\n", core->stat_n_jobs_removed); - dev_size += sprintf(Buf + dev_size, "Jobs in prio list : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Jobs in prio list : %lu\n", core->stat_n_jobs_in_prio_list); - dev_size += sprintf(Buf + dev_size, "Active jobs : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Active jobs : %lu\n", core->n_active_jobs); for (i = 0; i < ARRAY_SIZE(core->active_jobs); i++) - dev_size += sprintf(Buf + dev_size, + dev_size += sprintf(tmpbuf + dev_size, " Job in queue %d : 0x%08lx\n", i, (unsigned long) core->active_jobs[i]); - dev_size += sprintf(Buf + dev_size, "Clock requests : %lu\n", + dev_size += sprintf(tmpbuf + dev_size, "Clock requests : %lu\n", core->clock_request_count); /* No more to read if offset != 0 */ @@ -2220,14 +2023,14 @@ static int debugfs_b2r2_stat_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; out: - if (Buf != NULL) - kfree(Buf); + if (tmpbuf != NULL) + kfree(tmpbuf); return ret; } @@ -2254,14 +2057,14 @@ static int debugfs_b2r2_clock_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { /* 10 characters hex number + newline + string terminator; */ - char Buf[10+2]; + char tmpbuf[10+2]; size_t dev_size; int ret = 0; struct b2r2_core *core = filp->f_dentry->d_inode->i_private; unsigned long value = clk_get_rate(core->b2r2_clock); - dev_size = sprintf(Buf, "%#010lx\n", value); + dev_size = sprintf(tmpbuf, "%#010lx\n", value); /* No more to read if offset != 0 */ if (*f_pos > dev_size) @@ -2270,7 +2073,7 @@ static int debugfs_b2r2_clock_read(struct file *filp, char __user *buf, if (*f_pos + count > dev_size) count = dev_size - *f_pos; - if (copy_to_user(buf, Buf, count)) + if (copy_to_user(buf, tmpbuf, count)) ret = -EINVAL; *f_pos += count; ret = count; @@ -2292,16 +2095,16 @@ out: static int debugfs_b2r2_clock_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { - char Buf[80]; + char tmpbuf[80]; u32 reg_value; int ret = 0; - if (count >= sizeof(Buf)) - count = sizeof(Buf) - 1; - if (copy_from_user(Buf, buf, count)) + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; + if (copy_from_user(tmpbuf, buf, count)) return -EINVAL; - Buf[count] = 0; - if (sscanf(Buf, "%8lX", (unsigned long *) ®_value) != 1) + tmpbuf[count] = 0; + if (sscanf(tmpbuf, "%8lX", (unsigned long *) ®_value) != 1) return -EINVAL; /*not working yet*/ @@ -2322,6 +2125,88 @@ static const struct file_operations debugfs_b2r2_clock_fops = { .write = debugfs_b2r2_clock_write, }; +/** + * debugfs_b2r2_enabled_read() - Implements debugfs read for + * B2R2 Core Enable/Disable + * @filp: File pointer + * @buf: User space buffer + * @count: Number of bytes to read + * @f_pos: File position + * + * Returns number of bytes read or negative error code + */ +static int debugfs_b2r2_enabled_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + /* 4 characters hex number + newline + string terminator; */ + char tmpbuf[4+2]; + size_t dev_size; + int ret = 0; + struct b2r2_core *core = filp->f_dentry->d_inode->i_private; + + dev_size = sprintf(tmpbuf, "%02X\n", core->control->enabled); + + /* No more to read if offset != 0 */ + if (*f_pos > dev_size) + goto out; + + if (*f_pos + count > dev_size) + count = dev_size - *f_pos; + + if (copy_to_user(buf, tmpbuf, count)) + ret = -EINVAL; + *f_pos += count; + ret = count; +out: + return ret; +} + +/** + * debugfs_b2r2_enabled_write() - Implements debugfs write for + * B2R2 Core Enable/Disable + * @filp: File pointer + * @buf: User space buffer + * @count: Number of bytes to write + * @f_pos: File position + * + * Returns number of bytes written or negative error code + */ +static int debugfs_b2r2_enabled_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + char tmpbuf[80]; + unsigned int enable; + int ret = 0; + struct b2r2_core *core = filp->f_dentry->d_inode->i_private; + + if (count >= sizeof(tmpbuf)) + count = sizeof(tmpbuf) - 1; + if (copy_from_user(tmpbuf, buf, count)) + return -EINVAL; + tmpbuf[count] = 0; + if (sscanf(tmpbuf, "%02X", &enable) != 1) + return -EINVAL; + + if (enable) + core->control->enabled = true; + else + core->control->enabled = false; + + *f_pos += count; + ret = count; + + return ret; +} + +/** + * debugfs_b2r2_enabled_fops() - File operations for B2R2 Core Enable/Disable debugfs + */ +static const struct file_operations debugfs_b2r2_enabled_fops = { + .owner = THIS_MODULE, + .read = debugfs_b2r2_enabled_read, + .write = debugfs_b2r2_enabled_write, +}; + #endif /** @@ -2348,7 +2233,7 @@ static const struct file_operations debugfs_b2r2_clock_fops = { static int init_hw(struct b2r2_core *core) { int result = 0; - u32 uTimeOut = B2R2_DRIVER_TIMEOUT_VALUE; + u32 uTimeOut = B2R2_RESET_TIMEOUT_VALUE; /* Put B2R2 into reset */ clear_interrupts(core); @@ -2357,7 +2242,7 @@ static int init_hw(struct b2r2_core *core) &core->hw->BLT_CTL); /* Set up interrupt handler */ - result = request_irq(core->irq, b2r2_irq_handler, 0, + result = request_irq(core->irq, b2r2_irq_handler, IRQF_SHARED, "b2r2-interrupt", core); if (result) { b2r2_log_err(core->dev, @@ -2400,7 +2285,7 @@ static int init_hw(struct b2r2_core *core) debugfs_create_file(debugfs_regs[i].name, 0666, core->debugfs_regs_dir, (void *)(((u8 *) core->hw) + - debugfs_regs[i].offset), + debugfs_regs[i].offset), &debugfs_b2r2_reg_fops); } #endif @@ -2474,18 +2359,22 @@ static void exit_hw(struct b2r2_core *core) static int b2r2_probe(struct platform_device *pdev) { int ret = 0; - struct resource *res; - struct b2r2_core *core; - struct b2r2_control *control; + struct resource *res = NULL; + struct b2r2_core *core = NULL; + struct b2r2_control *control = NULL; + struct b2r2_platform_data *pdata = NULL; + int debug_init = 0; BUG_ON(pdev == NULL); BUG_ON(pdev->id < 0 || pdev->id >= B2R2_MAX_NBR_DEVICES); + pdata = pdev->dev.platform_data; + core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) { dev_err(&pdev->dev, "b2r2 core alloc failed\n"); ret = -EINVAL; - goto b2r2_probe_core_alloc_fail; + goto error_exit; } core->dev = &pdev->dev; @@ -2512,24 +2401,25 @@ static int b2r2_probe(struct platform_device *pdev) core->work_queue = create_workqueue("B2R2"); if (!core->work_queue) { ret = -ENOMEM; - goto b2r2_probe_no_work_queue; + goto error_exit; } /* Get the clock for B2R2 */ - core->b2r2_clock = clk_get(core->dev, "b2r2"); + core->b2r2_clock = clk_get(core->dev, pdata->clock_id); if (IS_ERR(core->b2r2_clock)) { ret = PTR_ERR(core->b2r2_clock); - dev_err(&pdev->dev, "clk_get b2r2 failed\n"); - goto b2r2_probe_no_clk; + dev_err(&pdev->dev, "clk_get %s failed\n", pdata->clock_id); + goto error_exit; } /* Get the B2R2 regulator */ - core->b2r2_reg = regulator_get(core->dev, "vsupply"); + core->b2r2_reg = regulator_get(core->dev, pdata->regulator_id); if (IS_ERR(core->b2r2_reg)) { ret = PTR_ERR(core->b2r2_reg); - dev_err(&pdev->dev, "regulator_get vsupply failed " - "(dev_name=%s)\n", dev_name(core->dev)); - goto b2r2_probe_no_reg; + dev_err(&pdev->dev, "regulator_get %s failed " + "(dev_name=%s)\n", pdata->regulator_id, + dev_name(core->dev)); + goto error_exit; } /* Init power management */ @@ -2537,18 +2427,19 @@ static int b2r2_probe(struct platform_device *pdev) INIT_DELAYED_WORK_DEFERRABLE(&core->domain_disable_work, domain_disable_work_function); core->domain_enabled = false; + core->valid = false; /* Map B2R2 into kernel virtual memory space */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) - goto b2r2_probe_no_res; + goto error_exit; /* Hook up irq */ core->irq = platform_get_irq(pdev, 0); if (core->irq <= 0) { dev_err(&pdev->dev, "%s: Failed to request irq (irq=%d)\n", __func__, core->irq); - goto b2r2_failed_irq_get; + goto error_exit; } core->hw = (struct b2r2_memory_map *) ioremap(res->start, @@ -2556,7 +2447,7 @@ static int b2r2_probe(struct platform_device *pdev) if (core->hw == NULL) { dev_err(&pdev->dev, "%s: ioremap failed\n", __func__); ret = -ENOMEM; - goto b2r2_probe_ioremap_failed; + goto error_exit; } dev_dbg(core->dev, "b2r2 structure address %p\n", core->hw); @@ -2565,14 +2456,12 @@ static int b2r2_probe(struct platform_device *pdev) if (!control) { dev_err(&pdev->dev, "b2r2 control alloc failed\n"); ret = -EINVAL; - goto b2r2_probe_control_alloc_fail; + goto error_exit; } - control->miscdev.parent = core->dev; control->data = (void *)core; control->id = pdev->id; control->dev = &pdev->dev; /* Temporary device */ - snprintf(control->name, sizeof(control->name), "%s_blt", core->name); core->op_size = B2R2_PLUG_OPCODE_SIZE_DEFAULT; core->ch_size = B2R2_PLUG_CHUNK_SIZE_DEFAULT; @@ -2598,6 +2487,9 @@ static int b2r2_probe(struct platform_device *pdev) core, &debugfs_b2r2_stat_fops); debugfs_create_file("clock", 0666, core->debugfs_core_root_dir, core, &debugfs_b2r2_clock_fops); + debugfs_create_file("enabled", 0666, + core->debugfs_core_root_dir, + core, &debugfs_b2r2_enabled_fops); debugfs_create_u8("op_size", 0666, core->debugfs_core_root_dir, &core->op_size); debugfs_create_u8("ch_size", 0666, core->debugfs_core_root_dir, @@ -2614,39 +2506,44 @@ static int b2r2_probe(struct platform_device *pdev) ret = b2r2_debug_init(control); if (ret < 0) { dev_err(&pdev->dev, "b2r2_debug_init failed\n"); - goto b2r2_probe_debug_init_failed; + goto error_exit; } + debug_init = 1; - /* Initialize b2r2_blt module. FIXME: Module of it's own - or perhaps a dedicated module init c file? */ - ret = b2r2_blt_module_init(control); + /* Initialize b2r2_control */ + ret = b2r2_control_init(control); if (ret < 0) { - b2r2_log_err(&pdev->dev, "b2r2_blt_module_init failed\n"); - goto b2r2_probe_blt_init_fail; + b2r2_log_err(&pdev->dev, "b2r2_control_init failed\n"); + goto error_exit; } - core->control = control; + + /* Add the control to the blitter */ + kref_init(&control->ref); + control->enabled = true; + b2r2_blt_add_control(control); + b2r2_core[pdev->id] = core; - dev_info(&pdev->dev, "init done.\n"); + dev_info(&pdev->dev, "%s done.\n", __func__); return ret; /** Recover from any error if something fails */ -b2r2_probe_blt_init_fail: +error_exit: kfree(control); -b2r2_probe_control_alloc_fail: -b2r2_probe_ioremap_failed: -b2r2_failed_irq_get: -b2r2_probe_no_res: - regulator_put(core->b2r2_reg); -b2r2_probe_no_reg: - clk_put(core->b2r2_clock); -b2r2_probe_no_clk: - destroy_workqueue(core->work_queue); - core->work_queue = NULL; -b2r2_probe_no_work_queue: - b2r2_debug_exit(); -b2r2_probe_debug_init_failed: + + if (!IS_ERR_OR_NULL(core->b2r2_reg)) + regulator_put(core->b2r2_reg); + + if (!IS_ERR_OR_NULL(core->b2r2_clock)) + clk_put(core->b2r2_clock); + + if (!IS_ERR_OR_NULL(core->work_queue)) + destroy_workqueue(core->work_queue); + + if (debug_init) + b2r2_debug_exit(); + #ifdef CONFIG_DEBUG_FS if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { debugfs_remove_recursive(core->debugfs_root_dir); @@ -2654,44 +2551,29 @@ b2r2_probe_debug_init_failed: } #endif kfree(core); -b2r2_probe_core_alloc_fail: - dev_info(&pdev->dev, "init done with errors.\n"); + + dev_info(&pdev->dev, "%s done with errors (%d).\n", __func__, ret); return ret; } - - -/** - * b2r2_remove - This routine unloads b2r2 driver - * - * @pdev: platform device. - */ -static int b2r2_remove(struct platform_device *pdev) +void b2r2_core_release(struct kref *control_ref) { + struct b2r2_control *control = container_of( + control_ref, struct b2r2_control, ref); + struct b2r2_core *core = control->data; + int id = control->id; unsigned long flags; - struct b2r2_core *core; - - BUG_ON(pdev == NULL); - - core = dev_get_drvdata(&pdev->dev); - BUG_ON(core == NULL); - b2r2_log_info(&pdev->dev, "%s: Started\n", __func__); - -#ifdef CONFIG_DEBUG_FS - if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { - debugfs_remove_recursive(core->debugfs_root_dir); - core->debugfs_root_dir = NULL; - } +#ifdef CONFIG_B2R2_DEBUG + struct device *dev = core->dev; #endif - /* Flush B2R2 work queue (call all callbacks) */ - flush_workqueue(core->work_queue); - - /* Exit b2r2 blt module */ - b2r2_blt_module_exit(core->control); + b2r2_log_info(dev, "%s: enter\n", __func__); - kfree(core->control); + /* Exit b2r2 control module */ + b2r2_control_exit(control); + kfree(control); + b2r2_debug_exit(); #ifdef HANDLE_TIMEOUTED_JOBS cancel_delayed_work(&core->timeout_work); @@ -2705,7 +2587,7 @@ static int b2r2_remove(struct platform_device *pdev) cancel_delayed_work_sync(&core->domain_disable_work); /** Unmap B2R2 registers */ - b2r2_log_info(&pdev->dev, "unmap b2r2 registers..\n"); + b2r2_log_info(dev, "%s: unmap b2r2 registers..\n", __func__); if (core->hw) { iounmap(core->hw); core->hw = NULL; @@ -2723,9 +2605,41 @@ static int b2r2_remove(struct platform_device *pdev) core->dev = NULL; kfree(core); - b2r2_core[pdev->id] = NULL; + b2r2_core[id] = NULL; - b2r2_debug_exit(); + b2r2_log_info(dev, "%s: exit\n", __func__); +} + + +/** + * b2r2_remove - This routine unloads b2r2 driver + * + * @pdev: platform device. + */ +static int b2r2_remove(struct platform_device *pdev) +{ + struct b2r2_core *core; + + BUG_ON(pdev == NULL); + + core = dev_get_drvdata(&pdev->dev); + BUG_ON(core == NULL); + b2r2_log_info(&pdev->dev, "%s: Started\n", __func__); + +#ifdef CONFIG_DEBUG_FS + if (!IS_ERR_OR_NULL(core->debugfs_root_dir)) { + debugfs_remove_recursive(core->debugfs_root_dir); + core->debugfs_root_dir = NULL; + } +#endif + + /* Flush B2R2 work queue (call all callbacks) */ + flush_workqueue(core->work_queue); + + /* Remove control from blitter */ + core->control->enabled = false; + b2r2_blt_remove_control(core->control); + kref_put(&core->control->ref, b2r2_core_release); b2r2_log_info(&pdev->dev, "%s: Ended\n", __func__); @@ -2785,6 +2699,23 @@ int b2r2_resume(struct platform_device *pdev) return 0; } +void b2r2_core_print_stats(struct b2r2_core *core) +{ + b2r2_log_info(core->dev, + "%s: n_irq %ld, n_irq_exit %ld, n_irq_skipped %ld,\n" + "n_jobs_added %ld, n_active_jobs %ld, " + "n_jobs_in_prio_list %ld,\n" + "n_jobs_removed %ld\n", + __func__, + core->stat_n_irq, + core->stat_n_irq_exit, + core->stat_n_irq_skipped, + core->stat_n_jobs_added, + core->n_active_jobs, + core->stat_n_jobs_in_prio_list, + core->stat_n_jobs_removed); +} + /** * struct platform_b2r2_driver - Platform driver configuration for the * B2R2 core driver diff --git a/drivers/video/b2r2/b2r2_core.h b/drivers/video/b2r2/b2r2_core.h index 991dd9d9d1b..5b9fdcdc2bb 100644 --- a/drivers/video/b2r2/b2r2_core.h +++ b/drivers/video/b2r2/b2r2_core.h @@ -18,6 +18,194 @@ #include <linux/wait.h> #include <linux/workqueue.h> +/** + * B2R2_RESET_TIMEOUT_VALUE - The number of times to read the status register + * waiting for b2r2 to go idle after soft reset. + */ +#define B2R2_RESET_TIMEOUT_VALUE (1500) + +/** + * B2R2_CLK_FLAG - Value to write into clock reg to turn clock on + */ +#define B2R2_CLK_FLAG (0x125) + +/** + * DEBUG_CHECK_ADDREF_RELEASE - Define this to enable addref / release debug + */ +#define DEBUG_CHECK_ADDREF_RELEASE 1 + +#ifdef CONFIG_DEBUG_FS +/** + * HANDLE_TIMEOUTED_JOBS - Define this to check jobs for timeout and cancel them + */ +#define HANDLE_TIMEOUTED_JOBS +#define JOB_TIMEOUT (HZ/2) +#endif + +/** + * B2R2_CLOCK_ALWAYS_ON - Define this to disable power save clock turn off + */ +/* #define B2R2_CLOCK_ALWAYS_ON 1 */ + +/** + * START_SENTINEL - Watch guard to detect job overwrites + */ +#define START_SENTINEL 0xBABEDEEA + +/** + * STOP_SENTINEL - Watch guard to detect job overwrites + */ +#define END_SENTINEL 0xDADBDCDD + +/** + * B2R2_CORE_LOWEST_PRIO - Lowest prio allowed + */ +#define B2R2_CORE_LOWEST_PRIO -19 +/** + * B2R2_CORE_HIGHEST_PRIO - Highest prio allowed + */ +#define B2R2_CORE_HIGHEST_PRIO 20 + +/** + * B2R2_DOMAIN_DISABLE - + */ +#define B2R2_DOMAIN_DISABLE_TIMEOUT (HZ/100) + +/** + * B2R2_REGULATOR_RETRY_COUNT - + */ +#define B2R2_REGULATOR_RETRY_COUNT 10 + + +#ifdef DEBUG_CHECK_ADDREF_RELEASE + +/** + * struct addref_release - Represents one addref or release. Used + * to debug addref / release problems + * + * @addref: true if this represents an addref else it represents + * a release. + * @job: The job that was referenced + * @caller: The caller of the addref or release + * @ref_count: The job reference count after addref / release + */ +struct addref_release { + bool addref; + struct b2r2_core_job *job; + const char *caller; + int ref_count; +}; + +#endif + +/** + * struct b2r2_core - Administration data for B2R2 core + * + * @lock: Spin lock protecting the b2r2_core structure and the B2R2 HW + * @hw: B2R2 registers memory mapped + * @pmu_b2r2_clock: Control of B2R2 clock + * @log_dev: Device used for logging via dev_... functions + * + * @prio_queue: Queue of jobs sorted in priority order + * @active_jobs: Array containing pointer to zero or one job per queue + * @n_active_jobs: Number of active jobs + * @jiffies_last_active: jiffie value when adding last active job + * @jiffies_last_irq: jiffie value when last irq occured + * @timeout_work: Work structure for timeout work + * + * @next_job_id: Contains the job id that will be assigned to the next + * added job. + * + * @clock_request_count: When non-zero, clock is on + * @clock_off_timer: Kernel timer to handle delayed turn off of clock + * + * @work_queue: Work queue to handle done jobs (callbacks) and timeouts in + * non-interrupt context. + * + * @stat_n_irq: Number of interrupts (statistics) + * @stat_n_jobs_added: Number of jobs added (statistics) + * @stat_n_jobs_removed: Number of jobs removed (statistics) + * @stat_n_jobs_in_prio_list: Number of jobs in prio list (statistics) + * + * @debugfs_root_dir: Root directory for B2R2 debugfs + * + * @ar: Circular array of addref / release debug structs + * @ar_write: Where next write will occur + * @ar_read: First valid place to read. When ar_read == ar_write then + * the array is empty. + */ +struct b2r2_core { + spinlock_t lock; + + struct b2r2_memory_map *hw; + + u8 op_size; + u8 ch_size; + u8 pg_size; + u8 mg_size; + u16 min_req_time; + int irq; + + char name[16]; + struct device *dev; + + struct list_head prio_queue; + + struct b2r2_core_job *active_jobs[B2R2_CORE_QUEUE_NO_OF]; + unsigned long n_active_jobs; + + unsigned long jiffies_last_active; + unsigned long jiffies_last_irq; +#ifdef HANDLE_TIMEOUTED_JOBS + struct delayed_work timeout_work; +#endif + int next_job_id; + + unsigned long clock_request_count; + struct timer_list clock_off_timer; + + struct workqueue_struct *work_queue; + + /* Statistics */ + unsigned long stat_n_irq_exit; + unsigned long stat_n_irq_skipped; + unsigned long stat_n_irq; + unsigned long stat_n_jobs_added; + unsigned long stat_n_jobs_removed; + + unsigned long stat_n_jobs_in_prio_list; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root_dir; + struct dentry *debugfs_core_root_dir; + struct dentry *debugfs_regs_dir; +#endif + +#ifdef DEBUG_CHECK_ADDREF_RELEASE + /* Tracking release bug...*/ + struct addref_release ar[100]; + int ar_write; + int ar_read; +#endif + + /* Power management variables */ + struct mutex domain_lock; + struct delayed_work domain_disable_work; + + /* + * We need to keep track of both the number of domain_enable/disable() + * calls and whether the power was actually turned off, since the + * power off is done in a delayed job. + */ + bool domain_enabled; + volatile bool valid; + int domain_request_count; + + struct clk *b2r2_clock; + struct regulator *b2r2_reg; + + struct b2r2_control *control; +}; /** * b2r2_core_job_add() - Adds a job to B2R2 job queues @@ -105,4 +293,8 @@ void b2r2_core_job_addref(struct b2r2_core_job *job, const char *caller); */ void b2r2_core_job_release(struct b2r2_core_job *job, const char *caller); +void b2r2_core_print_stats(struct b2r2_core *core); + +void b2r2_core_release(struct kref *control_ref); + #endif /* !defined(__B2R2_CORE_JOB_H__) */ diff --git a/drivers/video/b2r2/b2r2_input_validation.c b/drivers/video/b2r2/b2r2_input_validation.c index ac8b5728847..c8eb2f7b025 100644 --- a/drivers/video/b2r2/b2r2_input_validation.c +++ b/drivers/video/b2r2/b2r2_input_validation.c @@ -32,7 +32,7 @@ static bool is_valid_format(enum b2r2_blt_fmt fmt); static bool is_valid_bg_format(enum b2r2_blt_fmt fmt); -static bool is_valid_pitch_for_fmt(struct b2r2_control *cont, +static bool is_valid_pitch_for_fmt(struct device *dev, u32 pitch, s32 width, enum b2r2_blt_fmt fmt); static bool is_aligned_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt); @@ -40,9 +40,9 @@ static s32 width_2_complete_width(s32 width, enum b2r2_blt_fmt fmt); static bool is_complete_width_for_fmt(s32 width, enum b2r2_blt_fmt fmt); static bool is_valid_height_for_fmt(s32 height, enum b2r2_blt_fmt fmt); -static bool validate_img(struct b2r2_control *cont, +static bool validate_img(struct device *dev, struct b2r2_blt_img *img); -static bool validate_rect(struct b2r2_control *cont, +static bool validate_rect(struct device *dev, struct b2r2_blt_rect *rect); @@ -103,7 +103,7 @@ static bool is_valid_bg_format(enum b2r2_blt_fmt fmt) } -static bool is_valid_pitch_for_fmt(struct b2r2_control *cont, +static bool is_valid_pitch_for_fmt(struct device *dev, u32 pitch, s32 width, enum b2r2_blt_fmt fmt) { s32 complete_width; @@ -111,7 +111,7 @@ static bool is_valid_pitch_for_fmt(struct b2r2_control *cont, complete_width = width_2_complete_width(width, fmt); - pitch_derived_from_width = b2r2_calc_pitch_from_width(cont, + pitch_derived_from_width = b2r2_calc_pitch_from_width(dev, complete_width, fmt); if (pitch < pitch_derived_from_width) @@ -263,7 +263,7 @@ static bool is_valid_height_for_fmt(s32 height, enum b2r2_blt_fmt fmt) return true; } -static bool validate_img(struct b2r2_control *cont, +static bool validate_img(struct device *dev, struct b2r2_blt_img *img) { /* @@ -276,14 +276,14 @@ static bool validate_img(struct b2r2_control *cont, s32 img_size; if (!is_valid_format(img->fmt)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!is_valid_format(img->fmt)\n"); return false; } if (img->width < 0 || img->width > max_img_width_height || img->height < 0 || img->height > max_img_width_height) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "img->width < 0 || " "img->width > max_img_width_height || " "img->height < 0 || " @@ -293,7 +293,7 @@ static bool validate_img(struct b2r2_control *cont, if (b2r2_is_mb_fmt(img->fmt)) { if (!is_complete_width_for_fmt(img->width, img->fmt)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!is_complete_width_for_fmt(img->width," " img->fmt)\n"); return false; @@ -302,7 +302,7 @@ static bool validate_img(struct b2r2_control *cont, if (0 == img->pitch && (!is_aligned_width_for_fmt(img->width, img->fmt) || !is_complete_width_for_fmt(img->width, img->fmt))) { - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "Validation Error: " "0 == img->pitch && " "(!is_aligned_width_for_fmt(img->width," @@ -313,24 +313,24 @@ static bool validate_img(struct b2r2_control *cont, } if (img->pitch != 0 && - !is_valid_pitch_for_fmt(cont, img->pitch, img->width, + !is_valid_pitch_for_fmt(dev, img->pitch, img->width, img->fmt)) { - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "Validation Error: " "img->pitch != 0 && " - "!is_valid_pitch_for_fmt(cont, " + "!is_valid_pitch_for_fmt(dev, " "img->pitch, img->width, img->fmt)\n"); return false; } } if (!is_valid_height_for_fmt(img->width, img->fmt)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!is_valid_height_for_fmt(img->width, img->fmt)\n"); return false; } - img_size = b2r2_get_img_size(cont, img); + img_size = b2r2_get_img_size(dev, img); /* * To keep the entire image inside s32 range. @@ -338,7 +338,7 @@ static bool validate_img(struct b2r2_control *cont, if ((B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET == img->buf.type || B2R2_BLT_PTR_FD_OFFSET == img->buf.type) && img->buf.offset > (u32)b2r2_s32_max - (u32)img_size) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "(B2R2_BLT_PTR_HWMEM_BUF_NAME_OFFSET == " "img->buf.type || B2R2_BLT_PTR_FD_OFFSET == " "img->buf.type) && img->buf.offset > " @@ -349,11 +349,11 @@ static bool validate_img(struct b2r2_control *cont, return true; } -static bool validate_rect(struct b2r2_control *cont, +static bool validate_rect(struct device *dev, struct b2r2_blt_rect *rect) { if (rect->width < 0 || rect->height < 0) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "rect->width < 0 || rect->height < 0\n"); return false; } @@ -361,7 +361,7 @@ static bool validate_rect(struct b2r2_control *cont, return true; } -bool b2r2_validate_user_req(struct b2r2_control *cont, +bool b2r2_validate_user_req(struct device *dev, struct b2r2_blt_req *req) { bool is_src_img_used; @@ -370,7 +370,7 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, bool is_dst_clip_rect_used; if (req->size != sizeof(struct b2r2_blt_req)) { - b2r2_log_err(cont->dev, "Validation Error: " + b2r2_log_err(dev, "Validation Error: " "req->size != sizeof(struct b2r2_blt_req)\n"); return false; } @@ -382,31 +382,31 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, is_dst_clip_rect_used = req->flags & B2R2_BLT_FLAG_DESTINATION_CLIP; if (is_src_img_used || is_src_mask_used) { - if (!validate_rect(cont, &req->src_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_rect(cont, &req->src_rect)\n"); + if (!validate_rect(dev, &req->src_rect)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_rect(dev, &req->src_rect)\n"); return false; } } - if (!validate_rect(cont, &req->dst_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_rect(cont, &req->dst_rect)\n"); + if (!validate_rect(dev, &req->dst_rect)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_rect(dev, &req->dst_rect)\n"); return false; } if (is_bg_img_used) { - if (!validate_rect(cont, &req->bg_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_rect(cont, &req->bg_rect)\n"); + if (!validate_rect(dev, &req->bg_rect)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_rect(dev, &req->bg_rect)\n"); return false; } } if (is_dst_clip_rect_used) { - if (!validate_rect(cont, &req->dst_clip_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_rect(cont, &req->dst_clip_rect)\n"); + if (!validate_rect(dev, &req->dst_clip_rect)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_rect(dev, &req->dst_clip_rect)\n"); return false; } } @@ -414,9 +414,9 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, if (is_src_img_used) { struct b2r2_blt_rect src_img_bounding_rect; - if (!validate_img(cont, &req->src_img)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_img(cont, &req->src_img)\n"); + if (!validate_img(dev, &req->src_img)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_img(dev, &req->src_img)\n"); return false; } @@ -424,7 +424,7 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, &src_img_bounding_rect); if (!b2r2_is_rect_inside_rect(&req->src_rect, &src_img_bounding_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!b2r2_is_rect_inside_rect(&req->src_rect, " "&src_img_bounding_rect)\n"); return false; @@ -434,14 +434,14 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, if (is_bg_img_used) { struct b2r2_blt_rect bg_img_bounding_rect; - if (!validate_img(cont, &req->bg_img)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_img(cont, &req->bg_img)\n"); + if (!validate_img(dev, &req->bg_img)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_img(dev, &req->bg_img)\n"); return false; } if (!is_valid_bg_format(req->bg_img.fmt)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!is_valid_bg_format(req->bg_img->fmt)\n"); return false; } @@ -450,7 +450,7 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, &bg_img_bounding_rect); if (!b2r2_is_rect_inside_rect(&req->bg_rect, &bg_img_bounding_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!b2r2_is_rect_inside_rect(&req->bg_rect, " "&bg_img_bounding_rect)\n"); return false; @@ -460,9 +460,9 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, if (is_src_mask_used) { struct b2r2_blt_rect src_mask_bounding_rect; - if (!validate_img(cont, &req->src_mask)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_img(cont, &req->src_mask)\n"); + if (!validate_img(dev, &req->src_mask)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_img(dev, &req->src_mask)\n"); return false; } @@ -470,22 +470,22 @@ bool b2r2_validate_user_req(struct b2r2_control *cont, &src_mask_bounding_rect); if (!b2r2_is_rect_inside_rect(&req->src_rect, &src_mask_bounding_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!b2r2_is_rect_inside_rect(&req->src_rect, " "&src_mask_bounding_rect)\n"); return false; } } - if (!validate_img(cont, &req->dst_img)) { - b2r2_log_info(cont->dev, "Validation Error: " - "!validate_img(cont, &req->dst_img)\n"); + if (!validate_img(dev, &req->dst_img)) { + b2r2_log_info(dev, "Validation Error: " + "!validate_img(dev, &req->dst_img)\n"); return false; } if (is_bg_img_used) { if (!b2r2_is_rect_gte_rect(&req->bg_rect, &req->dst_rect)) { - b2r2_log_info(cont->dev, "Validation Error: " + b2r2_log_info(dev, "Validation Error: " "!b2r2_is_rect_gte_rect(&req->bg_rect, " "&req->dst_rect)\n"); return false; diff --git a/drivers/video/b2r2/b2r2_input_validation.h b/drivers/video/b2r2/b2r2_input_validation.h index d3c6ae1b296..25f022a45ab 100644 --- a/drivers/video/b2r2/b2r2_input_validation.h +++ b/drivers/video/b2r2/b2r2_input_validation.h @@ -25,7 +25,7 @@ #include "b2r2_internal.h" -bool b2r2_validate_user_req(struct b2r2_control *cont, +bool b2r2_validate_user_req(struct device *dev, struct b2r2_blt_req *req); #endif diff --git a/drivers/video/b2r2/b2r2_internal.h b/drivers/video/b2r2/b2r2_internal.h index c46e814df14..329d644e5e1 100644 --- a/drivers/video/b2r2/b2r2_internal.h +++ b/drivers/video/b2r2/b2r2_internal.h @@ -23,7 +23,7 @@ /** * B2R2_MAX_NBR_DEVICES - The maximum number of B2R2s handled */ -#define B2R2_MAX_NBR_DEVICES 1 +#define B2R2_MAX_NBR_DEVICES 2 /* The maximum possible number of temporary buffers needed */ #define MAX_TMP_BUFS_NEEDED 2 @@ -31,6 +31,9 @@ /* Size of the color look-up table */ #define CLUT_SIZE 1024 +/* The defined bits of the Interrupt Status Register */ +#define B2R2_ITS_MASK 0x0FFFF0FF + /** * b2r2_op_type - the type of B2R2 operation to configure */ @@ -127,9 +130,12 @@ struct tmp_buf { }; /** - * struct b2r2_blt_instance - Represents the B2R2 instance (one per open) + * struct b2r2_control_instance - Represents the B2R2 instance + * (one per open and blitter core) * * @lock: Lock to protect the instance + * @control_id: The b2r2 core core control identifier + * @control: The b2r2 core control entity * * @report_list: Ready requests that should be reported, * @report_list_waitq: Wait queue for report list @@ -137,10 +143,11 @@ struct tmp_buf { * in callback. * @synching: true if any client is waiting for b2r2_blt_synch(0) * @synch_done_waitq: Wait queue to handle synching on request_id 0 - * @control: The b2r2 control entity */ -struct b2r2_blt_instance { +struct b2r2_control_instance { struct mutex lock; + int control_id; + struct b2r2_control *control; /* Requests to be reported */ struct list_head report_list; @@ -150,8 +157,6 @@ struct b2r2_blt_instance { u32 no_of_active_requests; bool synching; wait_queue_head_t synch_done_waitq; - - struct b2r2_control *control; }; /** @@ -370,6 +375,7 @@ struct b2r2_core_job { /* Data to be filled in by client */ int tag; + int data; int prio; u32 first_node_address; u32 last_node_address; @@ -419,6 +425,7 @@ struct b2r2_core_job { * @node_split_job: The administration structure for the B2R2 node split job * @first_node: Pointer to the first B2R2 node * @request_id: Request id for this job + * @core_mask: Bit mask with the cores doing part of the job * @node_split_handle: Handle of the node split * @src_resolved: Calculated info about the source buffer * @src_mask_resolved: Calculated info about the source mask buffer @@ -427,13 +434,14 @@ struct b2r2_core_job { * @profile: True if the blit shall be profiled, false otherwise */ struct b2r2_blt_request { - struct b2r2_blt_instance *instance; + struct b2r2_control_instance *instance; struct list_head list; struct b2r2_blt_req user_req; struct b2r2_core_job job; struct b2r2_node_split_job node_split_job; struct b2r2_node *first_node; int request_id; + u32 core_mask; /* Resolved buffer addresses */ struct b2r2_resolved_buf src_resolved; @@ -491,7 +499,6 @@ struct b2r2_mem_heap { /** * - * @miscdev: The miscdev presenting b2r2 to the system * @dev: The device handle of the b2r2 instance * @id: The id of the b2r2 instance * @name: The name of the b2r2 instance @@ -525,36 +532,36 @@ struct b2r2_mem_heap { * @prev_node_count: Node cound of last_job */ struct b2r2_control { - struct miscdevice miscdev; - struct device *dev; - int id; - char name[16]; - void *data; - struct tmp_buf tmp_bufs[MAX_TMP_BUFS_NEEDED]; - int filters_initialized; - struct b2r2_mem_heap mem_heap; + struct device *dev; + void *data; + int id; + struct kref ref; + bool enabled; + struct tmp_buf tmp_bufs[MAX_TMP_BUFS_NEEDED]; + int filters_initialized; + struct b2r2_mem_heap mem_heap; #ifdef CONFIG_DEBUG_FS - struct b2r2_blt_request debugfs_latest_request; - struct dentry *debugfs_root_dir; - struct dentry *debugfs_debug_root_dir; + struct b2r2_blt_request debugfs_latest_request; + struct dentry *debugfs_root_dir; + struct dentry *debugfs_debug_root_dir; #endif - struct mutex stat_lock; - unsigned long stat_n_jobs_added; - unsigned long stat_n_jobs_released; - unsigned long stat_n_jobs_in_report_list; - unsigned long stat_n_in_blt; - unsigned long stat_n_in_blt_synch; - unsigned long stat_n_in_blt_add; - unsigned long stat_n_in_blt_wait; - unsigned long stat_n_in_synch_0; - unsigned long stat_n_in_synch_job; - unsigned long stat_n_in_query_cap; - unsigned long stat_n_in_open; - unsigned long stat_n_in_release; - struct mutex last_job_lock; - struct b2r2_node *last_job; - char *last_job_chars; - int prev_node_count; + struct mutex stat_lock; + unsigned long stat_n_jobs_added; + unsigned long stat_n_jobs_released; + unsigned long stat_n_jobs_in_report_list; + unsigned long stat_n_in_blt; + unsigned long stat_n_in_blt_synch; + unsigned long stat_n_in_blt_add; + unsigned long stat_n_in_blt_wait; + unsigned long stat_n_in_synch_0; + unsigned long stat_n_in_synch_job; + unsigned long stat_n_in_query_cap; + unsigned long stat_n_in_open; + unsigned long stat_n_in_release; + struct mutex last_job_lock; + struct b2r2_node *last_job; + char *last_job_chars; + int prev_node_count; }; /* FIXME: The functions below should be removed when we are @@ -590,4 +597,14 @@ int b2r2_blt_module_init(struct b2r2_control *cont); */ void b2r2_blt_module_exit(struct b2r2_control *cont); +/** + * b2r2_blt_add_control() - Add the b2r2 core control + */ +void b2r2_blt_add_control(struct b2r2_control *cont); + +/** + * b2r2_blt_remove_control() - Remove the b2r2 core control + */ +void b2r2_blt_remove_control(struct b2r2_control *cont); + #endif diff --git a/drivers/video/b2r2/b2r2_node_split.c b/drivers/video/b2r2/b2r2_node_split.c index d773865223b..b2fb07580ca 100644 --- a/drivers/video/b2r2/b2r2_node_split.c +++ b/drivers/video/b2r2/b2r2_node_split.c @@ -21,9 +21,6 @@ /* * 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 @@ -111,9 +108,6 @@ static int configure_rot_scale(struct b2r2_control *cont, struct b2r2_node_split_job *this, struct b2r2_node *node, struct b2r2_node **next); -static void recalculate_rects(struct b2r2_control *cont, - struct b2r2_blt_req *req); - static int check_rect(struct b2r2_control *cont, const struct b2r2_blt_img *img, const struct b2r2_blt_rect *rect, @@ -255,13 +249,6 @@ int b2r2_node_split_analyze(const struct b2r2_blt_request *req, color_fill = (this->flags & (B2R2_BLT_FLAG_SOURCE_FILL | B2R2_BLT_FLAG_SOURCE_FILL_RAW)) != 0; - /* - * B2R2 cannot handle destination clipping on buffers - * allocated close to 64MiB bank boundaries. - * recalculate src_ and dst_rect to avoid clipping. - */ - recalculate_rects(cont, (struct b2r2_blt_req *) &req->user_req); - /* Configure the source and destination buffers */ set_buf(cont, &this->src, req->src_resolved.physical_address, &req->user_req.src_img, &req->user_req.src_rect, @@ -579,25 +566,6 @@ void b2r2_node_split_cancel(struct b2r2_control *cont, return; } -/* - * Private functions - */ - -static void recalculate_rects(struct b2r2_control *cont, - struct b2r2_blt_req *req) -{ - struct b2r2_blt_rect new_dst_rect; - struct b2r2_blt_rect new_src_rect; - struct b2r2_blt_rect new_bg_rect; - - b2r2_trim_rects(cont, - req, &new_bg_rect, &new_dst_rect, &new_src_rect); - - req->dst_rect = new_dst_rect; - req->src_rect = new_src_rect; - req->bg_rect = new_bg_rect; -} - static int check_rect(struct b2r2_control *cont, const struct b2r2_blt_img *img, const struct b2r2_blt_rect *rect, @@ -619,10 +587,10 @@ static int check_rect(struct b2r2_control *cont, /* 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); + 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; @@ -1338,22 +1306,22 @@ static int analyze_scale_factors(struct b2r2_control *cont, u16 vsf; if (this->rotation) { - ret = calculate_scale_factor(cont, this->src.rect.width, + ret = calculate_scale_factor(cont->dev, this->src.rect.width, this->dst.rect.height, &hsf); if (ret < 0) goto error; - ret = calculate_scale_factor(cont, this->src.rect.height, + ret = calculate_scale_factor(cont->dev, this->src.rect.height, this->dst.rect.width, &vsf); if (ret < 0) goto error; } else { - ret = calculate_scale_factor(cont, this->src.rect.width, + ret = calculate_scale_factor(cont->dev, this->src.rect.width, this->dst.rect.width, &hsf); if (ret < 0) goto error; - ret = calculate_scale_factor(cont, this->src.rect.height, + ret = calculate_scale_factor(cont->dev, this->src.rect.height, this->dst.rect.height, &vsf); if (ret < 0) goto error; @@ -2791,9 +2759,9 @@ static int setup_tmp_buf(struct b2r2_control *cont, if (size > max_size) { /* We need to limit the size, so we choose a different width */ - width = MIN(width, B2R2_RESCALE_MAX_WIDTH); + width = min(width, (u32) B2R2_RESCALE_MAX_WIDTH); pitch = b2r2_fmt_byte_pitch(fmt, width); - height = MIN(height, max_size / pitch); + height = min(height, max_size / pitch); size = pitch * height; } @@ -2918,10 +2886,10 @@ static void set_target(struct b2r2_node *node, u32 addr, /* 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; + l = max(l, 0); + r = min(r, (s32) buf->width) - 1; + t = max(t, 0); + b = min(b, (s32) buf->height) - 1; node->node.GROUP0.B2R2_CIC |= B2R2_CIC_CLIP_WINDOW; node->node.GROUP0.B2R2_INS |= B2R2_INS_RECT_CLIP_ENABLED; diff --git a/drivers/video/b2r2/b2r2_profiler_socket.c b/drivers/video/b2r2/b2r2_profiler_socket.c index ffa7f2870c8..cb95af9380e 100644 --- a/drivers/video/b2r2/b2r2_profiler_socket.c +++ b/drivers/video/b2r2/b2r2_profiler_socket.c @@ -16,7 +16,7 @@ #include "b2r2_profiler_api.h" #include "b2r2_internal.h" - +#include "b2r2_core.h" /* * TODO: Call the profiler in a seperate thread and have a circular buffer @@ -81,7 +81,8 @@ void b2r2_call_profiler_blt_done(const struct b2r2_blt_request * const request) { int return_value; struct b2r2_blt_profiling_info blt_profiling_info; - struct b2r2_control *cont = request->instance->control; + struct b2r2_core *core = (struct b2r2_core *) request->job.data; + struct b2r2_control *cont = core->control; return_value = down_interruptible(&b2r2_profiler_lock); if (return_value != 0) { diff --git a/drivers/video/b2r2/b2r2_utils.c b/drivers/video/b2r2/b2r2_utils.c index d482701089e..44c738b5aab 100644 --- a/drivers/video/b2r2/b2r2_utils.c +++ b/drivers/video/b2r2/b2r2_utils.c @@ -24,19 +24,19 @@ const s32 b2r2_s32_max = 2147483647; * calculate_scale_factor() - calculates the scale factor between the given * values */ -int calculate_scale_factor(struct b2r2_control *cont, +int calculate_scale_factor(struct device *dev, u32 from, u32 to, u16 *sf_out) { int ret; u32 sf; - b2r2_log_info(cont->dev, "%s\n", __func__); + b2r2_log_info(dev, "%s\n", __func__); if (to == from) { *sf_out = 1 << 10; return 0; } else if (to == 0) { - b2r2_log_err(cont->dev, "%s: To is 0!\n", __func__); + b2r2_log_err(dev, "%s: To is 0!\n", __func__); BUG_ON(1); } @@ -44,12 +44,12 @@ int calculate_scale_factor(struct b2r2_control *cont, if ((sf & 0xffff0000) != 0) { /* Overflow error */ - b2r2_log_warn(cont->dev, "%s: " + b2r2_log_warn(dev, "%s: " "Scale factor too large\n", __func__); ret = -EINVAL; goto error; } else if (sf == 0) { - b2r2_log_warn(cont->dev, "%s: " + b2r2_log_warn(dev, "%s: " "Scale factor too small\n", __func__); ret = -EINVAL; goto error; @@ -57,12 +57,12 @@ int calculate_scale_factor(struct b2r2_control *cont, *sf_out = (u16)sf; - b2r2_log_info(cont->dev, "%s exit\n", __func__); + b2r2_log_info(dev, "%s exit\n", __func__); return 0; error: - b2r2_log_warn(cont->dev, "%s: Exit...\n", __func__); + b2r2_log_warn(dev, "%s: Exit...\n", __func__); return ret; } @@ -127,7 +127,7 @@ void b2r2_intersect_rects(struct b2r2_blt_rect *rect1, * the old source rectangle corresponds to * to the new part of old destination rectangle. */ -void b2r2_trim_rects(struct b2r2_control *cont, +void b2r2_trim_rects(struct device *dev, const struct b2r2_blt_req *req, struct b2r2_blt_rect *new_bg_rect, struct b2r2_blt_rect *new_dst_rect, @@ -150,11 +150,11 @@ void b2r2_trim_rects(struct b2r2_control *cont, s16 hsf; s16 vsf; - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nold_dst_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, old_dst_rect->x, old_dst_rect->y, old_dst_rect->width, old_dst_rect->height); - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nold_src_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, old_src_rect->x, old_src_rect->y, old_src_rect->width, old_src_rect->height); @@ -167,7 +167,7 @@ void b2r2_trim_rects(struct b2r2_control *cont, goto keep_rects; b2r2_intersect_rects(old_dst_rect, &dst_img_bounds, new_dst_rect); - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nnew_dst_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, new_dst_rect->x, new_dst_rect->y, new_dst_rect->width, new_dst_rect->height); @@ -181,13 +181,13 @@ void b2r2_trim_rects(struct b2r2_control *cont, if (transform & B2R2_BLT_TRANSFORM_CCW_ROT_90) { int res = 0; - res = calculate_scale_factor(cont, old_src_rect->width, + res = calculate_scale_factor(dev, old_src_rect->width, old_dst_rect->height, &hsf); /* invalid dimensions, leave them to validation */ if (res < 0) goto keep_rects; - res = calculate_scale_factor(cont, old_src_rect->height, + res = calculate_scale_factor(dev, old_src_rect->height, old_dst_rect->width, &vsf); if (res < 0) goto keep_rects; @@ -207,12 +207,12 @@ void b2r2_trim_rects(struct b2r2_control *cont, src_h = new_dst_rect->width * vsf; } else { int res = 0; - res = calculate_scale_factor(cont, old_src_rect->width, + res = calculate_scale_factor(dev, old_src_rect->width, old_dst_rect->width, &hsf); if (res < 0) goto keep_rects; - res = calculate_scale_factor(cont, old_src_rect->height, + res = calculate_scale_factor(dev, old_src_rect->height, old_dst_rect->height, &vsf); if (res < 0) goto keep_rects; @@ -270,7 +270,7 @@ void b2r2_trim_rects(struct b2r2_control *cont, new_src_rect->width = src_w; new_src_rect->height = src_h; - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nnew_src_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, new_src_rect->x, new_src_rect->y, new_src_rect->width, new_src_rect->height); @@ -279,7 +279,7 @@ void b2r2_trim_rects(struct b2r2_control *cont, /* Modify bg_rect in the same way as dst_rect */ s32 dw = new_dst_rect->width - old_dst_rect->width; s32 dh = new_dst_rect->height - old_dst_rect->height; - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nold bg_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, old_bg_rect->x, old_bg_rect->y, old_bg_rect->width, old_bg_rect->height); @@ -287,7 +287,7 @@ void b2r2_trim_rects(struct b2r2_control *cont, new_bg_rect->y = old_bg_rect->y + dy; new_bg_rect->width = old_bg_rect->width + dw; new_bg_rect->height = old_bg_rect->height + dh; - b2r2_log_info(cont->dev, + b2r2_log_info(dev, "%s\nnew bg_rect(x,y,w,h)=(%d, %d, %d, %d)\n", __func__, new_bg_rect->x, new_bg_rect->y, new_bg_rect->width, new_bg_rect->height); @@ -301,11 +301,11 @@ keep_rects: *new_src_rect = *old_src_rect; *new_dst_rect = *old_dst_rect; *new_bg_rect = *old_bg_rect; - b2r2_log_info(cont->dev, "%s original rectangles preserved.\n", __func__); + b2r2_log_info(dev, "%s original rectangles preserved.\n", __func__); return; } -int b2r2_get_fmt_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt) +int b2r2_get_fmt_bpp(struct device *dev, enum b2r2_blt_fmt fmt) { /* * Currently this function is not used that often but if that changes a @@ -351,14 +351,14 @@ int b2r2_get_fmt_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt) return 32; default: - b2r2_log_err(cont->dev, + b2r2_log_err(dev, "%s: Internal error! Format %#x not recognized.\n", __func__, fmt); return 32; } } -int b2r2_get_fmt_y_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt) +int b2r2_get_fmt_y_bpp(struct device *dev, enum b2r2_blt_fmt fmt) { switch (fmt) { case B2R2_BLT_FMT_YUV420_PACKED_PLANAR: @@ -381,7 +381,7 @@ int b2r2_get_fmt_y_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt) return 8; default: - b2r2_log_err(cont->dev, + b2r2_log_err(dev, "%s: Internal error! Non YCbCr format supplied.\n", __func__); return 8; @@ -542,40 +542,40 @@ bool b2r2_is_mb_fmt(enum b2r2_blt_fmt fmt) } } -u32 b2r2_calc_pitch_from_width(struct b2r2_control *cont, +u32 b2r2_calc_pitch_from_width(struct device *dev, s32 width, enum b2r2_blt_fmt fmt) { if (b2r2_is_single_plane_fmt(fmt)) { return (u32)b2r2_div_round_up(width * - b2r2_get_fmt_bpp(cont, fmt), 8); + b2r2_get_fmt_bpp(dev, fmt), 8); } else if (b2r2_is_ycbcrsp_fmt(fmt) || b2r2_is_ycbcrp_fmt(fmt)) { return (u32)b2r2_div_round_up(width * - b2r2_get_fmt_y_bpp(cont, fmt), 8); + b2r2_get_fmt_y_bpp(dev, fmt), 8); } else { - b2r2_log_err(cont->dev, "%s: Internal error! " + b2r2_log_err(dev, "%s: Internal error! " "Pitchless format supplied.\n", __func__); return 0; } } -u32 b2r2_get_img_pitch(struct b2r2_control *cont, struct b2r2_blt_img *img) +u32 b2r2_get_img_pitch(struct device *dev, struct b2r2_blt_img *img) { if (img->pitch != 0) return img->pitch; else - return b2r2_calc_pitch_from_width(cont, img->width, img->fmt); + return b2r2_calc_pitch_from_width(dev, img->width, img->fmt); } -s32 b2r2_get_img_size(struct b2r2_control *cont, struct b2r2_blt_img *img) +s32 b2r2_get_img_size(struct device *dev, struct b2r2_blt_img *img) { if (b2r2_is_single_plane_fmt(img->fmt)) { - return (s32)b2r2_get_img_pitch(cont, img) * img->height; + return (s32)b2r2_get_img_pitch(dev, img) * img->height; } else if (b2r2_is_ycbcrsp_fmt(img->fmt) || b2r2_is_ycbcrp_fmt(img->fmt)) { s32 y_plane_size; - y_plane_size = (s32)b2r2_get_img_pitch(cont, img) * img->height; + y_plane_size = (s32)b2r2_get_img_pitch(dev, img) * img->height; if (b2r2_is_ycbcr420_fmt(img->fmt)) { return y_plane_size + y_plane_size / 2; @@ -584,16 +584,16 @@ s32 b2r2_get_img_size(struct b2r2_control *cont, struct b2r2_blt_img *img) } else if (b2r2_is_ycbcr444_fmt(img->fmt)) { return y_plane_size * 3; } else { - b2r2_log_err(cont->dev, "%s: Internal error!" + b2r2_log_err(dev, "%s: Internal error!" " Format %#x not recognized.\n", __func__, img->fmt); return 0; } } else if (b2r2_is_mb_fmt(img->fmt)) { return (img->width * img->height * - b2r2_get_fmt_bpp(cont, img->fmt)) / 8; + b2r2_get_fmt_bpp(dev, img->fmt)) / 8; } else { - b2r2_log_err(cont->dev, "%s: Internal error! " + b2r2_log_err(dev, "%s: Internal error! " "Format %#x not recognized.\n", __func__, img->fmt); return 0; @@ -1097,3 +1097,228 @@ enum b2r2_fmt_type b2r2_get_fmt_type(enum b2r2_blt_fmt fmt) return B2R2_FMT_TYPE_RASTER; } } + +#ifdef CONFIG_DEBUG_FS +/** + * sprintf_req() - Builds a string representing the request, for debug + * + * @request:Request that should be encoded into a string + * @buf: Receiving buffer + * @size: Size of receiving buffer + * + * Returns number of characters in string, excluding null terminator + */ +int sprintf_req(struct b2r2_blt_request *request, char *buf, int size) +{ + size_t dev_size = 0; + + /* generic request info */ + dev_size += sprintf(buf + dev_size, + "instance : 0x%08lX\n", + (unsigned long) request->instance); + dev_size += sprintf(buf + dev_size, + "size : %d bytes\n", request->user_req.size); + dev_size += sprintf(buf + dev_size, + "flags : 0x%08lX\n", + (unsigned long) request->user_req.flags); + dev_size += sprintf(buf + dev_size, + "transform : %d\n", + (int) request->user_req.transform); + dev_size += sprintf(buf + dev_size, + "prio : %d\n", request->user_req.transform); + dev_size += sprintf(buf + dev_size, + "global_alpha : %d\n", + (int) request->user_req.global_alpha); + dev_size += sprintf(buf + dev_size, + "report1 : 0x%08lX\n", + (unsigned long) request->user_req.report1); + dev_size += sprintf(buf + dev_size, + "report2 : 0x%08lX\n", + (unsigned long) request->user_req.report2); + dev_size += sprintf(buf + dev_size, + "request_id : 0x%08lX\n\n", + (unsigned long) request->request_id); + + /* src info */ + dev_size += sprintf(buf + dev_size, + "src_img.fmt : %#010x\n", + request->user_req.src_img.fmt); + dev_size += sprintf(buf + dev_size, + "src_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d, " + "offset=%d, len=%d}\n", + request->user_req.src_img.buf.type, + request->user_req.src_img.buf.hwmem_buf_name, + request->user_req.src_img.buf.fd, + request->user_req.src_img.buf.offset, + request->user_req.src_img.buf.len); + dev_size += sprintf(buf + dev_size, + "src_img : {width=%d, height=%d, pitch=%d}\n", + request->user_req.src_img.width, + request->user_req.src_img.height, + request->user_req.src_img.pitch); + dev_size += sprintf(buf + dev_size, + "src_mask.fmt : %#010x\n", + request->user_req.src_mask.fmt); + dev_size += sprintf(buf + dev_size, + "src_mask.buf : {type=%d, hwmem_buf_name=%d, fd=%d," + " offset=%d, len=%d}\n", + request->user_req.src_mask.buf.type, + request->user_req.src_mask.buf.hwmem_buf_name, + request->user_req.src_mask.buf.fd, + request->user_req.src_mask.buf.offset, + request->user_req.src_mask.buf.len); + dev_size += sprintf(buf + dev_size, + "src_mask : {width=%d, height=%d, pitch=%d}\n", + request->user_req.src_mask.width, + request->user_req.src_mask.height, + request->user_req.src_mask.pitch); + dev_size += sprintf(buf + dev_size, + "src_rect : {x=%d, y=%d, width=%d, height=%d}\n", + request->user_req.src_rect.x, + request->user_req.src_rect.y, + request->user_req.src_rect.width, + request->user_req.src_rect.height); + dev_size += sprintf(buf + dev_size, + "src_color : 0x%08lX\n\n", + (unsigned long) request->user_req.src_color); + + /* bg info */ + dev_size += sprintf(buf + dev_size, + "bg_img.fmt : %#010x\n", + request->user_req.bg_img.fmt); + dev_size += sprintf(buf + dev_size, + "bg_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d," + " offset=%d, len=%d}\n", + request->user_req.bg_img.buf.type, + request->user_req.bg_img.buf.hwmem_buf_name, + request->user_req.bg_img.buf.fd, + request->user_req.bg_img.buf.offset, + request->user_req.bg_img.buf.len); + dev_size += sprintf(buf + dev_size, + "bg_img : {width=%d, height=%d, pitch=%d}\n", + request->user_req.bg_img.width, + request->user_req.bg_img.height, + request->user_req.bg_img.pitch); + dev_size += sprintf(buf + dev_size, + "bg_rect : {x=%d, y=%d, width=%d, height=%d}\n\n", + request->user_req.bg_rect.x, + request->user_req.bg_rect.y, + request->user_req.bg_rect.width, + request->user_req.bg_rect.height); + + /* dst info */ + dev_size += sprintf(buf + dev_size, + "dst_img.fmt : %#010x\n", + request->user_req.dst_img.fmt); + dev_size += sprintf(buf + dev_size, + "dst_img.buf : {type=%d, hwmem_buf_name=%d, fd=%d," + " offset=%d, len=%d}\n", + request->user_req.dst_img.buf.type, + request->user_req.dst_img.buf.hwmem_buf_name, + request->user_req.dst_img.buf.fd, + request->user_req.dst_img.buf.offset, + request->user_req.dst_img.buf.len); + dev_size += sprintf(buf + dev_size, + "dst_img : {width=%d, height=%d, pitch=%d}\n", + request->user_req.dst_img.width, + request->user_req.dst_img.height, + request->user_req.dst_img.pitch); + dev_size += sprintf(buf + dev_size, + "dst_rect : {x=%d, y=%d, width=%d, height=%d}\n", + request->user_req.dst_rect.x, + request->user_req.dst_rect.y, + request->user_req.dst_rect.width, + request->user_req.dst_rect.height); + dev_size += sprintf(buf + dev_size, + "dst_clip_rect : {x=%d, y=%d, width=%d, height=%d}\n", + request->user_req.dst_clip_rect.x, + request->user_req.dst_clip_rect.y, + request->user_req.dst_clip_rect.width, + request->user_req.dst_clip_rect.height); + dev_size += sprintf(buf + dev_size, + "dst_color : 0x%08lX\n\n", + (unsigned long) request->user_req.dst_color); + + dev_size += sprintf(buf + dev_size, + "src_resolved.physical : 0x%08lX\n", + (unsigned long) request->src_resolved. + physical_address); + dev_size += sprintf(buf + dev_size, + "src_resolved.virtual : 0x%08lX\n", + (unsigned long) request->src_resolved.virtual_address); + dev_size += sprintf(buf + dev_size, + "src_resolved.filep : 0x%08lX\n", + (unsigned long) request->src_resolved.filep); + dev_size += sprintf(buf + dev_size, + "src_resolved.filep_physical_start : 0x%08lX\n", + (unsigned long) request->src_resolved. + file_physical_start); + dev_size += sprintf(buf + dev_size, + "src_resolved.filep_virtual_start : 0x%08lX\n", + (unsigned long) request->src_resolved.file_virtual_start); + dev_size += sprintf(buf + dev_size, + "src_resolved.file_len : %d\n\n", + request->src_resolved.file_len); + + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.physical : 0x%08lX\n", + (unsigned long) request->src_mask_resolved. + physical_address); + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.virtual : 0x%08lX\n", + (unsigned long) request->src_mask_resolved.virtual_address); + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.filep : 0x%08lX\n", + (unsigned long) request->src_mask_resolved.filep); + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.filep_physical_start : 0x%08lX\n", + (unsigned long) request->src_mask_resolved. + file_physical_start); + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.filep_virtual_start : 0x%08lX\n", + (unsigned long) request->src_mask_resolved. + file_virtual_start); + dev_size += sprintf(buf + dev_size, + "src_mask_resolved.file_len : %d\n\n", + request->src_mask_resolved.file_len); + + dev_size += sprintf(buf + dev_size, + "dst_resolved.physical : 0x%08lX\n", + (unsigned long) request->dst_resolved. + physical_address); + dev_size += sprintf(buf + dev_size, + "dst_resolved.virtual : 0x%08lX\n", + (unsigned long) request->dst_resolved.virtual_address); + dev_size += sprintf(buf + dev_size, + "dst_resolved.filep : 0x%08lX\n", + (unsigned long) request->dst_resolved.filep); + dev_size += sprintf(buf + dev_size, + "dst_resolved.filep_physical_start : 0x%08lX\n", + (unsigned long) request->dst_resolved. + file_physical_start); + dev_size += sprintf(buf + dev_size, + "dst_resolved.filep_virtual_start : 0x%08lX\n", + (unsigned long) request->dst_resolved.file_virtual_start); + dev_size += sprintf(buf + dev_size, + "dst_resolved.file_len : %d\n\n", + request->dst_resolved.file_len); + + return dev_size; +} +#endif + +void b2r2_recalculate_rects(struct device *dev, + struct b2r2_blt_req *req) +{ + struct b2r2_blt_rect new_dst_rect; + struct b2r2_blt_rect new_src_rect; + struct b2r2_blt_rect new_bg_rect; + + b2r2_trim_rects(dev, + req, &new_bg_rect, &new_dst_rect, &new_src_rect); + + req->dst_rect = new_dst_rect; + req->src_rect = new_src_rect; + if (req->flags & B2R2_BLT_FLAG_BG_BLEND) + req->bg_rect = new_bg_rect; +} diff --git a/drivers/video/b2r2/b2r2_utils.h b/drivers/video/b2r2/b2r2_utils.h index e639e454121..081ac1f4848 100644 --- a/drivers/video/b2r2/b2r2_utils.h +++ b/drivers/video/b2r2/b2r2_utils.h @@ -17,7 +17,7 @@ extern const s32 b2r2_s32_max; -int calculate_scale_factor(struct b2r2_control *cont, +int calculate_scale_factor(struct device *dev, u32 from, u32 to, u16 *sf_out); void b2r2_get_img_bounding_rect(struct b2r2_blt_img *img, struct b2r2_blt_rect *bounding_rect); @@ -30,14 +30,14 @@ bool b2r2_is_rect_gte_rect(struct b2r2_blt_rect *rect1, void b2r2_intersect_rects(struct b2r2_blt_rect *rect1, struct b2r2_blt_rect *rect2, struct b2r2_blt_rect *intersection); -void b2r2_trim_rects(struct b2r2_control *cont, +void b2r2_trim_rects(struct device *dev, const struct b2r2_blt_req *req, struct b2r2_blt_rect *new_bg_rect, struct b2r2_blt_rect *new_dst_rect, struct b2r2_blt_rect *new_src_rect); -int b2r2_get_fmt_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt); -int b2r2_get_fmt_y_bpp(struct b2r2_control *cont, enum b2r2_blt_fmt fmt); +int b2r2_get_fmt_bpp(struct device *dev, enum b2r2_blt_fmt fmt); +int b2r2_get_fmt_y_bpp(struct device *dev, enum b2r2_blt_fmt fmt); bool b2r2_is_single_plane_fmt(enum b2r2_blt_fmt fmt); bool b2r2_is_independent_pixel_fmt(enum b2r2_blt_fmt fmt); @@ -52,11 +52,11 @@ bool b2r2_is_mb_fmt(enum b2r2_blt_fmt fmt); /* * Rounds up if an invalid width causes the pitch to be non byte aligned. */ -u32 b2r2_calc_pitch_from_width(struct b2r2_control *cont, +u32 b2r2_calc_pitch_from_width(struct device *dev, s32 width, enum b2r2_blt_fmt fmt); -u32 b2r2_get_img_pitch(struct b2r2_control *cont, +u32 b2r2_get_img_pitch(struct device *dev, struct b2r2_blt_img *img); -s32 b2r2_get_img_size(struct b2r2_control *cont, +s32 b2r2_get_img_size(struct device *dev, struct b2r2_blt_img *img); s32 b2r2_div_round_up(s32 dividend, s32 divisor); @@ -80,5 +80,10 @@ int b2r2_fmt_byte_pitch(enum b2r2_blt_fmt fmt, u32 width); enum b2r2_native_fmt b2r2_to_native_fmt(enum b2r2_blt_fmt fmt); u32 b2r2_to_RGB888(u32 color, const enum b2r2_blt_fmt fmt); enum b2r2_fmt_type b2r2_get_fmt_type(enum b2r2_blt_fmt fmt); +#ifdef CONFIG_DEBUG_FS +int sprintf_req(struct b2r2_blt_request *request, char *buf, int size); +#endif +void b2r2_recalculate_rects(struct device *dev, + struct b2r2_blt_req *req); #endif |