diff options
author | Jorgen Nilsson <jorgen.nilsson@stericsson.com> | 2012-02-06 17:04:49 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:04:28 +0200 |
commit | 57716d2b65bf5fa56f50a5f3d4ebb3a35b2483ab (patch) | |
tree | 8b47440bad8bf338431b9baf62ba737ec34b5d95 /drivers/video | |
parent | 809e8bdcb464d4be59d04825873f41ecebf9dc28 (diff) |
video: [ANDROID]: b2r2: Add kernel API to b2r2 and more
Brief:
As a preparation for the ICS hardware composer solution a
kernel API has been added to the b2r2 driver. Some minor
stability fixes are also part of this release. The full
support for dual b2r2 configuration should be in place,
but has yet to be verified with the apropriate hardware.
The change should have minor impact on the single core
solution.
Details:
A new logical device for handling the blitter requests
has been added. This is roughly the same device as the old
blitter the interfaces are therefore the same. From now there
is at least two devices - one blitter device and then for each
core one additional device (dual configuration is the current
target). As a consequence the maximum number of allowed cores
has been raised from 1 to 2.
When a dual configuration is active the b2r2 driver
seamlessly splits requests over available b2r2 cores. Each
request is assigned a core_mask defining which cores are doing
part of the job. This helps keep track when waiting for the reports.
Also, rectangles are now recalculated (based on intersection
of destination rectangle and destination image etc.) before
splitting the job in order to maximize throughput.
Clock and power source can now be configured from
devices-dbxxxx.c or equivalent.
Minor bug fixes:
- Check the return value from clk_enable
- Check if the core is valid before serving a IRQ.
- Check if the core is enabled (domain_enabled) before
adding a new job.
- Return actual job_id used for a request (previously
always 0)
ST-Ericsson ID: 404691
Original Change-Id: Ic3357c4c1e792a84077c1be4ba4c5acdb19da266
Signed-off-by: Jorgen Nilsson <jorgen.nilsson@stericsson.com>
Diffstat (limited to 'drivers/video')
-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 |