summaryrefslogtreecommitdiff
path: root/drivers/media/video/tiler
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/tiler')
-rw-r--r--drivers/media/video/tiler/Kconfig6
-rw-r--r--drivers/media/video/tiler/Makefile4
-rw-r--r--drivers/media/video/tiler/tcm/Makefile2
-rw-r--r--drivers/media/video/tiler/tcm/_tcm_sita.h91
-rw-r--r--drivers/media/video/tiler/tcm/tcm.h352
-rw-r--r--drivers/media/video/tiler/tcm/tcm_sita.c1358
-rw-r--r--drivers/media/video/tiler/tcm/tcm_sita.h39
-rw-r--r--drivers/media/video/tiler/tcm/tcm_utils.h59
-rw-r--r--drivers/media/video/tiler/tiler.c1583
-rw-r--r--drivers/media/video/tiler/tiler_def.h158
-rw-r--r--drivers/media/video/tiler/tiler_pack.c269
-rw-r--r--drivers/media/video/tiler/tiler_rot.c239
12 files changed, 4160 insertions, 0 deletions
diff --git a/drivers/media/video/tiler/Kconfig b/drivers/media/video/tiler/Kconfig
new file mode 100644
index 000000000000..fabbb59a6c8d
--- /dev/null
+++ b/drivers/media/video/tiler/Kconfig
@@ -0,0 +1,6 @@
+config TILER_OMAP
+ tristate "OMAP TILER support"
+ default y
+ help
+ TILER driver for OMAP based boards.
+
diff --git a/drivers/media/video/tiler/Makefile b/drivers/media/video/tiler/Makefile
new file mode 100644
index 000000000000..e6dbe24ce9d3
--- /dev/null
+++ b/drivers/media/video/tiler/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_TILER_OMAP) += tcm/
+obj-$(CONFIG_TILER_OMAP) += tiler_omap.o
+tiler_omap-objs = tiler.o tiler_pack.o tiler_rot.o
+
diff --git a/drivers/media/video/tiler/tcm/Makefile b/drivers/media/video/tiler/tcm/Makefile
new file mode 100644
index 000000000000..f03f3b7c862f
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TILER_OMAP) += tcm_sita.o
+
diff --git a/drivers/media/video/tiler/tcm/_tcm_sita.h b/drivers/media/video/tiler/tcm/_tcm_sita.h
new file mode 100644
index 000000000000..75de584c747d
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/_tcm_sita.h
@@ -0,0 +1,91 @@
+/*
+ * _tcm_sita.h
+ *
+ * SImple Tiler Allocator (SiTA) private structures.
+ *
+ * Author: Ravi Ramachandra <r.ramachandra@ti.com>
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _TCM_SITA_H_
+#define _TCM_SITA_H_
+
+#include "tcm.h"
+
+#define TL_CORNER 0
+#define TR_CORNER 1
+#define BL_CORNER 3
+#define BR_CORNER 4
+
+/*Provide inclusive length between co-ordinates */
+#define INCL_LEN(high, low) ((high) - (low) + 1)
+#define INCL_LEN_MOD(start, end) ((start) > (end) ? (start) - (end) + 1 : \
+ (end) - (start) + 1)
+
+#define BOUNDARY(stat) ((stat)->top_boundary + (stat)->bottom_boundary + \
+ (stat)->left_boundary + (stat)->right_boundary)
+#define OCCUPIED(stat) ((stat)->top_occupied + (stat)->bottom_occupied + \
+ (stat)->left_occupied + (stat)->right_occupied)
+
+enum Criteria {
+ CR_MAX_NEIGHS = 0x01,
+ CR_FIRST_FOUND = 0x10,
+ CR_BIAS_HORIZONTAL = 0x20,
+ CR_BIAS_VERTICAL = 0x40,
+ CR_DIAGONAL_BALANCE = 0x80
+};
+
+struct nearness_factor {
+ s32 x;
+ s32 y;
+};
+
+/*
+ * Area info kept
+ */
+struct area_spec {
+ struct tcm_area area;
+ struct list_head list;
+};
+
+/*
+ * Everything is a rectangle with four sides and on
+ * each side you could have a boundary or another Tile.
+ * The tile could be Occupied or Not. These info is stored
+ */
+struct neighbour_stats {
+ u16 left_boundary;
+ u16 left_occupied;
+ u16 top_boundary;
+ u16 top_occupied;
+ u16 right_boundary;
+ u16 right_occupied;
+ u16 bottom_boundary;
+ u16 bottom_occupied;
+};
+
+struct slot {
+ u8 busy; /* is slot occupied */
+ struct tcm_area parent; /* parent area */
+ u32 reserved;
+};
+
+struct sita_pvt {
+ u16 width;
+ u16 height;
+ struct list_head res; /* all allocations */
+ struct mutex mtx;
+ struct tcm_pt div_pt; /* divider point splitting container */
+ struct slot **map; /* container slots */
+};
+
+#endif /* _TCM_SITA_H_ */
diff --git a/drivers/media/video/tiler/tcm/tcm.h b/drivers/media/video/tiler/tcm/tcm.h
new file mode 100644
index 000000000000..d205dad32a46
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/tcm.h
@@ -0,0 +1,352 @@
+/*
+ * tcm.h
+ *
+ * TILER container manager specification and support functions for TI
+ * processors.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _TCM_H_
+#define _TCM_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+struct tcm;
+
+struct tcm_pt {
+ u16 x;
+ u16 y;
+};
+
+struct tcm_area {
+ bool is2d; /* whether are is 1d or 2d */
+ struct tcm *tcm; /* parent */
+ struct tcm_pt p0;
+ struct tcm_pt p1;
+};
+
+struct tcm {
+ u16 width, height; /* container dimensions */
+
+ /* 'pvt' structure shall contain any tcm details (attr) along with
+ linked list of allocated areas and mutex for mutually exclusive access
+ to the list. It may also contain copies of width and height to notice
+ any changes to the publicly available width and height fields. */
+ void *pvt;
+
+ /* function table */
+ s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u8 align,
+ struct tcm_area *area);
+ s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
+ s32 (*free) (struct tcm *tcm, struct tcm_area *area);
+ s32 (*get_parent)(struct tcm *tcm, struct tcm_pt *pt,
+ struct tcm_area *area);
+ void (*deinit) (struct tcm *tcm);
+};
+
+/*=============================================================================
+ BASIC TILER CONTAINER MANAGER INTERFACE
+=============================================================================*/
+
+/*
+ * NOTE:
+ *
+ * Since some basic parameter checking is done outside the TCM algorithms,
+ * TCM implementation do NOT have to check the following:
+ *
+ * area pointer is NULL
+ * width and height fits within container
+ * number of pages is more than the size of the container
+ *
+ */
+
+/**
+ * Template for <ALGO_NAME>_tcm_init method. Define as:
+ * TCM_INIT(<ALGO_NAME>_tcm_init)
+ *
+ * Allocates and initializes a tiler container manager.
+ *
+ * @param width Width of container
+ * @param height Height of container
+ * @param attr Container manager specific configuration
+ * arguments. Please describe these in
+ * your header file.
+ *
+ * @return Pointer to the allocated and initialized container
+ * manager. NULL on failure. DO NOT leak any memory on
+ * failure!
+ */
+#define TCM_INIT(name, attr_t) \
+struct tcm *name(u16 width, u16 height, typeof(attr_t) *attr);
+
+/**
+ * Deinitialize tiler container manager.
+ *
+ * @author Ravi Ramachandra (3/1/2010)
+ *
+ * @param tcm Pointer to container manager.
+ *
+ * @return 0 on success, non-0 error value on error. The call
+ * should free as much memory as possible and meaningful
+ * even on failure. Some error codes: -ENODEV: invalid
+ * manager.
+ */
+static inline void tcm_deinit(struct tcm *tcm)
+{
+ if (tcm)
+ tcm->deinit(tcm);
+}
+
+/**
+ * Reserves a 2D area in the container.
+ *
+ * @author Ravi Ramachandra (3/1/2010)
+ *
+ * @param tcm Pointer to container manager.
+ * @param height Height(in pages) of area to be reserved.
+ * @param width Width(in pages) of area to be reserved.
+ * @param align Alignment requirement for top-left corner of area. Not
+ * all values may be supported by the container manager,
+ * but it must support 0 (1), 32 and 64.
+ * 0 value is equivalent to 1.
+ * @param area Pointer to where the reserved area should be stored.
+ *
+ * @return 0 on success. Non-0 error code on failure. Also,
+ * the tcm field of the area will be set to NULL on
+ * failure. Some error codes: -ENODEV: invalid manager,
+ * -EINVAL: invalid area, -ENOMEM: not enough space for
+ * allocation.
+ */
+static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
+ u16 align, struct tcm_area *area)
+{
+ /* perform rudimentary error checking */
+ s32 res = (tcm == NULL ? -ENODEV :
+ area == NULL ? -EINVAL :
+ (height > tcm->height || width > tcm->width) ? -ENOMEM :
+ tcm->reserve_2d(tcm, height, width, align, area));
+
+ if (area)
+ area->tcm = res ? NULL : tcm;
+
+ return res;
+}
+
+/**
+ * Reserves a 1D area in the container.
+ *
+ * @author Ravi Ramachandra (3/1/2010)
+ *
+ * @param tcm Pointer to container manager.
+ * @param slots Number of (contiguous) slots to reserve.
+ * @param area Pointer to where the reserved area should be stored.
+ *
+ * @return 0 on success. Non-0 error code on failure. Also,
+ * the tcm field of the area will be set to NULL on
+ * failure. Some error codes: -ENODEV: invalid manager,
+ * -EINVAL: invalid area, -ENOMEM: not enough space for
+ * allocation.
+ */
+static inline s32 tcm_reserve_1d(struct tcm *tcm, u32 slots,
+ struct tcm_area *area)
+{
+ /* perform rudimentary error checking */
+ s32 res = (tcm == NULL ? -ENODEV :
+ area == NULL ? -EINVAL :
+ slots > (tcm->width * (u32) tcm->height) ? -ENOMEM :
+ tcm->reserve_1d(tcm, slots, area));
+
+ if (area)
+ area->tcm = res ? NULL : tcm;
+
+ return res;
+}
+
+/**
+ * Free a previously reserved area from the container.
+ *
+ * @author Ravi Ramachandra (3/1/2010)
+ *
+ * @param area Pointer to area reserved by a prior call to
+ * tcm_reserve_1d or tcm_reserve_2d call, whether
+ * it was successful or not. (Note: all fields of
+ * the structure must match.)
+ *
+ * @return 0 on success. Non-0 error code on failure. Also, the tcm
+ * field of the area is set to NULL on success to avoid subsequent
+ * freeing. This call will succeed even if supplying
+ * the area from a failed reserved call.
+ */
+static inline s32 tcm_free(struct tcm_area *area)
+{
+ s32 res = 0; /* free succeeds by default */
+
+ if (area && area->tcm) {
+ res = area->tcm->free(area->tcm, area);
+ if (res == 0)
+ area->tcm = NULL;
+ }
+
+ return res;
+}
+
+
+/**
+ * Retrieves the parent area (1D or 2D) for a given co-ordinate in the
+ * container.
+ *
+ * @author Ravi Ramachandra (3/1/2010)
+ *
+ * @param tcm Pointer to container manager.
+ * @param pt Pointer to the coordinates of a slot in the container.
+ * @param area Pointer to where the reserved area should be stored.
+ *
+ * @return 0 on success. Non-0 error code on failure. Also,
+ * the tcm field of the area will be set to NULL on
+ * failure. Some error codes: -ENODEV: invalid manager,
+ * -EINVAL: invalid area, -ENOENT: coordinate is not part of any
+ * active area.
+ */
+static inline s32 tcm_get_parent(struct tcm *tcm, struct tcm_pt *pt,
+ struct tcm_area *area)
+{
+ s32 res = (tcm == NULL ? -ENODEV :
+ area == NULL ? -EINVAL :
+ (pt->x >= tcm->width || pt->y >= tcm->height) ? -ENOENT :
+ tcm->get_parent(tcm, pt, area));
+
+ if (area)
+ area->tcm = res ? NULL : tcm;
+
+ return res;
+}
+
+/*=============================================================================
+ HELPER FUNCTION FOR ANY TILER CONTAINER MANAGER
+=============================================================================*/
+
+/**
+ * This method slices off the topmost 2D slice from the parent area, and stores
+ * it in the 'slice' parameter. The 'parent' parameter will get modified to
+ * contain the remaining portion of the area. If the whole parent area can
+ * fit in a 2D slice, its tcm pointer is set to NULL to mark that it is no
+ * longer a valid area.
+ *
+ * @author Lajos Molnar (3/17/2010)
+ *
+ * @param parent Pointer to a VALID parent area that will get modified
+ * @param slice Pointer to the slice area that will get modified
+ */
+static inline void tcm_slice(struct tcm_area *parent, struct tcm_area *slice)
+{
+ *slice = *parent;
+
+ /* check if we need to slice */
+ if (slice->tcm && !slice->is2d &&
+ slice->p0.y != slice->p1.y &&
+ (slice->p0.x || (slice->p1.x != slice->tcm->width - 1))) {
+ /* set end point of slice (start always remains) */
+ slice->p1.x = slice->tcm->width - 1;
+ slice->p1.y = (slice->p0.x) ? slice->p0.y : slice->p1.y - 1;
+ /* adjust remaining area */
+ parent->p0.x = 0;
+ parent->p0.y = slice->p1.y + 1;
+ } else {
+ /* mark this as the last slice */
+ parent->tcm = NULL;
+ }
+}
+
+/**
+ * Verifies if a tcm area is logically valid.
+ *
+ * @param area Pointer to tcm area
+ *
+ * @return TRUE if area is logically valid, FALSE otherwise.
+ */
+static inline bool tcm_area_is_valid(struct tcm_area *area)
+{
+ return (area && area->tcm &&
+ /* coordinate bounds */
+ area->p1.x < area->tcm->width &&
+ area->p1.y < area->tcm->height &&
+ area->p0.y <= area->p1.y &&
+ /* 1D coordinate relationship + p0.x check */
+ ((!area->is2d &&
+ area->p0.x < area->tcm->width &&
+ area->p0.x + area->p0.y * area->tcm->width <=
+ area->p1.x + area->p1.y * area->tcm->width) ||
+ /* 2D coordinate relationship */
+ (area->is2d &&
+ area->p0.x <= area->p1.x))
+ );
+}
+
+/* see if a coordinate is within an area */
+static inline bool __tcm_is_in(struct tcm_pt *p, struct tcm_area *a)
+{
+ u16 i;
+
+ if (a->is2d) {
+ return p->x >= a->p0.x && p->x <= a->p1.x &&
+ p->y >= a->p0.y && p->y <= a->p1.y;
+ } else {
+ i = p->x + p->y * a->tcm->width;
+ return i >= a->p0.x + a->p0.y * a->tcm->width &&
+ i <= a->p1.x + a->p1.y * a->tcm->width;
+ }
+}
+
+/* calculate area width */
+static inline u16 __tcm_area_width(struct tcm_area *area)
+{
+ return area->p1.x - area->p0.x + 1;
+}
+
+/* calculate area height */
+static inline u16 __tcm_area_height(struct tcm_area *area)
+{
+ return area->p1.y - area->p0.y + 1;
+}
+
+/* calculate number of slots in an area */
+static inline u16 __tcm_sizeof(struct tcm_area *area)
+{
+ return area->is2d ?
+ __tcm_area_width(area) * __tcm_area_height(area) :
+ (area->p1.x - area->p0.x + 1) + (area->p1.y - area->p0.y) *
+ area->tcm->width;
+}
+#define tcm_sizeof(area) __tcm_sizeof(&(area))
+#define tcm_awidth(area) __tcm_area_width(&(area))
+#define tcm_aheight(area) __tcm_area_height(&(area))
+#define tcm_is_in(pt, area) __tcm_is_in(&(pt), &(area))
+
+/**
+ * Iterate through 2D slices of a valid area. Behaves
+ * syntactically as a for(;;) statement.
+ *
+ * @param var Name of a local variable of type 'struct
+ * tcm_area *' that will get modified to
+ * contain each slice.
+ * @param area Pointer to the VALID parent area. This
+ * structure will not get modified
+ * throughout the loop.
+ *
+ */
+#define tcm_for_each_slice(var, area, safe) \
+ for (safe = area, \
+ tcm_slice(&safe, &var); \
+ var.tcm; tcm_slice(&safe, &var))
+
+#endif /* _TCM_H_ */
diff --git a/drivers/media/video/tiler/tcm/tcm_sita.c b/drivers/media/video/tiler/tcm/tcm_sita.c
new file mode 100644
index 000000000000..0aea8f12be21
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/tcm_sita.c
@@ -0,0 +1,1358 @@
+/*
+ * tcm_sita.c
+ *
+ * Author: Ravi Ramachandra <r.ramachandra@ti.com>
+ *
+ * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#include <linux/slab.h>
+
+#include "_tcm_sita.h"
+#include "tcm_sita.h"
+
+#define TCM_ALG_NAME "tcm_sita"
+#include "tcm_utils.h"
+
+#define X_SCAN_LIMITER 1
+#define Y_SCAN_LIMITER 1
+
+#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
+
+/* Individual selection criteria for different scan areas */
+static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
+static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
+#ifdef SCAN_BOTTOM_UP
+static s32 CR_R2L_B2T = CR_FIRST_FOUND;
+static s32 CR_L2R_B2T = CR_DIAGONAL_BALANCE;
+#endif
+
+/*********************************************
+ * TCM API - Sita Implementation
+ *********************************************/
+static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
+ struct tcm_area *area);
+static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area
+ *area);
+static s32 sita_free(struct tcm *tcm, struct tcm_area *to_be_removed_area);
+static s32 sita_get_parent(struct tcm *tcm, struct tcm_pt *pt,
+ struct tcm_area *area);
+static void sita_deinit(struct tcm *tcm);
+
+/*********************************************
+ * Main Scanner functions
+ *********************************************/
+static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *area);
+
+static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area);
+
+static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area);
+
+#ifdef SCAN_BOTTOM_UP
+static s32 scan_l2r_b2t(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area);
+
+static s32 scan_r2l_b2t(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area);
+#endif
+static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_pages,
+ struct tcm_area *field, struct tcm_area *area);
+
+/*********************************************
+ * Support Infrastructure Methods
+ *********************************************/
+static s32 check_fit_r_and_b(struct tcm *tcm, u16 w, u16 h, u16 left_x,
+ u16 top_y);
+
+static s32 check_fit_r_one_dim(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *busy_x, u16 *busy_y);
+
+static void select_candidate(struct tcm *tcm, u16 w, u16 h,
+ struct list_head *maybes, struct tcm_area *field,
+ s32 criteria, struct tcm_area *area);
+
+static void get_nearness_factor(struct tcm_area *field,
+ struct tcm_area *candidate, struct nearness_factor *nf);
+
+static s32 get_busy_neigh_stats(struct tcm *tcm, u16 width, u16 height,
+ struct tcm_area *top_left_corner,
+ struct neighbour_stats *neighbour_stat);
+
+static void fill_1d_area(struct tcm *tcm,
+ struct tcm_area *area, struct slot slot);
+
+static void fill_2d_area(struct tcm *tcm,
+ struct tcm_area *area, struct slot slot);
+
+static s32 move_left(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *xx, u16 *yy);
+static s32 move_right(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *xx, u16 *yy);
+/*********************************************/
+
+/*********************************************
+ * Utility Methods
+ *********************************************/
+
+/* TODO: check if element allocation succeeded */
+
+/* insert a given area at the end of a given list */
+static
+struct area_spec *insert_element(struct list_head *head, struct tcm_area *area)
+{
+ struct area_spec *elem;
+
+ elem = kmalloc(sizeof(*elem), GFP_KERNEL);
+ if (elem) {
+ elem->area = *area;
+ list_add_tail(&elem->list, head);
+ }
+ return elem;
+}
+
+static
+s32 rem_element_with_match(struct list_head *head,
+ struct tcm_area *area, u16 *is2d)
+{
+ struct area_spec *elem = NULL;
+
+ /*If the area to be removed matchs the list head itself,
+ we need to put the next one as list head */
+ list_for_each_entry(elem, head, list) {
+ if (elem->area.p0.x == area->p0.x
+ && elem->area.p0.y == area->p0.y
+ && elem->area.p1.x == area->p1.x
+ && elem->area.p1.y == area->p1.y) {
+
+ *is2d = elem->area.is2d;
+ list_del(&elem->list);
+
+ kfree(elem);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static
+void clean_list(struct list_head *head)
+{
+ struct area_spec *elem = NULL, *elem_ = NULL;
+
+ list_for_each_entry_safe(elem, elem_, head, list) {
+ list_del(&elem->list);
+ kfree(elem);
+ }
+}
+
+#if 0
+static
+void dump_list_entries(struct list_head *head)
+{
+ struct area_spec *elem = NULL;
+
+ P1("Printing List Entries:\n");
+
+ list_for_each_entry(elem, head, list) {
+ printk(KERN_NOTICE "%dD:" AREA_FMT "\n", elem->area.type,
+ AREA(elem->area));
+ }
+
+ P1("List Finished\n");
+}
+
+static
+s32 dump_neigh_stats(struct neighbour_stats *neighbour)
+{
+ P1("Top Occ:Boundary %d:%d\n", neighbour->top_occupied,
+ neighbour->top_boundary);
+ P1("Bot Occ:Boundary %d:%d\n", neighbour->bottom_occupied,
+ neighbour->bottom_boundary);
+ P1("Left Occ:Boundary %d:%d\n", neighbour->left_occupied,
+ neighbour->left_boundary);
+ P1("Rigt Occ:Boundary %d:%d\n", neighbour->right_occupied,
+ neighbour->right_boundary);
+ return 0;
+}
+#endif
+
+struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
+{
+ struct tcm *tcm = NULL;
+ struct sita_pvt *pvt = NULL;
+ struct slot init_tile = {0};
+ struct tcm_area area = {0};
+ s32 i = 0;
+
+ if (width == 0 || height == 0)
+ goto error;
+
+ tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
+ pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
+ if (!tcm || !pvt)
+ goto error;
+
+ memset(tcm, 0, sizeof(*tcm));
+ memset(pvt, 0, sizeof(*pvt));
+
+ /* Updating the pointers to SiTA implementation APIs */
+ tcm->height = height;
+ tcm->width = width;
+ tcm->reserve_2d = sita_reserve_2d;
+ tcm->reserve_1d = sita_reserve_1d;
+ tcm->get_parent = sita_get_parent;
+ tcm->free = sita_free;
+ tcm->deinit = sita_deinit;
+ tcm->pvt = (void *)pvt;
+
+ INIT_LIST_HEAD(&pvt->res);
+ pvt->height = height;
+ pvt->width = width;
+
+ mutex_init(&(pvt->mtx));
+
+ /* Creating tam map */
+ pvt->map = kmalloc(sizeof(*pvt->map) * pvt->width, GFP_KERNEL);
+
+ if (!pvt->map)
+ goto error;
+
+ for (i = 0; i < pvt->width; i++) {
+ pvt->map[i] =
+ kmalloc(sizeof(**pvt->map) * pvt->height,
+ GFP_KERNEL);
+ if (pvt->map[i] == NULL) {
+ while (i--)
+ kfree(pvt->map[i]);
+ kfree(pvt->map);
+ goto error;
+ }
+ }
+
+ if (attr && attr->x <= pvt->width && attr->y <= pvt->height) {
+ pvt->div_pt.x = attr->x;
+ pvt->div_pt.y = attr->y;
+
+ } else {
+ /* Defaulting to 3:1 ratio on width for 2D area split */
+ /* Defaulting to 3:1 ratio on height for 2D and 1D split */
+ pvt->div_pt.x = (pvt->width * 3) / 4;
+ pvt->div_pt.y = (pvt->height * 3) / 4;
+ }
+
+ area.p1.x = width - 1;
+ area.p1.y = height - 1;
+
+ mutex_lock(&(pvt->mtx));
+ fill_2d_area(tcm, &area, init_tile);
+ mutex_unlock(&(pvt->mtx));
+ return tcm;
+
+error:
+ kfree(tcm);
+ kfree(pvt);
+ return NULL;
+}
+
+static void sita_deinit(struct tcm *tcm)
+{
+ struct slot init_tile = {0};
+ struct sita_pvt *pvt = NULL;
+ struct tcm_area area = {0};
+ s32 i = 0;
+
+ pvt = (struct sita_pvt *)tcm->pvt;
+ if (pvt) {
+ area.p1.x = pvt->width - 1;
+ area.p1.y = pvt->height - 1;
+
+ mutex_lock(&(pvt->mtx));
+ fill_2d_area(tcm, &area, init_tile);
+ mutex_unlock(&(pvt->mtx));
+
+ mutex_destroy(&(pvt->mtx));
+
+ for (i = 0; i < pvt->height; i++) {
+ kfree(pvt->map[i]);
+ pvt->map[i] = NULL;
+ }
+ kfree(pvt->map);
+ pvt->map = NULL;
+ kfree(pvt);
+ }
+}
+
+/**
+ * @description: Allocate 1d pages if the required number of pages are
+ * available in the container
+ *
+ * @input:num_pages to be allocated
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * area contain co-ordinates of start and end Tiles(inclusive)
+ */
+static s32 sita_reserve_1d(struct tcm *tcm, u32 num_pages,
+ struct tcm_area *area)
+{
+ s32 ret = 0;
+ struct tcm_area field = {0};
+ struct slot slot = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ area->is2d = false;
+
+ mutex_lock(&(pvt->mtx));
+#ifdef RESTRICT_1D
+ /* scan within predefined 1D boundary */
+ assign(&field, pvt->width - 1, pvt->height - 1, 0, pvt->div_pt.y);
+#else
+ /* Scanning entire container */
+ assign(&field, pvt->width - 1, pvt->height - 1, 0, 0);
+#endif
+ ret = scan_r2l_b2t_one_dim(tcm, num_pages,
+ &field, area);
+ /* There is not much to select, we pretty much give the first one
+ which accomodates */
+ if (!ret) {
+ slot.busy = true;
+ slot.parent = *area;
+ /* inserting into tiler container */
+ fill_1d_area(tcm, area, slot);
+ /* updating the list of allocations */
+ insert_element(&pvt->res, area);
+ }
+ mutex_unlock(&(pvt->mtx));
+ return ret;
+}
+
+/**
+ * @description: Allocate 2d area on availability in the container
+ *
+ * @input:'w'idth and 'h'eight of the 2d area, 'align'ment specification
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * area contain co-ordinates of TL corner Tile and BR corner Tile of
+ * the rectangle (inclusive)
+ */
+static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
+ struct tcm_area *area)
+{
+ s32 ret = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ /* we only support 1, 32 and 64 as alignment */
+ u16 stride = align <= 1 ? 1 : align <= 32 ? 32 : 64;
+ struct slot slot = {0};
+
+ area->is2d = true;
+
+ /* align must be 2 power */
+ if (align & (align - 1) || align > 64)
+ return -EINVAL;
+
+ mutex_lock(&(pvt->mtx));
+ ret = scan_areas_and_find_fit(tcm, w, h, stride, area);
+ if (!ret) {
+ slot.busy = true;
+ slot.parent = *area;
+
+ fill_2d_area(tcm, area, slot);
+ insert_element(&(pvt->res), area);
+ }
+ mutex_unlock(&(pvt->mtx));
+ return ret;
+}
+
+/**
+ * @description: unreserve 2d or 1D allocations if previously allocated
+ *
+ * @input:'area' specification: for 2D this should contain
+ * TL Corner and BR Corner of the 2D area, or for 1D allocation this should
+ * contain the start and end Tiles
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the to_be_removed_area is removed from g_allocation_list and the
+ * corresponding tiles are marked 'NOT_OCCUPIED'
+ *
+ */
+static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
+{
+ s32 ret = 0;
+ struct slot slot = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ u16 is2d;
+
+ slot.busy = false;
+ mutex_lock(&(pvt->mtx));
+ /*First we check if the given Area is aleast valid in our list*/
+ ret = rem_element_with_match(&(pvt->res), area, &is2d);
+
+ /* If we found a positive match & removed the area details from list
+ * then we clear the contents of the associated tiles in the global
+ * container*/
+ if (!ret) {
+ if (is2d)
+ fill_2d_area(tcm, area, slot);
+ else
+ fill_1d_area(tcm, area, slot);
+ }
+ mutex_unlock(&(pvt->mtx));
+ return ret;
+}
+
+/**
+ * @description: raster scan right to left from top to bottom; find if there is
+ * a free area to fit a given w x h inside the 'scan area'. If there is a free
+ * area, then adds to maybes candidates, which later is sent for selection
+ * as per pre-defined criteria.
+ *
+ * @input:'w x h' width and height of the allocation area.
+ * 'stride' - 64/32/None for start address alignment
+ * 'field' - area in which the scan operation should take place
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the 'area' area contains TL and BR corners of the allocated area
+ *
+ */
+static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area)
+{
+ s32 xx = 0, yy = 0;
+ s16 start_x = -1, end_x = -1, start_y = -1, end_y = -1;
+ s16 found_x = -1, found_y = -1;
+ LIST_HEAD(maybes);
+ struct tcm_area candidate = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "scan_r2l_t2b:", field);
+
+ start_x = field->p0.x;
+ end_x = field->p1.x;
+ start_y = field->p0.y;
+ end_y = field->p1.y;
+
+ /* check scan area co-ordinates */
+ if (field->p0.x < field->p1.x ||
+ field->p1.y < field->p0.y)
+ return -EINVAL;
+
+ /* check if allocation would fit in scan area */
+ if (w > INCL_LEN(start_x, end_x) || h > INCL_LEN(end_y, start_y))
+ return -ENOSPC;
+
+ /* adjust start_x and end_y, as allocation would not fit beyond */
+ start_x = ALIGN_DOWN(start_x - w + 1, stride); /* - 1 to be inclusive */
+ end_y = end_y - h + 1;
+
+ /* check if allocation would still fit in scan area */
+ if (start_x < end_x)
+ return -ENOSPC;
+
+ P2("ali=%d x=%d..%d y=%d..%d", stride, start_x, end_x, start_y, end_y);
+
+ /*
+ * Start scanning: These scans are always inclusive ones so if we are
+ * given a start x = 0 is a valid value so if we have a end_x = 255,
+ * 255th element is also checked
+ */
+ for (yy = start_y; yy <= end_y; yy++) {
+ for (xx = start_x; xx >= end_x; xx -= stride) {
+ if (!pvt->map[xx][yy].busy) {
+ if (check_fit_r_and_b(tcm, w, h, xx, yy)) {
+ P3("found shoulder: %d,%d", xx, yy);
+ found_x = xx;
+ found_y = yy;
+ /* Insert this candidate, it is just a
+ co-ordinate, reusing Area */
+ assign(&candidate, xx, yy, 0, 0);
+ insert_element(&maybes, &candidate);
+#ifdef X_SCAN_LIMITER
+ /* change upper x bound */
+ end_x = xx + 1;
+#endif
+ break;
+ }
+ } else {
+ /* Optimization required only for Non Aligned,
+ Aligned anyways skip by 32/64 tiles at a time */
+ if (stride == 1 &&
+ pvt->map[xx][yy].parent.is2d) {
+ xx = pvt->map[xx][yy].parent.p0.x;
+ P3("moving to: %d,%d", xx, yy);
+ }
+ }
+ }
+
+ /* if you find a free area shouldering the given scan area on
+ then we can break */
+#ifdef Y_SCAN_LIMITER
+ if (found_x == start_x)
+ break;
+#endif
+ }
+
+ if (list_empty(&maybes))
+ return -ENOSPC;
+
+ select_candidate(tcm, w, h, &maybes, field, CR_R2L_T2B, area);
+ /* dump_list_entries(maybes); */
+ clean_list(&maybes);
+ return 0;
+}
+
+#ifdef SCAN_BOTTOM_UP
+/**
+ * @description: raster scan right to left from bottom to top; find if there is
+ * a free area to fit a given w x h inside the 'scan area'. If there is a free
+ * area, then adds to maybes candidates, which later is sent for selection
+ * as per pre-defined criteria.
+ *
+ * @input:'w x h' width and height of the allocation area.
+ * 'stride' - 64/32/None for start address alignment
+ * 'field' - area in which the scan operation should take place
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the 'area' area contains TL and BR corners of the allocated area
+ *
+ */
+static s32 scan_r2l_b2t(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area)
+{
+ /* TODO: Should I check scan area?
+ * Might have to take it as input during initialization
+ */
+ s32 xx = 0, yy = 0;
+ s16 start_x = -1, end_x = -1, start_y = -1, end_y = -1;
+ s16 found_x = -1, found_y = -1;
+ LIST_HEAD(maybes);
+ struct tcm_area candidate = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "scan_r2l_b2t:", field);
+
+ start_x = field->p0.x;
+ end_x = field->p1.x;
+ start_y = field->p0.y;
+ end_y = field->p1.y;
+
+ /* check scan area co-ordinates */
+ if (field->p1.x < field->p0.x ||
+ field->p1.y < field->p0.y)
+ return -EINVAL;
+
+ /* check if allocation would fit in scan area */
+ if (w > INCL_LEN(start_x, end_x) || h > INCL_LEN(start_y, end_y))
+ return -ENOSPC;
+
+ /* adjust start_x and start_y, as allocation would not fit beyond */
+ start_x = ALIGN_DOWN(start_x - w + 1, stride); /* + 1 to be inclusive */
+ start_y = start_y - h + 1;
+
+ /* check if allocation would still fit in scan area */
+ if (start_x < end_x)
+ return -ENOSPC;
+
+ P2("ali=%d x=%d..%d y=%d..%d", stride, start_x, end_x, start_y, end_y);
+
+ /*
+ * Start scanning: These scans are always inclusive ones so if we are
+ * given a start x = 0 is a valid value so if we have a end_x = 255,
+ * 255th element is also checked
+ */
+ for (yy = start_y; yy >= end_y; yy--) {
+ for (xx = start_x; xx >= end_x; xx -= stride) {
+ if (!pvt->map[xx][yy].busy) {
+ if (check_fit_r_and_b(tcm, w, h, xx, yy)) {
+ P3("found shoulder: %d,%d", xx, yy);
+ found_x = xx;
+ found_y = yy;
+ /* Insert this candidate, it is just a
+ co-ordinate, reusing Area */
+ assign(&candidate, xx, yy, 0, 0);
+ insert_element(&maybes, &candidate);
+#ifdef X_SCAN_LIMITER
+ /* change upper x bound */
+ end_x = xx + 1;
+#endif
+ break;
+ }
+ } else {
+ /* Optimization required only for Non Aligned,
+ Aligned anyways skip by 32/64 tiles at a time */
+ if (stride == 1 &&
+ pvt->map[xx][yy].parent.is2d) {
+ xx = pvt->map[xx][yy].parent.p0.x;
+ P3("moving to: %d,%d", xx, yy);
+ }
+ }
+
+ }
+
+ /* if you find a free area shouldering the given scan area on
+ then we can break */
+#ifdef Y_SCAN_LIMITER
+ if (found_x == start_x)
+ break;
+#endif
+ }
+
+ if (list_empty(&maybes))
+ return -ENOSPC;
+
+ select_candidate(tcm, w, h, &maybes, field, CR_R2L_B2T, area);
+ /* dump_list_entries(maybes); */
+ clean_list(&maybes);
+ return 0;
+}
+#endif
+
+/**
+ * @description: raster scan left to right from top to bottom; find if there is
+ * a free area to fit a given w x h inside the 'scan area'. If there is a free
+ * area, then adds to maybes candidates, which later is sent for selection
+ * as per pre-defined criteria.
+ *
+ * @input:'w x h' width and height of the allocation area.
+ * 'stride' - 64/32/None for start address alignment
+ * 'field' - area in which the scan operation should take place
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the 'area' area contains TL and BR corners of the allocated area
+ *
+ */
+s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area)
+{
+ s32 xx = 0, yy = 0;
+ s16 start_x = -1, end_x = -1, start_y = -1, end_y = -1;
+ s16 found_x = -1, found_y = -1;
+ LIST_HEAD(maybes);
+ struct tcm_area candidate = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "scan_l2r_t2b:", field);
+
+ start_x = field->p0.x;
+ end_x = field->p1.x;
+ start_y = field->p0.y;
+ end_y = field->p1.y;
+
+ /* check scan area co-ordinates */
+ if (field->p1.x < field->p0.x ||
+ field->p1.y < field->p0.y)
+ return -EINVAL;
+
+ /* check if allocation would fit in scan area */
+ if (w > INCL_LEN(end_x, start_x) || h > INCL_LEN(end_y, start_y))
+ return -ENOSPC;
+
+ start_x = ALIGN(start_x, stride);
+
+ /* check if allocation would still fit in scan area */
+ if (w > INCL_LEN(end_x, start_x))
+ return -ENOSPC;
+
+ /* adjust end_x and end_y, as allocation would not fit beyond */
+ end_x = end_x - w + 1; /* + 1 to be inclusive */
+ end_y = end_y - h + 1;
+
+ P2("ali=%d x=%d..%d y=%d..%d", stride, start_x, end_x, start_y, end_y);
+
+ /*
+ * Start scanning: These scans are always inclusive ones so if we are
+ * given a start x = 0 is a valid value so if we have a end_x = 255,
+ * 255th element is also checked
+ */
+ for (yy = start_y; yy <= end_y; yy++) {
+ for (xx = start_x; xx <= end_x; xx += stride) {
+ /* if NOT occupied */
+ if (!pvt->map[xx][yy].busy) {
+ if (check_fit_r_and_b(tcm, w, h, xx, yy)) {
+ P3("found shoulder: %d,%d", xx, yy);
+ found_x = xx;
+ found_y = yy;
+ /* Insert this candidate, it is just a
+ co-ordinate, reusing Area */
+ assign(&candidate, xx, yy, 0, 0);
+ insert_element(&maybes, &candidate);
+#ifdef X_SCAN_LIMITER
+ /* change upper x bound */
+ end_x = xx - 1;
+#endif
+ break;
+ }
+ } else {
+ /* Optimization required only for Non Aligned,
+ Aligned anyways skip by 32/64 tiles at a time */
+ if (stride == 1 &&
+ pvt->map[xx][yy].parent.is2d) {
+ xx = pvt->map[xx][yy].parent.p1.x;
+ P3("moving to: %d,%d", xx, yy);
+ }
+ }
+ }
+ /* if you find a free area shouldering the given scan area on
+ then we can break */
+#ifdef Y_SCAN_LIMITER
+ if (found_x == start_x)
+ break;
+#endif
+ }
+
+ if (list_empty(&maybes))
+ return -ENOSPC;
+
+ select_candidate(tcm, w, h, &maybes, field, CR_L2R_T2B, area);
+ /* dump_list_entries(maybes); */
+ clean_list(&maybes);
+ return 0;
+}
+
+#ifdef SCAN_BOTTOM_UP
+/**
+ * @description: raster scan left to right from bottom to top; find if there is
+ * a free area to fit a given w x h inside the 'scan area'. If there is a free
+ * area, then adds to maybes candidates, which later is sent for selection
+ * as per pre-defined criteria.
+ *
+ * @input:'w x h' width and height of the allocation area.
+ * 'stride' - 64/32/None for start address alignment
+ * 'field' - area in which the scan operation should take place
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the 'area' area contains TL and BR corners of the allocated area
+ *
+ */
+static s32 scan_l2r_b2t(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *field, struct tcm_area *area)
+{
+ s32 xx = 0, yy = 0;
+ s16 start_x = -1, end_x = -1, start_y = -1, end_y = -1;
+ s16 found_x = -1, found_y = -1;
+ LIST_HEAD(maybes);
+ struct tcm_area candidate = {0};
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "scan_l2r_b2t:", field);
+
+ start_x = field->p0.x;
+ end_x = field->p1.x;
+ start_y = field->p0.y;
+ end_y = field->p1.y;
+
+ /* check scan area co-ordinates */
+ if (field->p1.x < field->p0.x ||
+ field->p0.y < field->p1.y)
+ return -EINVAL;
+
+ /* check if allocation would fit in scan area */
+ if (w > INCL_LEN(end_x, start_x) || h > INCL_LEN(start_y, end_y))
+ return -ENOSPC;
+
+ start_x = ALIGN(start_x, stride);
+
+ /* check if allocation would still fit in scan area */
+ if (w > INCL_LEN(end_x, start_x))
+ return -ENOSPC;
+
+ /* adjust end_x and start_y, as allocation would not fit beyond */
+ end_x = end_x - w + 1; /* + 1 to be inclusive */
+ start_y = start_y - h + 1;
+
+ P2("ali=%d x=%d..%d y=%d..%d", stride, start_x, end_x, start_y, end_y);
+
+ /*
+ * Start scanning: These scans are always inclusive ones so if we are
+ * given a start x = 0 is a valid value so if we have a end_x = 255,
+ * 255th element is also checked
+ */
+ for (yy = start_y; yy >= end_y; yy--) {
+ for (xx = start_x; xx <= end_x; xx += stride) {
+ /* if NOT occupied */
+ if (!pvt->map[xx][yy].busy) {
+ if (check_fit_r_and_b(tcm, w, h, xx, yy)) {
+ P3("found shoulder: %d,%d", xx, yy);
+ found_x = xx;
+ found_y = yy;
+ /* Insert this candidate, it is just a
+ co-ordinate, reusing Area */
+ assign(&candidate, xx, yy, 0, 0);
+ insert_element(&maybes, &candidate);
+#ifdef X_SCAN_LIMITER
+ /* change upper x bound */
+ end_x = xx - 1;
+#endif
+ break;
+ }
+ } else {
+ /* Optimization required only for Non Aligned,
+ Aligned anyways skip by 32/64 tiles at a time */
+ if (stride == 1 &&
+ pvt->map[xx][yy].parent.is2d) {
+ xx = pvt->map[xx][yy].parent.p1.x;
+ P3("moving to: %d,%d", xx, yy);
+ }
+ }
+ }
+
+ /* if you find a free area shouldering the given scan area on
+ then we can break */
+#ifdef Y_SCAN_LIMITER
+ if (found_x == start_x)
+ break;
+#endif
+ }
+
+ if (list_empty(&maybes))
+ return -ENOSPC;
+
+ select_candidate(tcm, w, h, &maybes, field, CR_L2R_B2T, area);
+ /* dump_list_entries(maybes); */
+ clean_list(&maybes);
+ return 0;
+}
+#endif
+/*
+Note: In General the cordinates specified in the scan area area relevant to the
+scan sweep directions. i.e A scan Area from Top Left Corner will have
+p0.x <= p1.x and p0.y <= p1.y. Where as A scan Area from bottom Right Corner
+will have p1.x <= p0.x and p1.y <= p0.y
+*/
+
+/**
+ * @description: raster scan right to left from bottom to top; find if there are
+ * continuous free pages(one slot is one page, continuity always from left to
+ * right) inside the 'scan area'. If there are enough continous free pages,
+ * then it returns the start and end Tile/page co-ordinates inside 'area'
+ *
+ * @input:'num_pages' required,
+ * 'field' - area in which the scan operation should take place
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ * the 'area' area contains start and end slot (inclusive).
+ *
+ */
+static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_pages,
+ struct tcm_area *field, struct tcm_area *area)
+{
+ s32 fit = false;
+ u16 x, y;
+ u16 left_x, left_y, busy_x, busy_y;
+ s32 ret = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ /* check scan area co-ordinates */
+ if (field->p0.y < field->p1.y)
+ return -EINVAL;
+
+ PA(2, "scan_r2l_b2t_one_dim:", field);
+
+ /* Note: Checking sanctity of scan area
+ * The reason for checking this that 1D allocations assume that the X
+ ranges the entire TilerSpace X ie ALL Columns
+ * The scan area can limit only the Y ie, Num of Rows for 1D allocation.
+ We also expect we could have only 1 row for 1D allocation
+ * i.e our field p0.y and p1.y may have a same value.
+ */
+
+ /* only support full width 1d scan area */
+ if (pvt->width != field->p0.x - field->p1.x + 1)
+ return -EINVAL;
+
+ /* check if allocation would fit in scan area */
+ if (num_pages > pvt->width * INCL_LEN(field->p0.y, field->p1.y))
+ return -ENOSPC;
+
+ left_x = field->p0.x;
+ left_y = field->p0.y;
+ while (!ret) {
+ x = left_x;
+ y = left_y;
+
+ if (!pvt->map[x][y].busy) {
+ ret = move_left(tcm, x, y, num_pages - 1,
+ &left_x, &left_y);
+ if (ret)
+ break; /* out of space */
+
+ P3("moved left %d slots: %d,%d", num_pages - 1,
+ left_x, left_y);
+ fit = check_fit_r_one_dim(tcm, left_x, left_y,
+ num_pages, &busy_x, &busy_y);
+ if (fit) {
+ assign(area, left_x, left_y,
+ busy_x, busy_y);
+ break;
+ } else {
+ /* no fit, continue at the busy slot */
+ x = busy_x;
+ y = busy_y;
+ }
+ }
+
+ /* now the tile is occupied, skip busy region */
+ if (pvt->map[x][y].parent.is2d) {
+ busy_x = pvt->map[x][y].parent.p0.x;
+ busy_y = y;
+ } else {
+ busy_x = pvt->map[x][y].parent.p0.x;
+ busy_y = pvt->map[x][y].parent.p0.y;
+ }
+ x = busy_x;
+ y = busy_y;
+
+ P3("moving left from: %d,%d", x, y);
+ ret = move_left(tcm, x, y, 1, &left_x, &left_y);
+ }
+
+ return fit ? 0 : -ENOSPC;
+}
+
+/**
+ * @description:
+ *
+ *
+ *
+ *
+ * @input:
+ *
+ *
+ * @return 0 on success, non-0 error value on failure. On success
+ */
+static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 stride,
+ struct tcm_area *area)
+{
+ s32 ret = 0;
+ struct tcm_area field = {0};
+ u16 boundary_x = 0, boundary_y = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ s32 need_scan = 2;
+
+ if (stride > 1) {
+ boundary_x = pvt->div_pt.x - 1;
+ boundary_y = pvt->div_pt.y - 1;
+
+ /* more intelligence here */
+ if (w > pvt->div_pt.x) {
+ boundary_x = pvt->width - 1;
+ need_scan--;
+ }
+ if (h > pvt->div_pt.y) {
+ boundary_y = pvt->height - 1;
+ need_scan--;
+ }
+
+ assign(&field, 0, 0, boundary_x, boundary_y);
+ ret = scan_l2r_t2b(tcm, w, h, stride, &field, area);
+ if (ret != 0 && need_scan) {
+ /* scan the entire container if nothing found */
+ assign(&field, 0, 0, pvt->width - 1, pvt->height - 1);
+ ret = scan_l2r_t2b(tcm, w, h, stride, &field, area);
+ }
+ } else if (stride == 1) {
+ boundary_x = pvt->div_pt.x;
+ boundary_y = pvt->div_pt.y - 1;
+
+ /* more intelligence here */
+ if (w > (pvt->width - pvt->div_pt.x)) {
+ boundary_x = 0;
+ need_scan--;
+ }
+ if (h > pvt->div_pt.y) {
+ boundary_y = pvt->height - 1;
+ need_scan--;
+ }
+
+ assign(&field, pvt->width - 1, 0, boundary_x, boundary_y);
+ ret = scan_r2l_t2b(tcm, w, h, stride, &field, area);
+
+ if (ret != 0 && need_scan) {
+ /* scan the entire container if nothing found */
+ assign(&field, pvt->width - 1, 0, 0,
+ pvt->height - 1);
+ ret = scan_r2l_t2b(tcm, w, h, stride, &field,
+ area);
+ }
+ }
+
+ /* 3/30/2010: moved aligned to left, and unaligned to right side. */
+#if 0
+ else if (stride == 1) {
+ /* use 64-align area so we don't grow down and shrink 1D area */
+ if (h > pvt->div_pt.y) {
+ need_scan -= 2;
+ assign(&field, 0, 0, pvt->width - 1, pvt->height - 1);
+ ret = scan_l2r_t2b(tcm, w, h, stride, &field, area);
+ } else {
+ assign(&field, 0, pvt->div_pt.y - 1, pvt->width - 1, 0);
+ /* scan up in 64 and 32 areas accross whole width */
+ ret = scan_l2r_b2t(tcm, w, h, stride, &field, area);
+ }
+
+ if (ret != 0 && need_scan) {
+ assign(&field, 0, 0, pvt->width - 1, pvt->height - 1);
+ ret = scan_l2r_t2b(tcm, w, h, stride, &field, area);
+ }
+ }
+#endif
+ return ret;
+}
+
+static s32 check_fit_r_and_b(struct tcm *tcm, u16 w, u16 h, u16 left_x,
+ u16 top_y)
+{
+ u16 xx = 0, yy = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ for (yy = top_y; yy < top_y + h; yy++) {
+ for (xx = left_x; xx < left_x + w; xx++) {
+ if (pvt->map[xx][yy].busy)
+ return false;
+ }
+ }
+ return true;
+}
+
+static s32 check_fit_r_one_dim(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *busy_x, u16 *busy_y)
+{
+ s32 ret = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ s32 i = 0;
+ *busy_x = x;
+ *busy_y = y;
+
+ P2("checking fit for %d pages from %d,%d", num_pages, x, y);
+ while (i < num_pages) {
+ if (pvt->map[x][y].busy) {
+ /* go to the start of the blocking allocation
+ to avoid unecessary checking */
+ if (pvt->map[x][y].parent.is2d) {
+ *busy_x = pvt->map[x][y].parent.p0.x;
+ *busy_y = y;
+ } else {
+ *busy_x = pvt->map[x][y].parent.p0.x;
+ *busy_y = pvt->map[x][y].parent.p0.y;
+ }
+ /* TODO: Could also move left in case of 2D */
+ P2("after busy slot at: %d,%d", *busy_x, *busy_y);
+ return false;
+ }
+
+ i++;
+
+ /* break here so busy_x, busy_y will be correct */
+ if (i == num_pages)
+ break;
+
+ ret = move_right(tcm, x, y, 1, busy_x, busy_y);
+ if (ret)
+ return false;
+
+ x = *busy_x;
+ y = *busy_y;
+ }
+
+ return true;
+}
+
+static void fill_2d_area(struct tcm *tcm, struct tcm_area *area,
+ struct slot slot)
+{
+ s32 x, y;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "fill 2d area", area);
+ for (x = area->p0.x; x <= area->p1.x; ++x)
+ for (y = area->p0.y; y <= area->p1.y; ++y)
+ pvt->map[x][y] = slot;
+}
+
+/* area should be a valid area */
+static void fill_1d_area(struct tcm *tcm, struct tcm_area *area,
+ struct slot slot)
+{
+ u16 x = 0, y = 0;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ PA(2, "fill 1d area", area);
+ x = area->p0.x;
+ y = area->p0.y;
+
+ while (!(x == area->p1.x && y == area->p1.y)) {
+ pvt->map[x++][y] = slot;
+ if (x == pvt->width) {
+ x = 0;
+ y++;
+ }
+ }
+ /* set the last slot */
+ pvt->map[x][y] = slot;
+}
+
+static void select_candidate(struct tcm *tcm, u16 w, u16 h,
+ struct list_head *maybes,
+ struct tcm_area *field, s32 criteria,
+ struct tcm_area *area)
+{
+ /* bookkeeping the best match and the one evaluated */
+ struct area_spec *best = NULL;
+ struct nearness_factor best_factor = {0};
+ struct neighbour_stats best_stats = {0};
+ u16 win_neighs = 0;
+
+ /* bookkeeping the current one being evaluated */
+ struct area_spec *elem = NULL;
+ struct nearness_factor factor = {0};
+ struct neighbour_stats stats = {0};
+ u16 neighs = 0;
+
+ bool better; /* whether current is better */
+
+ /* we default to the 1st candidate */
+ best = list_first_entry(maybes, struct area_spec, list);
+
+ /*i f there is only one candidate then that is the selection*/
+
+ /* If first found is enabled then we just provide bluntly the first
+ found candidate
+ * NOTE: For Horizontal bias we just give the first found, because our
+ * scan is Horizontal raster based and the first candidate will always
+ * be the same as if selecting the Horizontal one.
+ */
+ if (list_is_singular(maybes) ||
+ criteria & CR_FIRST_FOUND || criteria & CR_BIAS_HORIZONTAL)
+ /* Note: Sure we could have done this in the previous function,
+ but just wanted this to be cleaner so having
+ * one place where the selection is made. Here I am returning
+ the first one
+ */
+ goto done;
+
+ /* lets calculate for the first candidate and assign him the best and
+ replace with the one who has better credentials w/ to the criteria */
+
+ get_busy_neigh_stats(tcm, w, h, &best->area, &best_stats);
+ win_neighs = BOUNDARY(&best_stats) +
+ OCCUPIED(&best_stats);
+ get_nearness_factor(field, &best->area, &best_factor);
+
+ list_for_each_entry(elem, maybes->next, list) {
+ better = false;
+
+ /* calculate required statistics */
+ get_busy_neigh_stats(tcm, w, h, &elem->area, &stats);
+ get_nearness_factor(field, &elem->area, &factor);
+ neighs = BOUNDARY(&stats) + OCCUPIED(&stats);
+
+ /* see if this are is better than the best so far */
+
+ /* neighbor check */
+ if ((criteria & CR_MAX_NEIGHS) &&
+ neighs > win_neighs)
+ better = true;
+
+ /* vertical bias check */
+ if ((criteria & CR_BIAS_VERTICAL) &&
+ /*
+ * NOTE: not checking if lengths are same, because that does not
+ * find new shoulders on the same row after a fit
+ */
+ INCL_LEN_MOD(elem->area.p0.y, field->p0.y) >
+ INCL_LEN_MOD(best->area.p0.y, field->p0.y))
+ better = true;
+
+ /* diagonal balance check */
+ if ((criteria & CR_DIAGONAL_BALANCE) &&
+ win_neighs <= neighs &&
+ (win_neighs < neighs ||
+ /* this implies that neighs and occupied match */
+ OCCUPIED(&best_stats) < OCCUPIED(&stats) ||
+ (OCCUPIED(&best_stats) == OCCUPIED(&stats) &&
+ /* check the nearness factor */
+ best_factor.x + best_factor.y > factor.x + factor.y)))
+ better = true;
+
+ if (better) {
+ best = elem;
+ best_factor = factor;
+ best_stats = stats;
+ win_neighs = neighs;
+ }
+ }
+
+done:
+ assign(area, best->area.p0.x, best->area.p0.y,
+ best->area.p0.x + w - 1, best->area.p0.y + h - 1);
+}
+
+/* get the nearness factor of an area in a search field */
+static void get_nearness_factor(struct tcm_area *field,
+ struct tcm_area *area, struct nearness_factor *nf)
+{
+ /* For the following calculation we need worry of +/- sign, the
+ relative distances take of this. Multiplied by 1000, there
+ is no floating point arithmetic used in kernel */
+
+ nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
+ (field->p1.x - field->p0.x);
+ nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
+ (field->p1.y - field->p0.y);
+}
+
+/* Neighbours
+ *
+ * |<-----T------>|
+ * _ _______________ _
+ * L | Ar | R
+ * _ |______________|_
+ * |<-----B------>|
+ */
+static s32 get_busy_neigh_stats(struct tcm *tcm, u16 width, u16 height,
+ struct tcm_area *top_left_corner,
+ struct neighbour_stats *neighbour_stat)
+{
+ s16 xx = 0, yy = 0;
+ struct tcm_area left_edge;
+ struct tcm_area right_edge;
+ struct tcm_area top_edge;
+ struct tcm_area bottom_edge;
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+
+ if (neighbour_stat == NULL)
+ return -EINVAL;
+
+ if (width == 0 || height == 0)
+ return -EINVAL;
+
+ /* Clearing any exisiting values */
+ memset(neighbour_stat, 0, sizeof(*neighbour_stat));
+
+ /* Finding Top Edge */
+ assign(&top_edge, top_left_corner->p0.x, top_left_corner->p0.y,
+ top_left_corner->p0.x + width - 1, top_left_corner->p0.y);
+
+ /* Finding Bottom Edge */
+ assign(&bottom_edge, top_left_corner->p0.x,
+ top_left_corner->p0.y+height - 1,
+ top_left_corner->p0.x + width - 1,
+ top_left_corner->p0.y + height - 1);
+
+ /* Finding Left Edge */
+ assign(&left_edge, top_left_corner->p0.x, top_left_corner->p0.y,
+ top_left_corner->p0.x, top_left_corner->p0.y + height - 1);
+
+ /* Finding Right Edge */
+ assign(&right_edge, top_left_corner->p0.x + width - 1,
+ top_left_corner->p0.y,
+ top_left_corner->p0.x + width - 1,
+ top_left_corner->p0.y + height - 1);
+
+ /* dump_area(&top_edge);
+ dump_area(&right_edge);
+ dump_area(&bottom_edge);
+ dump_area(&left_edge);
+ */
+
+ /* Parsing through top & bottom edge */
+ for (xx = top_edge.p0.x; xx <= top_edge.p1.x; xx++) {
+ if (top_edge.p0.y - 1 < 0)
+ neighbour_stat->top_boundary++;
+ else if (pvt->map[xx][top_edge.p0.y - 1].busy)
+ neighbour_stat->top_occupied++;
+
+ if (bottom_edge.p0.y + 1 > pvt->height - 1)
+ neighbour_stat->bottom_boundary++;
+ else if (pvt->map[xx][bottom_edge.p0.y+1].busy)
+ neighbour_stat->bottom_occupied++;
+ }
+
+ /* Parsing throught left and right edge */
+ for (yy = left_edge.p0.y; yy <= left_edge.p1.y; ++yy) {
+ if (left_edge.p0.x - 1 < 0)
+ neighbour_stat->left_boundary++;
+ else if (pvt->map[left_edge.p0.x - 1][yy].busy)
+ neighbour_stat->left_occupied++;
+
+ if (right_edge.p0.x + 1 > pvt->width - 1)
+ neighbour_stat->right_boundary++;
+ else if (pvt->map[right_edge.p0.x + 1][yy].busy)
+ neighbour_stat->right_occupied++;
+
+ }
+
+ return 0;
+}
+
+/**
+ @description: Retrieves the parent area of the page at p0.x, p0.y if
+ occupied
+ @input:co-ordinates of the page (p0.x, p0.y) whoes parent area
+ is required
+ @return 0 on success, non-0 error value on failure. On success
+
+ parent will contain co-ordinates (TL & BR corner) of the parent
+ area
+*/
+static s32 sita_get_parent(struct tcm *tcm, struct tcm_pt *pt,
+ struct tcm_area *parent)
+{
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ s32 res = 0;
+
+ mutex_lock(&(pvt->mtx));
+
+ if (pvt->map[pt->x][pt->y].busy) {
+ *parent = pvt->map[pt->x][pt->y].parent;
+ } else {
+ memset(parent, 0, sizeof(*parent));
+ res = -ENOENT;
+ }
+
+ mutex_unlock(&(pvt->mtx));
+
+ return res;
+}
+
+static s32 move_left(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *xx, u16 *yy)
+{
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ u32 pos = x + pvt->width * y;
+
+ if (pos < num_pages)
+ return -ENOSPC;
+
+ pos -= num_pages;
+ *xx = pos % pvt->width;
+ *yy = pos / pvt->width;
+ return 0;
+}
+
+static s32 move_right(struct tcm *tcm, u16 x, u16 y, u32 num_pages,
+ u16 *xx, u16 *yy)
+{
+ struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ u32 pos = x + pvt->width * y;
+
+ if (num_pages > pvt->width * pvt->height - pos)
+ return -ENOSPC;
+
+ pos += num_pages;
+ *xx = pos % pvt->width;
+ *yy = pos / pvt->width;
+ return 0;
+}
+
diff --git a/drivers/media/video/tiler/tcm/tcm_sita.h b/drivers/media/video/tiler/tcm/tcm_sita.h
new file mode 100644
index 000000000000..fb5f8e89192c
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/tcm_sita.h
@@ -0,0 +1,39 @@
+/*
+ * tcm_sita.h
+ *
+ * SImple Tiler Allocator (SiTA) interface.
+ *
+ * Author: Ravi Ramachandra <r.ramachandra@ti.com>
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef TCM_SITA_H_
+#define TCM_SITA_H_
+
+#include "tcm.h"
+
+/**
+ * Create a SiTA tiler container manager.
+ *
+ * @param width Container width
+ * @param height Container height
+ * @param attr preferred division point between 64-aligned
+ * allocation (top left), 32-aligned allocations
+ * (top right), and page mode allocations (bottom)
+ *
+ * @return TCM instance
+ */
+struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr);
+
+TCM_INIT(sita_init, struct tcm_pt);
+
+#endif /* TCM_SITA_H_ */
diff --git a/drivers/media/video/tiler/tcm/tcm_utils.h b/drivers/media/video/tiler/tcm/tcm_utils.h
new file mode 100644
index 000000000000..7d0ed5c149b8
--- /dev/null
+++ b/drivers/media/video/tiler/tcm/tcm_utils.h
@@ -0,0 +1,59 @@
+/*
+ * tcm_utils.h
+ *
+ * Utility functions for implementing TILER container managers.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _TCM_UTILS_H
+#define _TCM_UTILS_H
+
+#include "tcm.h"
+
+#define AREA_FMT "(%03d %03d)-(%03d %03d)"
+#define AREA(area) (area).p0.x, (area).p0.y, (area).p1.x, (area).p1.y
+
+/* TCM_ALG_NAME must be defined to use the debug methods */
+
+#ifdef DEBUG
+#define IFDEBUG(x) x
+#else
+#define IFDEBUG(x) do { if (0) x; } while (0)
+#endif
+
+#define P(level, fmt, ...) \
+ IFDEBUG(printk(level TCM_ALG_NAME ":%d:%s()" fmt "\n", \
+ __LINE__, __func__, ##__VA_ARGS__))
+
+#define P1(fmt, ...) P(KERN_NOTICE, fmt, ##__VA_ARGS__)
+#define P2(fmt, ...) P(KERN_INFO, fmt, ##__VA_ARGS__)
+#define P3(fmt, ...) P(KERN_DEBUG, fmt, ##__VA_ARGS__)
+
+#define PA(level, msg, p_area) P##level(msg " " AREA_FMT "\n", AREA(*(p_area)))
+
+/* assign coordinates to area */
+static inline
+void assign(struct tcm_area *a, u16 x0, u16 y0, u16 x1, u16 y1)
+{
+ a->p0.x = x0;
+ a->p0.y = y0;
+ a->p1.x = x1;
+ a->p1.y = y1;
+}
+
+static inline
+void dump_area(struct tcm_area *area)
+{
+ printk(KERN_NOTICE AREA_FMT "\n", AREA(*area));
+}
+
+#endif
diff --git a/drivers/media/video/tiler/tiler.c b/drivers/media/video/tiler/tiler.c
new file mode 100644
index 000000000000..0d4e572a3400
--- /dev/null
+++ b/drivers/media/video/tiler/tiler.c
@@ -0,0 +1,1583 @@
+/*
+ * tiler.c
+ *
+ * TILER driver support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/cdev.h> /* struct cdev */
+#include <linux/kdev_t.h> /* MKDEV() */
+#include <linux/fs.h> /* register_chrdev_region() */
+#include <linux/device.h> /* struct class */
+#include <linux/platform_device.h> /* platform_device() */
+#include <linux/err.h> /* IS_ERR() */
+#include <linux/uaccess.h> /* copy_to_user */
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/pagemap.h> /* page_cache_release() */
+#include <linux/slab.h>
+
+#include <mach/tiler.h>
+#include <mach/dmm.h>
+#include "../dmm/tmm.h"
+#include "tiler_def.h"
+#include "tcm/tcm_sita.h" /* Algo Specific header */
+
+#include <linux/syscalls.h>
+
+struct tiler_dev {
+ struct cdev cdev;
+};
+
+struct platform_driver tiler_driver_ldm = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tiler",
+ },
+ .probe = NULL,
+ .shutdown = NULL,
+ .remove = NULL,
+};
+
+/* per process (thread group) info */
+struct process_info {
+ struct list_head list; /* other processes */
+ struct list_head groups; /* my groups */
+ struct list_head bufs; /* my registered buffers */
+ pid_t pid; /* really: thread group ID */
+ u32 refs; /* open tiler devices, 0 for processes
+ tracked via kernel APIs */
+ bool kernel; /* tracking kernel objects */
+};
+
+/* per group info (within a process) */
+struct gid_info {
+ struct list_head by_pid; /* other groups */
+ struct list_head areas; /* all areas in this pid/gid */
+ struct list_head reserved; /* areas pre-reserved */
+ struct list_head onedim; /* all 1D areas in this pid/gid */
+ u32 gid; /* group ID */
+ struct process_info *pi; /* parent */
+};
+
+struct list_head blocks;
+struct list_head procs;
+struct list_head orphan_areas;
+struct list_head orphan_onedim;
+
+struct area_info {
+ struct list_head by_gid; /* areas in this pid/gid */
+ struct list_head blocks; /* blocks in this area */
+ u32 nblocks; /* # of blocks in this area */
+
+ struct tcm_area area; /* area details */
+ struct gid_info *gi; /* link to parent, if still alive */
+};
+
+struct mem_info {
+ struct list_head global; /* reserved / global blocks */
+ u32 sys_addr; /* system space (L3) tiler addr */
+ u32 num_pg; /* number of pages in page-list */
+ u32 usr; /* user space address */
+ u32 *pg_ptr; /* list of mapped struct page pointers */
+ struct tcm_area area;
+ u32 *mem; /* list of alloced phys addresses */
+ u32 refs; /* number of times referenced */
+ bool alloced; /* still alloced */
+
+ struct list_head by_area; /* blocks in the same area / 1D */
+ void *parent; /* area info for 2D, else group info */
+};
+
+struct __buf_info {
+ struct list_head by_pid; /* list of buffers per pid */
+ struct tiler_buf_info buf_info;
+ struct mem_info *mi[TILER_MAX_NUM_BLOCKS]; /* blocks */
+};
+
+#define TILER_FORMATS 4
+
+static s32 tiler_major;
+static s32 tiler_minor;
+static struct tiler_dev *tiler_device;
+static struct class *tilerdev_class;
+static u32 id;
+static struct mutex mtx;
+static struct tcm *tcm[TILER_FORMATS];
+static struct tmm *tmm[TILER_FORMATS];
+static u32 *dmac_va;
+static dma_addr_t dmac_pa;
+
+#define TCM(fmt) tcm[(fmt) - TILFMT_8BIT]
+#define TCM_SS(ssptr) TCM(TILER_GET_ACC_MODE(ssptr))
+#define TCM_SET(fmt, i) tcm[(fmt) - TILFMT_8BIT] = i
+#define TMM(fmt) tmm[(fmt) - TILFMT_8BIT]
+#define TMM_SS(ssptr) TMM(TILER_GET_ACC_MODE(ssptr))
+#define TMM_SET(fmt, i) tmm[(fmt) - TILFMT_8BIT] = i
+
+/* get process info, and increment refs for device tracking */
+static struct process_info *__get_pi(pid_t pid, bool kernel)
+{
+ struct process_info *pi;
+
+ /* find process context */
+ mutex_lock(&mtx);
+ list_for_each_entry(pi, &procs, list) {
+ if (pi->pid == pid && pi->kernel == kernel)
+ goto done;
+ }
+
+ /* create process context */
+ pi = kmalloc(sizeof(*pi), GFP_KERNEL);
+ if (!pi)
+ goto done;
+
+ memset(pi, 0, sizeof(*pi));
+ pi->pid = pid;
+ pi->kernel = kernel;
+ INIT_LIST_HEAD(&pi->groups);
+ INIT_LIST_HEAD(&pi->bufs);
+ list_add(&pi->list, &procs);
+done:
+ if (pi && !kernel)
+ pi->refs++;
+ mutex_unlock(&mtx);
+ return pi;
+}
+
+/* allocate an reserved area of size, alignment and link it to gi */
+static struct area_info *area_new(u16 width, u16 height, u16 align,
+ struct tcm *tcm, struct gid_info *gi)
+{
+ struct area_info *ai = kmalloc(sizeof(*ai), GFP_KERNEL);
+ if (!ai)
+ return NULL;
+
+ /* set up empty area info */
+ memset(ai, 0x0, sizeof(*ai));
+ INIT_LIST_HEAD(&ai->blocks);
+
+ /* reserve an allocation area */
+ if (tcm_reserve_2d(tcm, width, height, align, &ai->area)) {
+ kfree(ai);
+ return NULL;
+ }
+
+ ai->gi = gi;
+ mutex_lock(&mtx);
+ list_add_tail(&ai->by_gid, &gi->areas);
+ mutex_unlock(&mtx);
+ return ai;
+}
+
+/* (must have mutex) free an area and return NULL */
+static inline void _m_area_free(struct area_info *ai)
+{
+ if (ai) {
+ list_del(&ai->by_gid);
+ kfree(ai);
+ }
+}
+
+static s32 __analize_area(enum tiler_fmt fmt, u32 width, u32 height,
+ u16 *x_area, u16 *y_area, u16 *band,
+ u16 *align, u16 *offs)
+{
+ /* input: width, height is in pixels, align, offs in bytes */
+ /* output: x_area, y_area, band, align, offs in slots */
+
+ /* slot width, height, and row size */
+ u32 slot_w, slot_h, slot_row, bpp;
+
+ /* align must be 2 power */
+ if (*align & (*align - 1))
+ return -1;
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ slot_w = DMM_PAGE_DIMM_X_MODE_8;
+ slot_h = DMM_PAGE_DIMM_Y_MODE_8;
+ break;
+ case TILFMT_16BIT:
+ slot_w = DMM_PAGE_DIMM_X_MODE_16;
+ slot_h = DMM_PAGE_DIMM_Y_MODE_16;
+ break;
+ case TILFMT_32BIT:
+ slot_w = DMM_PAGE_DIMM_X_MODE_32;
+ slot_h = DMM_PAGE_DIMM_Y_MODE_32;
+ break;
+ case TILFMT_PAGE:
+ /* adjust size to accomodate offset, only do page alignment */
+ *align = PAGE_SIZE;
+ width += *offs & (PAGE_SIZE - 1);
+
+ /* for 1D area keep the height (1), width is in tiler slots */
+ *x_area = DIV_ROUND_UP(width, TILER_PAGE);
+ *y_area = *band = 1;
+
+ if (*x_area * *y_area > TILER_WIDTH * TILER_HEIGHT)
+ return -1;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ /* get the # of bytes per row in 1 slot */
+ bpp = tilfmt_bpp(fmt);
+ slot_row = slot_w * bpp;
+
+ /* how many slots are can be accessed via one physical page */
+ *band = PAGE_SIZE / slot_row;
+
+ /* minimum alignment is 1 slot, default alignment is page size */
+ *align = ALIGN(*align ? : PAGE_SIZE, slot_row);
+
+ /* offset must be multiple of bpp */
+ if (*offs & (bpp - 1))
+ return -EINVAL;
+
+ /* round down the offset to the nearest slot size, and increase width
+ to allow space for having the correct offset */
+ width += (*offs & (*align - 1)) / bpp;
+ *offs &= ~(*align - 1);
+
+ /* adjust to slots */
+ *x_area = DIV_ROUND_UP(width, slot_w);
+ *y_area = DIV_ROUND_UP(height, slot_h);
+ *align /= slot_row;
+ *offs /= slot_row;
+
+ if (*x_area > TILER_WIDTH || *y_area > TILER_HEIGHT)
+ return -1;
+ return 0x0;
+}
+
+/**
+ * Find a place where a 2D block would fit into a 2D area of the
+ * same height.
+ *
+ * @author a0194118 (3/19/2010)
+ *
+ * @param w Width of the block.
+ * @param align Alignment of the block.
+ * @param offs Offset of the block (within alignment)
+ * @param ai Pointer to area info
+ * @param next Pointer to the variable where the next block
+ * will be stored. The block should be inserted
+ * before this block.
+ *
+ * @return the end coordinate (x1 + 1) where a block would fit,
+ * or 0 if it does not fit.
+ *
+ * (must have mutex)
+ */
+static u16 _m_blk_find_fit(u16 w, u16 align, u16 offs,
+ struct area_info *ai, struct list_head **before)
+{
+ int x = ai->area.p0.x + w + offs;
+ struct mem_info *mi;
+
+ /* area blocks are sorted by x */
+ list_for_each_entry(mi, &ai->blocks, by_area) {
+ /* check if buffer would fit before this area */
+ if (x <= mi->area.p0.x) {
+ *before = &mi->by_area;
+ return x;
+ }
+ x = ALIGN(mi->area.p1.x + 1 - offs, align) + w + offs;
+ }
+ *before = &ai->blocks;
+
+ /* check if buffer would fit after last area */
+ return (x <= ai->area.p1.x + 1) ? x : 0;
+}
+
+/* (must have mutex) adds a block to an area with certain x coordinates */
+static inline
+struct mem_info *_m_add2area(struct mem_info *mi, struct area_info *ai,
+ u16 x0, u16 x1, struct list_head *before)
+{
+ mi->parent = ai;
+ mi->area = ai->area;
+ mi->area.p0.x = x0;
+ mi->area.p1.x = x1;
+ list_add_tail(&mi->by_area, before);
+ ai->nblocks++;
+ return mi;
+}
+
+static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 offs, u16 band,
+ struct gid_info *gi, struct tcm *tcm) {
+ struct area_info *ai = NULL;
+ struct mem_info *mi = NULL;
+ struct list_head *before = NULL;
+ u16 x = 0; /* this holds the end of a potential area */
+
+ /* allocate map info */
+
+ /* see if there is available prereserved space */
+ mutex_lock(&mtx);
+ list_for_each_entry(mi, &gi->reserved, global) {
+ if (mi->area.tcm == tcm &&
+ tcm_aheight(mi->area) == h &&
+ tcm_awidth(mi->area) == w &&
+ (mi->area.p0.x & (align - 1)) == offs) {
+ /* this area is already set up */
+
+ /* remove from reserved list */
+ list_del(&mi->global);
+ goto done;
+ }
+ }
+ mutex_unlock(&mtx);
+
+ /* if not, reserve a block struct */
+ mi = kmalloc(sizeof(*mi), GFP_KERNEL);
+ if (!mi)
+ return mi;
+ memset(mi, 0, sizeof(*mi));
+
+ /* see if allocation fits in one of the existing areas */
+ /* this sets x, ai and before */
+ mutex_lock(&mtx);
+ list_for_each_entry(ai, &gi->areas, by_gid) {
+ if (ai->area.tcm == tcm &&
+ tcm_aheight(ai->area) == h) {
+ x = _m_blk_find_fit(w, align, offs, ai, &before);
+ if (x) {
+ _m_add2area(mi, ai, x - w, x - 1, before);
+ goto done;
+ }
+ }
+ }
+ mutex_unlock(&mtx);
+
+ /* if no area fit, reserve a new one */
+ ai = area_new(ALIGN(w + offs, max(band, align)), h,
+ max(band, align), tcm, gi);
+ if (ai) {
+ mutex_lock(&mtx);
+ _m_add2area(mi, ai, ai->area.p0.x + offs,
+ ai->area.p0.x + offs + w - 1,
+ &ai->blocks);
+ } else {
+ /* clean up */
+ kfree(mi);
+ return NULL;
+ }
+
+done:
+ mutex_unlock(&mtx);
+ return mi;
+}
+
+/* (must have mutex) */
+static void _m_try_free_group(struct gid_info *gi)
+{
+ if (gi && list_empty(&gi->areas) && list_empty(&gi->onedim)) {
+ BUG_ON(!list_empty(&gi->reserved));
+ list_del(&gi->by_pid);
+
+ /* if group is tracking kernel objects, we may free even
+ the process info */
+ if (gi->pi->kernel && list_empty(&gi->pi->groups)) {
+ list_del(&gi->pi->list);
+ kfree(gi->pi);
+ }
+
+ kfree(gi);
+ }
+}
+
+static void clear_pat(struct tmm *tmm, struct tcm_area *area)
+{
+ struct pat_area p_area = {0};
+ struct tcm_area slice, area_s;
+
+ tcm_for_each_slice(slice, *area, area_s) {
+ p_area.x0 = slice.p0.x;
+ p_area.y0 = slice.p0.y;
+ p_area.x1 = slice.p1.x;
+ p_area.y1 = slice.p1.y;
+
+ tmm_clear(tmm, p_area);
+ }
+}
+
+/* (must have mutex) free block and any freed areas */
+static s32 _m_free(struct mem_info *mi)
+{
+ struct area_info *ai = NULL;
+ struct page *page = NULL;
+ s32 res = 0;
+ u32 i;
+
+ /* release memory */
+ if (mi->pg_ptr) {
+ for (i = 0; i < mi->num_pg; i++) {
+ page = (struct page *)mi->pg_ptr[i];
+ if (page) {
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ page_cache_release(page);
+ }
+ }
+ kfree(mi->pg_ptr);
+ } else if (mi->mem) {
+ tmm_free(TMM_SS(mi->sys_addr), mi->mem);
+ }
+
+ /* safe deletion as list may not have been assigned */
+ if (mi->global.next)
+ list_del(&mi->global);
+ if (mi->by_area.next)
+ list_del(&mi->by_area);
+
+ /* remove block from area first if 2D */
+ if (mi->area.is2d) {
+ ai = mi->parent;
+
+ /* check to see if area needs removing also */
+ if (ai && !--ai->nblocks) {
+ clear_pat(TMM_SS(mi->sys_addr), &ai->area);
+ res = tcm_free(&ai->area);
+ list_del(&ai->by_gid);
+ /* try to remove parent if it became empty */
+ _m_try_free_group(ai->gi);
+ kfree(ai);
+ ai = NULL;
+ }
+ } else {
+ /* remove 1D area */
+ clear_pat(TMM_SS(mi->sys_addr), &mi->area);
+ res = tcm_free(&mi->area);
+ /* try to remove parent if it became empty */
+ _m_try_free_group(mi->parent);
+ }
+
+ kfree(mi);
+ return res;
+}
+
+/* (must have mutex) returns true if block was freed */
+static bool _m_chk_ref(struct mem_info *mi)
+{
+ /* check references */
+ if (mi->refs)
+ return 0;
+
+ if (_m_free(mi))
+ printk(KERN_ERR "error while removing tiler block\n");
+
+ return 1;
+}
+
+/* (must have mutex) */
+static inline s32 _m_dec_ref(struct mem_info *mi)
+{
+ if (mi->refs-- <= 1)
+ return _m_chk_ref(mi);
+
+ return 0;
+}
+
+/* (must have mutex) */
+static inline void _m_inc_ref(struct mem_info *mi)
+{
+ mi->refs++;
+}
+
+/* (must have mutex) returns true if block was freed */
+static inline bool _m_try_free(struct mem_info *mi)
+{
+ if (mi->alloced) {
+ mi->refs--;
+ mi->alloced = false;
+ }
+ return _m_chk_ref(mi);
+}
+
+static s32 register_buf(struct __buf_info *_b, struct process_info *pi)
+{
+ struct mem_info *mi = NULL;
+ struct tiler_buf_info *b = &_b->buf_info;
+ u32 i, num = b->num_blocks, remain = num;
+
+ /* check validity */
+ if (num > TILER_MAX_NUM_BLOCKS)
+ return -EINVAL;
+
+ mutex_lock(&mtx);
+
+ /* find each block */
+ list_for_each_entry(mi, &blocks, global) {
+ for (i = 0; i < num; i++) {
+ if (!_b->mi[i] && mi->sys_addr == b->blocks[i].ssptr) {
+ _b->mi[i] = mi;
+
+ /* quit if found all*/
+ if (!--remain)
+ break;
+
+ }
+ }
+ }
+
+ /* if found all, register buffer */
+ if (!remain) {
+ b->offset = id;
+ id += 0x1000;
+
+ list_add(&_b->by_pid, &pi->bufs);
+
+ /* using each block */
+ for (i = 0; i < num; i++)
+ _m_inc_ref(_b->mi[i]);
+ }
+
+ mutex_unlock(&mtx);
+
+ return remain ? -EACCES : 0;
+}
+
+/* must have mutex */
+static void _m_unregister_buf(struct __buf_info *_b)
+{
+ u32 i;
+
+ /* unregister */
+ list_del(&_b->by_pid);
+
+ /* no longer using the blocks */
+ for (i = 0; i < _b->buf_info.num_blocks; i++)
+ _m_dec_ref(_b->mi[i]);
+
+ kfree(_b);
+}
+
+/**
+ * Free all info kept by a process:
+ *
+ * all registered buffers, allocated blocks, and unreferenced
+ * blocks. Any blocks/areas still referenced will move to the
+ * orphaned lists to avoid issues if a new process is created
+ * with the same pid.
+ *
+ * (must have mutex)
+ */
+static void _m_free_process_info(struct process_info *pi)
+{
+ struct area_info *ai, *ai_;
+ struct mem_info *mi, *mi_;
+ struct gid_info *gi, *gi_;
+ struct __buf_info *_b = NULL, *_b_ = NULL;
+ bool ai_autofreed, need2free;
+
+ /* unregister all buffers */
+ list_for_each_entry_safe(_b, _b_, &pi->bufs, by_pid)
+ _m_unregister_buf(_b);
+
+ BUG_ON(!list_empty(&pi->bufs));
+
+ /* free all allocated blocks, and remove unreferenced ones */
+ list_for_each_entry_safe(gi, gi_, &pi->groups, by_pid) {
+
+ /*
+ * Group info structs when they become empty on an _m_try_free.
+ * However, if the group info is already empty, we need to
+ * remove it manually
+ */
+ need2free = list_empty(&gi->areas) && list_empty(&gi->onedim);
+ list_for_each_entry_safe(ai, ai_, &gi->areas, by_gid) {
+ ai_autofreed = true;
+ list_for_each_entry_safe(mi, mi_, &ai->blocks, by_area)
+ ai_autofreed &= _m_try_free(mi);
+
+ /* save orphaned areas for later removal */
+ if (!ai_autofreed) {
+ need2free = true;
+ ai->gi = NULL;
+ list_move(&ai->by_gid, &orphan_areas);
+ }
+ }
+
+ list_for_each_entry_safe(mi, mi_, &gi->onedim, by_area) {
+ if (!_m_try_free(mi)) {
+ need2free = true;
+ /* save orphaned 1D blocks */
+ mi->parent = NULL;
+ list_move(&mi->by_area, &orphan_onedim);
+ }
+ }
+
+ /* if group is still alive reserved list should have been
+ emptied as there should be no reference on those blocks */
+ if (need2free) {
+ BUG_ON(!list_empty(&gi->onedim));
+ BUG_ON(!list_empty(&gi->areas));
+ _m_try_free_group(gi);
+ }
+ }
+
+ BUG_ON(!list_empty(&pi->groups));
+ list_del(&pi->list);
+ kfree(pi);
+}
+
+static s32 get_area(u32 sys_addr, struct tcm_pt *pt)
+{
+ enum tiler_fmt fmt;
+
+ sys_addr &= TILER_ALIAS_VIEW_CLEAR;
+ fmt = TILER_GET_ACC_MODE(sys_addr);
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ pt->x = DMM_HOR_X_PAGE_COOR_GET_8(sys_addr);
+ pt->y = DMM_HOR_Y_PAGE_COOR_GET_8(sys_addr);
+ break;
+ case TILFMT_16BIT:
+ pt->x = DMM_HOR_X_PAGE_COOR_GET_16(sys_addr);
+ pt->y = DMM_HOR_Y_PAGE_COOR_GET_16(sys_addr);
+ break;
+ case TILFMT_32BIT:
+ pt->x = DMM_HOR_X_PAGE_COOR_GET_32(sys_addr);
+ pt->y = DMM_HOR_Y_PAGE_COOR_GET_32(sys_addr);
+ break;
+ case TILFMT_PAGE:
+ pt->x = (sys_addr & 0x7FFFFFF) >> 12;
+ pt->y = pt->x / TILER_WIDTH;
+ pt->x &= (TILER_WIDTH - 1);
+ break;
+ default:
+ return -EFAULT;
+ }
+ return 0x0;
+}
+
+static u32 __get_alias_addr(enum tiler_fmt fmt, u16 x, u16 y)
+{
+ u32 acc_mode = -1;
+ u32 x_shft = -1, y_shft = -1;
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ acc_mode = 0; x_shft = 6; y_shft = 20;
+ break;
+ case TILFMT_16BIT:
+ acc_mode = 1; x_shft = 7; y_shft = 20;
+ break;
+ case TILFMT_32BIT:
+ acc_mode = 2; x_shft = 7; y_shft = 20;
+ break;
+ case TILFMT_PAGE:
+ acc_mode = 3; y_shft = 8;
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ if (fmt == TILFMT_PAGE)
+ return (u32)TIL_ALIAS_ADDR((x | y << y_shft) << 12, acc_mode);
+ else
+ return (u32)TIL_ALIAS_ADDR(x << x_shft | y << y_shft, acc_mode);
+}
+
+/* must have mutex */
+static struct gid_info *_m_get_gi(struct process_info *pi, u32 gid)
+{
+ struct gid_info *gi;
+
+ /* see if group already exist */
+ list_for_each_entry(gi, &pi->groups, by_pid) {
+ if (gi->gid == gid)
+ return gi;
+ }
+
+ /* create new group */
+ gi = kmalloc(sizeof(*gi), GFP_KERNEL);
+ if (!gi)
+ return gi;
+
+ memset(gi, 0, sizeof(*gi));
+ INIT_LIST_HEAD(&gi->areas);
+ INIT_LIST_HEAD(&gi->onedim);
+ INIT_LIST_HEAD(&gi->reserved);
+ gi->pi = pi;
+ gi->gid = gid;
+ list_add(&gi->by_pid, &pi->groups);
+ return gi;
+}
+
+static struct mem_info *__get_area(enum tiler_fmt fmt, u32 width, u32 height,
+ u16 align, u16 offs, struct gid_info *gi)
+{
+ u16 x, y, band;
+ struct mem_info *mi = NULL;
+
+ /* calculate dimensions, band, offs and alignment in slots */
+ if (__analize_area(fmt, width, height, &x, &y, &band, &align, &offs))
+ return NULL;
+
+ if (fmt == TILFMT_PAGE) {
+ /* 1D areas don't pack */
+ mi = kmalloc(sizeof(*mi), GFP_KERNEL);
+ if (!mi)
+ return NULL;
+ memset(mi, 0x0, sizeof(*mi));
+
+ if (tcm_reserve_1d(TCM(fmt), x * y, &mi->area)) {
+ kfree(mi);
+ return NULL;
+ }
+
+ mutex_lock(&mtx);
+ mi->parent = gi;
+ list_add(&mi->by_area, &gi->onedim);
+ } else {
+ mi = get_2d_area(x, y, align, offs, band, gi, TCM(fmt));
+ if (!mi)
+ return NULL;
+
+ mutex_lock(&mtx);
+ }
+
+ list_add(&mi->global, &blocks);
+ mi->alloced = true;
+ mi->refs++;
+ mutex_unlock(&mtx);
+
+ mi->sys_addr = __get_alias_addr(fmt, mi->area.p0.x, mi->area.p0.y);
+ return mi;
+}
+
+static s32 tiler_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct __buf_info *_b = NULL;
+ struct tiler_buf_info *b = NULL;
+ s32 i = 0, j = 0, k = 0, m = 0, p = 0, bpp = 1;
+ struct list_head *pos = NULL;
+ struct process_info *pi = filp->private_data;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* don't allow mremap */
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+
+ mutex_lock(&mtx);
+ list_for_each(pos, &pi->bufs) {
+ _b = list_entry(pos, struct __buf_info, by_pid);
+ if ((vma->vm_pgoff << PAGE_SHIFT) == _b->buf_info.offset)
+ break;
+ }
+ mutex_unlock(&mtx);
+ if (!_b)
+ return -ENXIO;
+
+ b = &_b->buf_info;
+
+ for (i = 0; i < b->num_blocks; i++) {
+ if (b->blocks[i].fmt >= TILFMT_8BIT &&
+ b->blocks[i].fmt <= TILFMT_32BIT) {
+ /* get line width */
+ bpp = (b->blocks[i].fmt == TILFMT_8BIT ? 1 :
+ b->blocks[i].fmt == TILFMT_16BIT ? 2 : 4);
+ p = PAGE_ALIGN(b->blocks[i].dim.area.width * bpp);
+
+ for (j = 0; j < b->blocks[i].dim.area.height; j++) {
+ /* map each page of the line */
+ vma->vm_pgoff =
+ (b->blocks[i].ssptr + m) >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, vma->vm_start + k,
+ (b->blocks[i].ssptr + m) >> PAGE_SHIFT,
+ p, vma->vm_page_prot))
+ return -EAGAIN;
+ k += p;
+ if (b->blocks[i].fmt == TILFMT_8BIT)
+ m += 64*TILER_WIDTH;
+ else
+ m += 2*64*TILER_WIDTH;
+ }
+ m = 0;
+ } else if (b->blocks[i].fmt == TILFMT_PAGE) {
+ vma->vm_pgoff = (b->blocks[i].ssptr) >> PAGE_SHIFT;
+ p = PAGE_ALIGN(b->blocks[i].dim.len);
+ if (remap_pfn_range(vma, vma->vm_start + k,
+ (b->blocks[i].ssptr) >> PAGE_SHIFT, p,
+ vma->vm_page_prot))
+ return -EAGAIN;;
+ k += p;
+ }
+ }
+ return 0;
+}
+
+static s32 refill_pat(struct tmm *tmm, struct tcm_area *area, u32 *ptr)
+{
+ s32 res = 0;
+ s32 size = tcm_sizeof(*area) * sizeof(*ptr);
+ u32 *page;
+ dma_addr_t page_pa;
+ struct pat_area p_area = {0};
+ struct tcm_area slice, area_s;
+
+ /* must be a 16-byte aligned physical address */
+ page = dma_alloc_coherent(NULL, size, &page_pa, GFP_ATOMIC);
+ if (!page)
+ return -ENOMEM;
+
+ tcm_for_each_slice(slice, *area, area_s) {
+ p_area.x0 = slice.p0.x;
+ p_area.y0 = slice.p0.y;
+ p_area.x1 = slice.p1.x;
+ p_area.y1 = slice.p1.y;
+
+ memcpy(page, ptr, sizeof(*ptr) * tcm_sizeof(slice));
+ ptr += tcm_sizeof(slice);
+
+ if (tmm_map(tmm, p_area, page_pa)) {
+ res = -EFAULT;
+ break;
+ }
+ }
+
+ dma_free_coherent(NULL, size, page, page_pa);
+
+ return res;
+}
+
+static s32 map_block(enum tiler_fmt fmt, u32 width, u32 height, u32 gid,
+ struct process_info *pi, u32 *sys_addr, u32 usr_addr)
+{
+ u32 i = 0, tmp = -1, *mem = NULL;
+ u8 write = 0;
+ s32 res = -ENOMEM;
+ struct mem_info *mi = NULL;
+ struct page *page = NULL;
+ struct task_struct *curr_task = current;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma = NULL;
+ struct gid_info *gi = NULL;
+
+ /* we only support mapping a user buffer in page mode */
+ if (fmt != TILFMT_PAGE)
+ return -EPERM;
+
+ /* check if mapping is supported by tmm */
+ if (!tmm_can_map(TMM(fmt)))
+ return -EPERM;
+
+ /* get group context */
+ mutex_lock(&mtx);
+ gi = _m_get_gi(pi, gid);
+ mutex_unlock(&mtx);
+
+ if (!gi)
+ return -ENOMEM;
+
+ /* reserve area in tiler container */
+ mi = __get_area(fmt, width, height, 0, 0, gi);
+ if (!mi) {
+ mutex_lock(&mtx);
+ _m_try_free_group(gi);
+ mutex_unlock(&mtx);
+ return -ENOMEM;
+ }
+
+ *sys_addr = mi->sys_addr;
+ mi->usr = usr_addr;
+
+ /* allocate pages */
+ mi->num_pg = tcm_sizeof(mi->area);
+
+ mem = kmalloc(mi->num_pg * sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ goto done;
+ memset(mem, 0x0, sizeof(*mem) * mi->num_pg);
+
+ mi->pg_ptr = kmalloc(mi->num_pg * sizeof(*mi->pg_ptr), GFP_KERNEL);
+ if (!mi->pg_ptr)
+ goto done;
+ memset(mi->pg_ptr, 0x0, sizeof(*mi->pg_ptr) * mi->num_pg);
+
+ /*
+ * Important Note: usr_addr is mapped from user
+ * application process to current process - it must lie
+ * completely within the current virtual memory address
+ * space in order to be of use to us here.
+ */
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, mi->usr);
+ res = -EFAULT;
+
+ /*
+ * It is observed that under some circumstances, the user
+ * buffer is spread across several vmas, so loop through
+ * and check if the entire user buffer is covered.
+ */
+ while ((vma) && (mi->usr + width > vma->vm_end)) {
+ /* jump to the next VMA region */
+ vma = find_vma(mm, vma->vm_end + 1);
+ }
+ if (!vma) {
+ printk(KERN_ERR "Failed to get the vma region for "
+ "user buffer.\n");
+ goto fault;
+ }
+
+ if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
+ write = 1;
+
+ tmp = mi->usr;
+ for (i = 0; i < mi->num_pg; i++) {
+ if (get_user_pages(curr_task, mm, tmp, 1, write, 1, &page,
+ NULL)) {
+ if (page_count(page) < 1) {
+ printk(KERN_ERR "Bad page count from"
+ "get_user_pages()\n");
+ }
+ mi->pg_ptr[i] = (u32)page;
+ mem[i] = page_to_phys(page);
+ tmp += PAGE_SIZE;
+ } else {
+ printk(KERN_ERR "get_user_pages() failed\n");
+ goto fault;
+ }
+ }
+ up_read(&mm->mmap_sem);
+
+ /* Ensure the data reaches to main memory before PAT refill */
+ wmb();
+
+ if (refill_pat(TMM(fmt), &mi->area, mem))
+ goto fault;
+
+ res = 0;
+ goto done;
+fault:
+ up_read(&mm->mmap_sem);
+done:
+ if (res) {
+ mutex_lock(&mtx);
+ _m_free(mi);
+ mutex_unlock(&mtx);
+ }
+ kfree(mem);
+ return res;
+}
+
+s32 tiler_mapx(enum tiler_fmt fmt, u32 width, u32 height, u32 gid,
+ pid_t pid, u32 *sys_addr, u32 usr_addr)
+{
+ return map_block(fmt, width, height, gid, __get_pi(pid, true),
+ sys_addr, usr_addr);
+}
+EXPORT_SYMBOL(tiler_mapx);
+
+s32 tiler_map(enum tiler_fmt fmt, u32 width, u32 height, u32 *sys_addr,
+ u32 usr_addr)
+{
+ return tiler_mapx(fmt, width, height, 0, current->tgid, sys_addr,
+ usr_addr);
+}
+EXPORT_SYMBOL(tiler_map);
+
+s32 free_block(u32 sys_addr, struct process_info *pi)
+{
+ struct gid_info *gi = NULL;
+ struct area_info *ai = NULL;
+ struct mem_info *mi = NULL;
+ s32 res = -ENOENT;
+
+ mutex_lock(&mtx);
+
+ /* find block in process list and free it */
+ list_for_each_entry(gi, &pi->groups, by_pid) {
+ /* currently we know if block is 1D or 2D by the address */
+ if (TILER_GET_ACC_MODE(sys_addr) == TILFMT_PAGE) {
+ list_for_each_entry(mi, &gi->onedim, by_area) {
+ if (mi->sys_addr == sys_addr) {
+ _m_try_free(mi);
+ res = 0;
+ goto done;
+ }
+ }
+ } else {
+ list_for_each_entry(ai, &gi->areas, by_gid) {
+ list_for_each_entry(mi, &ai->blocks, by_area) {
+ if (mi->sys_addr == sys_addr) {
+ _m_try_free(mi);
+ res = 0;
+ goto done;
+ }
+ }
+ }
+ }
+ }
+
+done:
+ mutex_unlock(&mtx);
+
+ /* for debugging, we can set the PAT entries to DMM_LISA_MAP__0 */
+ return res;
+}
+
+s32 tiler_free(u32 sys_addr)
+{
+ struct mem_info *mi;
+ s32 res = -ENOENT;
+
+ mutex_lock(&mtx);
+
+ /* find block in global list and free it */
+ list_for_each_entry(mi, &blocks, global) {
+ if (mi->sys_addr == sys_addr) {
+ _m_try_free(mi);
+ res = 0;
+ break;
+ }
+ }
+ mutex_unlock(&mtx);
+
+ /* for debugging, we can set the PAT entries to DMM_LISA_MAP__0 */
+ return res;
+}
+EXPORT_SYMBOL(tiler_free);
+
+/* :TODO: Currently we do not track enough information from alloc to get back
+ the actual width and height of the container, so we must make a guess. We
+ do not even have enough information to get the virtual stride of the buffer,
+ which is the real reason for this ioctl */
+s32 find_block(u32 sys_addr, struct tiler_block_info *blk)
+{
+ struct mem_info *i;
+ struct tcm_pt pt;
+
+ if (get_area(sys_addr, &pt))
+ return -EFAULT;
+
+ list_for_each_entry(i, &blocks, global) {
+ if (tcm_is_in(pt, i->area))
+ goto found;
+ }
+
+ blk->fmt = TILFMT_INVALID;
+ blk->dim.len = blk->stride = blk->ssptr = 0;
+ return -EFAULT;
+
+found:
+ blk->ptr = NULL;
+ blk->fmt = TILER_GET_ACC_MODE(sys_addr);
+ blk->ssptr = __get_alias_addr(blk->fmt, i->area.p0.x, i->area.p0.y);
+
+ if (blk->fmt == TILFMT_PAGE) {
+ blk->dim.len = tcm_sizeof(i->area) * TILER_PAGE;
+ blk->stride = 0;
+ } else {
+ blk->stride = blk->dim.area.width =
+ tcm_awidth(i->area) * TILER_BLOCK_WIDTH;
+ blk->dim.area.height = tcm_aheight(i->area)
+ * TILER_BLOCK_HEIGHT;
+ if (blk->fmt != TILFMT_8BIT) {
+ blk->stride <<= 1;
+ blk->dim.area.height >>= 1;
+ if (blk->fmt == TILFMT_32BIT)
+ blk->dim.area.width >>= 1;
+ }
+ blk->stride = PAGE_ALIGN(blk->stride);
+ }
+ return 0;
+}
+
+static s32 alloc_block(enum tiler_fmt fmt, u32 width, u32 height,
+ u32 align, u32 offs, u32 gid, struct process_info *pi,
+ u32 *sys_addr);
+
+static s32 tiler_ioctl(struct inode *ip, struct file *filp, u32 cmd,
+ unsigned long arg)
+{
+ pgd_t *pgd = NULL;
+ pmd_t *pmd = NULL;
+ pte_t *ptep = NULL, pte = 0x0;
+ s32 r = -1;
+ u32 til_addr = 0x0;
+ struct process_info *pi = filp->private_data;
+
+ struct __buf_info *_b = NULL;
+ struct tiler_buf_info buf_info = {0};
+ struct tiler_block_info block_info = {0};
+
+ switch (cmd) {
+ case TILIOC_GBUF:
+ if (copy_from_user(&block_info, (void __user *)arg,
+ sizeof(block_info)))
+ return -EFAULT;
+
+ switch (block_info.fmt) {
+ case TILFMT_PAGE:
+ r = alloc_block(block_info.fmt, block_info.dim.len, 1,
+ 0, 0, 0, pi, &til_addr);
+ if (r)
+ return r;
+ break;
+ case TILFMT_8BIT:
+ case TILFMT_16BIT:
+ case TILFMT_32BIT:
+ r = alloc_block(block_info.fmt,
+ block_info.dim.area.width,
+ block_info.dim.area.height,
+ 0, 0, 0, pi, &til_addr);
+ if (r)
+ return r;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ block_info.ssptr = til_addr;
+ if (copy_to_user((void __user *)arg, &block_info,
+ sizeof(block_info)))
+ return -EFAULT;
+ break;
+ case TILIOC_FBUF:
+ case TILIOC_UMBUF:
+ if (copy_from_user(&block_info, (void __user *)arg,
+ sizeof(block_info)))
+ return -EFAULT;
+
+ /* search current process first, then all processes */
+ free_block(block_info.ssptr, pi) ?
+ tiler_free(block_info.ssptr) : 0;
+
+ /* free always succeeds */
+ break;
+
+ case TILIOC_GSSP:
+ pgd = pgd_offset(current->mm, arg);
+ if (!(pgd_none(*pgd) || pgd_bad(*pgd))) {
+ pmd = pmd_offset(pgd, arg);
+ if (!(pmd_none(*pmd) || pmd_bad(*pmd))) {
+ ptep = pte_offset_map(pmd, arg);
+ if (ptep) {
+ pte = *ptep;
+ if (pte_present(pte))
+ return (pte & PAGE_MASK) |
+ (~PAGE_MASK & arg);
+ }
+ }
+ }
+ /* va not in page table */
+ return 0x0;
+ break;
+ case TILIOC_MBUF:
+ if (copy_from_user(&block_info, (void __user *)arg,
+ sizeof(block_info)))
+ return -EFAULT;
+
+ if (!block_info.ptr)
+ return -EFAULT;
+
+ if (map_block(block_info.fmt, block_info.dim.len, 1, 0, pi,
+ &block_info.ssptr, (u32)block_info.ptr))
+ return -ENOMEM;
+
+ if (copy_to_user((void __user *)arg, &block_info,
+ sizeof(block_info)))
+ return -EFAULT;
+ break;
+ case TILIOC_QBUF:
+ if (copy_from_user(&buf_info, (void __user *)arg,
+ sizeof(buf_info)))
+ return -EFAULT;
+
+ mutex_lock(&mtx);
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ if (buf_info.offset == _b->buf_info.offset) {
+ if (copy_to_user((void __user *)arg,
+ &_b->buf_info,
+ sizeof(_b->buf_info))) {
+ mutex_unlock(&mtx);
+ return -EFAULT;
+ } else {
+ mutex_unlock(&mtx);
+ return 0;
+ }
+ }
+ }
+ mutex_unlock(&mtx);
+ return -EFAULT;
+ break;
+ case TILIOC_RBUF:
+ _b = kmalloc(sizeof(*_b), GFP_KERNEL);
+ if (!_b)
+ return -ENOMEM;
+
+ memset(_b, 0x0, sizeof(*_b));
+
+ if (copy_from_user(&_b->buf_info, (void __user *)arg,
+ sizeof(_b->buf_info))) {
+ kfree(_b); return -EFAULT;
+ }
+
+ r = register_buf(_b, pi);
+ if (r) {
+ kfree(_b); return -EACCES;
+ }
+
+ if (copy_to_user((void __user *)arg, &_b->buf_info,
+ sizeof(_b->buf_info))) {
+ _m_unregister_buf(_b);
+ return -EFAULT;
+ }
+ break;
+ case TILIOC_URBUF:
+ if (copy_from_user(&buf_info, (void __user *)arg,
+ sizeof(buf_info)))
+ return -EFAULT;
+
+ mutex_lock(&mtx);
+ /* buffer registration is per process */
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ if (buf_info.offset == _b->buf_info.offset) {
+ _m_unregister_buf(_b);
+ mutex_unlock(&mtx);
+ return 0;
+ }
+ }
+ mutex_unlock(&mtx);
+ return -EFAULT;
+ break;
+ case TILIOC_QUERY_BLK:
+ if (copy_from_user(&block_info, (void __user *)arg,
+ sizeof(block_info)))
+ return -EFAULT;
+
+ if (find_block(block_info.ssptr, &block_info))
+ return -EFAULT;
+
+ if (copy_to_user((void __user *)arg, &block_info,
+ sizeof(block_info)))
+ return -EFAULT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0x0;
+}
+
+s32 alloc_block(enum tiler_fmt fmt, u32 width, u32 height,
+ u32 align, u32 offs, u32 gid, struct process_info *pi,
+ u32 *sys_addr)
+{
+ struct mem_info *mi = NULL;
+ struct gid_info *gi = NULL;
+
+ /* only support up to page alignment */
+ if (align > PAGE_SIZE || offs > align || !pi)
+ return -EINVAL;
+
+ /* get group context */
+ mutex_lock(&mtx);
+ gi = _m_get_gi(pi, gid);
+ mutex_unlock(&mtx);
+
+ if (!gi)
+ return -ENOMEM;
+
+ /* reserve area in tiler container */
+ mi = __get_area(fmt, width, height, align, offs, gi);
+ if (!mi) {
+ mutex_lock(&mtx);
+ _m_try_free_group(gi);
+ mutex_unlock(&mtx);
+ return -ENOMEM;
+ }
+
+ *sys_addr = mi->sys_addr;
+
+ /* allocate and map if mapping is supported */
+ if (tmm_can_map(TMM(fmt))) {
+ mi->num_pg = tcm_sizeof(mi->area);
+
+ mi->mem = tmm_get(TMM(fmt), mi->num_pg);
+ if (!mi->mem)
+ goto cleanup;
+
+ /* Ensure the data reaches to main memory before PAT refill */
+ wmb();
+
+ /* program PAT */
+ if (refill_pat(TMM(fmt), &mi->area, mi->mem))
+ goto cleanup;
+ }
+ return 0;
+
+cleanup:
+ mutex_lock(&mtx);
+ _m_free(mi);
+ mutex_unlock(&mtx);
+ return -ENOMEM;
+
+}
+
+s32 tiler_allocx(enum tiler_fmt fmt, u32 width, u32 height,
+ u32 align, u32 offs, u32 gid, pid_t pid, u32 *sys_addr)
+{
+ return alloc_block(fmt, width, height, align, offs, gid,
+ __get_pi(pid, true), sys_addr);
+}
+EXPORT_SYMBOL(tiler_allocx);
+
+s32 tiler_alloc(enum tiler_fmt fmt, u32 width, u32 height, u32 *sys_addr)
+{
+ return tiler_allocx(fmt, width, height, 0, 0,
+ 0, current->tgid, sys_addr);
+}
+EXPORT_SYMBOL(tiler_alloc);
+
+
+static void reserve_nv12_blocks(u32 n, u32 width, u32 height,
+ u32 align, u32 offs, u32 gid, pid_t pid)
+{
+}
+
+static void reserve_blocks(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
+ u32 align, u32 offs, u32 gid, pid_t pid)
+{
+}
+
+/* reserve area for n identical buffers */
+s32 tiler_reservex(u32 n, struct tiler_buf_info *b, pid_t pid)
+{
+ u32 i;
+
+ if (b->num_blocks > TILER_MAX_NUM_BLOCKS)
+ return -EINVAL;
+
+ for (i = 0; i < b->num_blocks; i++) {
+ /* check for NV12 reservations */
+ if (i + 1 < b->num_blocks &&
+ b->blocks[i].fmt == TILFMT_8BIT &&
+ b->blocks[i + 1].fmt == TILFMT_16BIT &&
+ b->blocks[i].dim.area.height ==
+ b->blocks[i + 1].dim.area.height &&
+ b->blocks[i].dim.area.width ==
+ b->blocks[i + 1].dim.area.width) {
+ reserve_nv12_blocks(n,
+ b->blocks[i].dim.area.width,
+ b->blocks[i].dim.area.height,
+ 0, /* align */
+ 0, /* offs */
+ 0, /* gid */
+ pid);
+ i++;
+ } else if (b->blocks[i].fmt >= TILFMT_8BIT &&
+ b->blocks[i].fmt <= TILFMT_32BIT) {
+ /* other 2D reservations */
+ reserve_blocks(n,
+ b->blocks[i].fmt,
+ b->blocks[i].dim.area.width,
+ b->blocks[i].dim.area.height,
+ 0, /* align */
+ 0, /* offs */
+ 0, /* gid */
+ pid);
+ } else {
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tiler_reservex);
+
+s32 tiler_reserve(u32 n, struct tiler_buf_info *b)
+{
+ return tiler_reservex(n, b, current->tgid);
+}
+EXPORT_SYMBOL(tiler_reserve);
+
+static void __exit tiler_exit(void)
+{
+ struct process_info *pi = NULL, *pi_ = NULL;
+ int i, j;
+
+ mutex_lock(&mtx);
+
+ /* free all process data */
+ list_for_each_entry_safe(pi, pi_, &procs, list)
+ _m_free_process_info(pi);
+
+ /* all lists should have cleared */
+ BUG_ON(!list_empty(&blocks));
+ BUG_ON(!list_empty(&procs));
+ BUG_ON(!list_empty(&orphan_onedim));
+ BUG_ON(!list_empty(&orphan_areas));
+
+ mutex_unlock(&mtx);
+
+ dma_free_coherent(NULL, TILER_WIDTH * TILER_HEIGHT * sizeof(*dmac_va),
+ dmac_va, dmac_pa);
+
+ /* close containers only once */
+ for (i = TILFMT_8BIT; i <= TILFMT_MAX; i++) {
+ /* remove identical containers (tmm is unique per tcm) */
+ for (j = i + 1; j <= TILFMT_MAX; j++)
+ if (TCM(i) == TCM(j)) {
+ TCM_SET(j, NULL);
+ TMM_SET(j, NULL);
+ }
+
+ tcm_deinit(TCM(i));
+ tmm_deinit(TMM(i));
+ }
+
+ mutex_destroy(&mtx);
+ platform_driver_unregister(&tiler_driver_ldm);
+ cdev_del(&tiler_device->cdev);
+ kfree(tiler_device);
+ device_destroy(tilerdev_class, MKDEV(tiler_major, tiler_minor));
+ class_destroy(tilerdev_class);
+}
+
+static s32 tiler_open(struct inode *ip, struct file *filp)
+{
+ struct process_info *pi = __get_pi(current->tgid, false);
+
+ if (!pi)
+ return -ENOMEM;
+
+ filp->private_data = pi;
+ return 0x0;
+}
+
+static s32 tiler_release(struct inode *ip, struct file *filp)
+{
+ struct process_info *pi = filp->private_data;
+
+ mutex_lock(&mtx);
+ /* free resources if last device in this process */
+ if (0 == --pi->refs)
+ _m_free_process_info(pi);
+
+ mutex_unlock(&mtx);
+
+ return 0x0;
+}
+
+static const struct file_operations tiler_fops = {
+ .open = tiler_open,
+ .ioctl = tiler_ioctl,
+ .release = tiler_release,
+ .mmap = tiler_mmap,
+};
+
+static s32 __init tiler_init(void)
+{
+ dev_t dev = 0;
+ s32 r = -1;
+ struct device *device = NULL;
+ struct tcm_pt div_pt;
+ struct tcm *sita = NULL;
+ struct tmm *tmm_pat = NULL;
+
+ /* Allocate tiler container manager (we share 1 on OMAP4) */
+ div_pt.x = TILER_WIDTH; /* hardcoded default */
+ div_pt.y = (3 * TILER_HEIGHT) / 4;
+ sita = sita_init(TILER_WIDTH, TILER_HEIGHT, (void *)&div_pt);
+
+ TCM_SET(TILFMT_8BIT, sita);
+ TCM_SET(TILFMT_16BIT, sita);
+ TCM_SET(TILFMT_32BIT, sita);
+ TCM_SET(TILFMT_PAGE, sita);
+
+ /* Allocate tiler memory manager (must have 1 unique TMM per TCM ) */
+ tmm_pat = tmm_pat_init(0);
+ TMM_SET(TILFMT_8BIT, tmm_pat);
+ TMM_SET(TILFMT_16BIT, tmm_pat);
+ TMM_SET(TILFMT_32BIT, tmm_pat);
+ TMM_SET(TILFMT_PAGE, tmm_pat);
+
+ /**
+ * Array of physical pages for PAT programming, which must be a 16-byte
+ * aligned physical address
+ */
+ dmac_va = dma_alloc_coherent(NULL, TILER_WIDTH * TILER_HEIGHT *
+ sizeof(*dmac_va), &dmac_pa, GFP_ATOMIC);
+ if (!dmac_va)
+ return -ENOMEM;
+
+ tiler_device = kmalloc(sizeof(*tiler_device), GFP_KERNEL);
+ if (!tiler_device || !sita || !tmm_pat) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ memset(tiler_device, 0x0, sizeof(*tiler_device));
+ if (tiler_major) {
+ dev = MKDEV(tiler_major, tiler_minor);
+ r = register_chrdev_region(dev, 1, "tiler");
+ } else {
+ r = alloc_chrdev_region(&dev, tiler_minor, 1, "tiler");
+ tiler_major = MAJOR(dev);
+ }
+
+ cdev_init(&tiler_device->cdev, &tiler_fops);
+ tiler_device->cdev.owner = THIS_MODULE;
+ tiler_device->cdev.ops = &tiler_fops;
+
+ r = cdev_add(&tiler_device->cdev, dev, 1);
+ if (r)
+ printk(KERN_ERR "cdev_add():failed\n");
+
+ tilerdev_class = class_create(THIS_MODULE, "tiler");
+
+ if (IS_ERR(tilerdev_class)) {
+ printk(KERN_ERR "class_create():failed\n");
+ goto error;
+ }
+
+ device = device_create(tilerdev_class, NULL, dev, NULL, "tiler");
+ if (device == NULL)
+ printk(KERN_ERR "device_create() fail\n");
+
+ r = platform_driver_register(&tiler_driver_ldm);
+
+ mutex_init(&mtx);
+ INIT_LIST_HEAD(&blocks);
+ INIT_LIST_HEAD(&procs);
+ INIT_LIST_HEAD(&orphan_areas);
+ INIT_LIST_HEAD(&orphan_onedim);
+ id = 0xda7a000;
+
+error:
+ /* TODO: error handling for device registration */
+ if (r) {
+ kfree(tiler_device);
+ tcm_deinit(sita);
+ tmm_deinit(tmm_pat);
+ }
+
+ return r;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Sin <davidsin@ti.com>");
+MODULE_AUTHOR("Lajos Molnar <molnar@ti.com>");
+module_init(tiler_init);
+module_exit(tiler_exit);
diff --git a/drivers/media/video/tiler/tiler_def.h b/drivers/media/video/tiler/tiler_def.h
new file mode 100644
index 000000000000..d92bfde8e452
--- /dev/null
+++ b/drivers/media/video/tiler/tiler_def.h
@@ -0,0 +1,158 @@
+/*
+ * tiler_def.h
+ *
+ * TILER driver support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef TILER_DEF_H
+#define TILER_DEF_H
+
+#define ROUND_UP_2P(a, b) (((a) + (b) - 1) & ~((b) - 1))
+#define DIVIDE_UP(a, b) (((a) + (b) - 1) / (b))
+#define ROUND_UP(a, b) (DIVIDE_UP(a, b) * (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define TILER_ACC_MODE_SHIFT (27)
+#define TILER_ACC_MODE_MASK (3)
+#define TILER_GET_ACC_MODE(x) ((enum tiler_fmt) (1 + \
+(((u32)x & (TILER_ACC_MODE_MASK<<TILER_ACC_MODE_SHIFT))>>TILER_ACC_MODE_SHIFT)))
+
+#define TILER_ALIAS_BASE (0x60000000)
+#define TILER_ACC_MODE_SHIFT (27)
+#define DMM_ACC_MODE_SHIFT (27)
+
+#define TIL_ALIAS_ADDR(x, access_mode)\
+((void *)(TILER_ALIAS_BASE | (u32)x | (access_mode << TILER_ACC_MODE_SHIFT)))
+
+#define TIL_ADDR(x, r, yi, xi, a)\
+((void *)((u32)x | (r << DMM_ROTATION_SHIFT) |\
+(yi << DMM_Y_INVERT_SHIFT) | (xi << DMM_X_INVERT_SHIFT) |\
+(a << DMM_ACC_MODE_SHIFT)))
+
+#define TILER_ALIAS_VIEW_CLEAR (~0xE0000000)
+
+#define DMM_X_INVERT_SHIFT (29)
+#define DMM_GET_X_INVERTED(x) ((((u32)x & (1<<DMM_X_INVERT_SHIFT)) > 0) ? 1 : 0)
+#define DMM_Y_INVERT_SHIFT (30)
+#define DMM_GET_Y_INVERTED(x) ((((u32)x & (1<<DMM_Y_INVERT_SHIFT)) > 0) ? 1 : 0)
+
+#define DMM_ROTATION_SHIFT (31)
+#define DMM_GET_ROTATED(x)\
+((((u32)x & ((u32)1<<DMM_ROTATION_SHIFT)) > 0) ? 1 : 0)
+
+#define DMM_ALIAS_VIEW_CLEAR (~0xE0000000)
+
+#define DMM_TILE_DIMM_X_MODE_8 (32)
+#define DMM_TILE_DIMM_Y_MODE_8 (32)
+
+#define DMM_TILE_DIMM_X_MODE_16 (32)
+#define DMM_TILE_DIMM_Y_MODE_16 (16)
+
+#define DMM_TILE_DIMM_X_MODE_32 (16)
+#define DMM_TILE_DIMM_Y_MODE_32 (16)
+
+#define DMM_PAGE_DIMM_X_MODE_8 (DMM_TILE_DIMM_X_MODE_8*2)
+#define DMM_PAGE_DIMM_Y_MODE_8 (DMM_TILE_DIMM_Y_MODE_8*2)
+
+#define DMM_PAGE_DIMM_X_MODE_16 (DMM_TILE_DIMM_X_MODE_16*2)
+#define DMM_PAGE_DIMM_Y_MODE_16 (DMM_TILE_DIMM_Y_MODE_16*2)
+
+#define DMM_PAGE_DIMM_X_MODE_32 (DMM_TILE_DIMM_X_MODE_32*2)
+#define DMM_PAGE_DIMM_Y_MODE_32 (DMM_TILE_DIMM_Y_MODE_32*2)
+
+#define DMM_HOR_X_ADDRSHIFT_8 (0)
+#define DMM_HOR_X_ADDRMASK_8 (0x3FFF)
+#define DMM_HOR_X_COOR_GET_8(x)\
+ (((unsigned long)x >> DMM_HOR_X_ADDRSHIFT_8) & DMM_HOR_X_ADDRMASK_8)
+#define DMM_HOR_X_PAGE_COOR_GET_8(x)\
+ (DMM_HOR_X_COOR_GET_8(x)/DMM_PAGE_DIMM_X_MODE_8)
+
+#define DMM_HOR_Y_ADDRSHIFT_8 (14)
+#define DMM_HOR_Y_ADDRMASK_8 (0x1FFF)
+#define DMM_HOR_Y_COOR_GET_8(x)\
+ (((unsigned long)x >> DMM_HOR_Y_ADDRSHIFT_8) & DMM_HOR_Y_ADDRMASK_8)
+#define DMM_HOR_Y_PAGE_COOR_GET_8(x)\
+ (DMM_HOR_Y_COOR_GET_8(x)/DMM_PAGE_DIMM_Y_MODE_8)
+
+#define DMM_HOR_X_ADDRSHIFT_16 (1)
+#define DMM_HOR_X_ADDRMASK_16 (0x7FFE)
+#define DMM_HOR_X_COOR_GET_16(x) (((unsigned long)x >> \
+ DMM_HOR_X_ADDRSHIFT_16) & DMM_HOR_X_ADDRMASK_16)
+#define DMM_HOR_X_PAGE_COOR_GET_16(x) (DMM_HOR_X_COOR_GET_16(x) / \
+ DMM_PAGE_DIMM_X_MODE_16)
+
+#define DMM_HOR_Y_ADDRSHIFT_16 (15)
+#define DMM_HOR_Y_ADDRMASK_16 (0xFFF)
+#define DMM_HOR_Y_COOR_GET_16(x) (((unsigned long)x >> \
+ DMM_HOR_Y_ADDRSHIFT_16) & DMM_HOR_Y_ADDRMASK_16)
+#define DMM_HOR_Y_PAGE_COOR_GET_16(x) (DMM_HOR_Y_COOR_GET_16(x) / \
+ DMM_PAGE_DIMM_Y_MODE_16)
+
+#define DMM_HOR_X_ADDRSHIFT_32 (2)
+#define DMM_HOR_X_ADDRMASK_32 (0x7FFC)
+#define DMM_HOR_X_COOR_GET_32(x) (((unsigned long)x >> \
+ DMM_HOR_X_ADDRSHIFT_32) & DMM_HOR_X_ADDRMASK_32)
+#define DMM_HOR_X_PAGE_COOR_GET_32(x) (DMM_HOR_X_COOR_GET_32(x) / \
+ DMM_PAGE_DIMM_X_MODE_32)
+
+#define DMM_HOR_Y_ADDRSHIFT_32 (15)
+#define DMM_HOR_Y_ADDRMASK_32 (0xFFF)
+#define DMM_HOR_Y_COOR_GET_32(x) (((unsigned long)x >> \
+ DMM_HOR_Y_ADDRSHIFT_32) & DMM_HOR_Y_ADDRMASK_32)
+#define DMM_HOR_Y_PAGE_COOR_GET_32(x) (DMM_HOR_Y_COOR_GET_32(x) / \
+ DMM_PAGE_DIMM_Y_MODE_32)
+
+#define DMM_VER_X_ADDRSHIFT_8 (14)
+#define DMM_VER_X_ADDRMASK_8 (0x1FFF)
+#define DMM_VER_X_COOR_GET_8(x)\
+ (((unsigned long)x >> DMM_VER_X_ADDRSHIFT_8) & DMM_VER_X_ADDRMASK_8)
+#define DMM_VER_X_PAGE_COOR_GET_8(x)\
+ (DMM_VER_X_COOR_GET_8(x)/DMM_PAGE_DIMM_X_MODE_8)
+
+#define DMM_VER_Y_ADDRSHIFT_8 (0)
+#define DMM_VER_Y_ADDRMASK_8 (0x3FFF)
+#define DMM_VER_Y_COOR_GET_8(x)\
+ (((unsigned long)x >> DMM_VER_Y_ADDRSHIFT_8) & DMM_VER_Y_ADDRMASK_8)
+#define DMM_VER_Y_PAGE_COOR_GET_8(x)\
+ (DMM_VER_Y_COOR_GET_8(x)/DMM_PAGE_DIMM_Y_MODE_8)
+
+#define DMM_VER_X_ADDRSHIFT_16 (14)
+#define DMM_VER_X_ADDRMASK_16 (0x1FFF)
+#define DMM_VER_X_COOR_GET_16(x) (((unsigned long)x >> \
+ DMM_VER_X_ADDRSHIFT_16) & DMM_VER_X_ADDRMASK_16)
+#define DMM_VER_X_PAGE_COOR_GET_16(x) (DMM_VER_X_COOR_GET_16(x) / \
+ DMM_PAGE_DIMM_X_MODE_16)
+
+#define DMM_VER_Y_ADDRSHIFT_16 (0)
+#define DMM_VER_Y_ADDRMASK_16 (0x3FFF)
+#define DMM_VER_Y_COOR_GET_16(x) (((unsigned long)x >> \
+ DMM_VER_Y_ADDRSHIFT_16) & DMM_VER_Y_ADDRMASK_16)
+#define DMM_VER_Y_PAGE_COOR_GET_16(x) (DMM_VER_Y_COOR_GET_16(x) / \
+ DMM_PAGE_DIMM_Y_MODE_16)
+
+#define DMM_VER_X_ADDRSHIFT_32 (15)
+#define DMM_VER_X_ADDRMASK_32 (0xFFF)
+#define DMM_VER_X_COOR_GET_32(x) (((unsigned long)x >> \
+ DMM_VER_X_ADDRSHIFT_32) & DMM_VER_X_ADDRMASK_32)
+#define DMM_VER_X_PAGE_COOR_GET_32(x) (DMM_VER_X_COOR_GET_32(x) / \
+ DMM_PAGE_DIMM_X_MODE_32)
+
+#define DMM_VER_Y_ADDRSHIFT_32 (0)
+#define DMM_VER_Y_ADDRMASK_32 (0x7FFF)
+#define DMM_VER_Y_COOR_GET_32(x) (((unsigned long)x >> \
+ DMM_VER_Y_ADDRSHIFT_32) & DMM_VER_Y_ADDRMASK_32)
+#define DMM_VER_Y_PAGE_COOR_GET_32(x) (DMM_VER_Y_COOR_GET_32(x) / \
+ DMM_PAGE_DIMM_Y_MODE_32)
+
+#endif
diff --git a/drivers/media/video/tiler/tiler_pack.c b/drivers/media/video/tiler/tiler_pack.c
new file mode 100644
index 000000000000..e21846909bc3
--- /dev/null
+++ b/drivers/media/video/tiler/tiler_pack.c
@@ -0,0 +1,269 @@
+/*
+ * tiler_pack.c
+ *
+ * TILER driver support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <mach/tiler.h>
+#include "tiler_def.h"
+
+void tiler_alloc_packed(s32 *count, enum tiler_fmt fmt, u32 width, u32 height,
+ void **sysptr, void **allocptr, s32 aligned)
+{
+ int til_width, bpp, bpt, buf_width, alloc_width, map_width;
+ int buf_map_width, n_per_m, m_per_a, i = 0, m, n;
+
+ /* Check input parameters for correctness */
+ if (!width || !height || !sysptr || !allocptr || !count ||
+ *count <= 0 || fmt < TILFMT_8BIT || fmt > TILFMT_32BIT) {
+ if (count)
+ *count = 0;
+ return;
+ }
+
+ /* tiler page width in pixels, bytes per pixel, tiler page in bytes */
+ til_width = fmt == TILFMT_32BIT ? 32 : 64;
+ bpp = 1 << (fmt - TILFMT_8BIT);
+ bpt = til_width * bpp;
+
+ /* width of buffer in tiled pages */
+ buf_width = DIVIDE_UP(width, til_width);
+
+ /* :TODO: for now tiler allocation width is 64-multiple */
+ alloc_width = ROUND_UP_2P(buf_width, 64);
+ map_width = TILER_PAGE / bpt;
+
+ /* ensure alignment if needed */
+ buf_map_width = ROUND_UP_2P(buf_width, map_width);
+
+ /* number of buffers in a map window */
+ n_per_m = aligned ? 1 : (buf_map_width / buf_width);
+
+ /* number of map windows per allocation */
+ m_per_a = alloc_width / buf_map_width;
+
+ printk(KERN_INFO "packing %d*%d buffers into an allocation\n",
+ n_per_m, m_per_a);
+
+ while (i < *count) {
+ /* allocate required width of a frame to fit remaining
+ frames */
+ int n_alloc, m_alloc, tiles, res;
+ void *base;
+
+ n_alloc = MIN(*count - i, m_per_a * n_per_m);
+ m_alloc = DIVIDE_UP(n_alloc, n_per_m);
+ tiles = ((m_alloc - 1) * map_width +
+ buf_width * (n_alloc - (m_alloc - 1) * n_per_m));
+
+ res = tiler_alloc(fmt, til_width * tiles, height,
+ (u32 *)sysptr + i);
+ if (res != 0)
+ break;
+
+ /* mark allocation */
+ base = allocptr[i] = sysptr[i];
+ i++;
+
+ /* portion out remaining buffers */
+ for (m = 0; m < m_per_a; m++, base += bpt * buf_map_width) {
+ for (n = 0; n < n_per_m; n++) {
+ /* first buffer is already allocated */
+ if (n + m == 0)
+ continue;
+
+ /* stop if we are done */
+ if (i == *count)
+ break;
+
+ /* set buffer address */
+ sysptr[i] = base + bpt * n * buf_width;
+ allocptr[i++] = NULL;
+ }
+ }
+ }
+
+ /* mark how many buffers we allocated */
+ *count = i;
+}
+EXPORT_SYMBOL(tiler_alloc_packed);
+
+static int layout_packed_nv12(char *offsets, int y_width, int uv_width,
+ void **buf, int blocks, int i,
+ void **y_sysptr, void **uv_sysptr,
+ void **y_allocptr, void **uv_allocptr)
+{
+ int j;
+ for (j = 0; j < blocks; j++, offsets += 3) {
+ int page_offset = (63 & (int) offsets[0])
+ + y_width * ((int) offsets[1])
+ + uv_width * (int) offsets[2];
+ void *base = buf[offsets[0] >> 6] + 64 * page_offset;
+
+ if (j & 1) {
+ /* convert 8-bit to 16-bit view */
+ /* this formula only works for even ys */
+ uv_sysptr[i] = base + (0x3FFF & (unsigned long) base)
+ + 0x8000000;
+ uv_allocptr[i] = page_offset ? NULL : uv_sysptr[i];
+ i++;
+ } else {
+ y_sysptr[i] = base;
+ y_allocptr[i] = page_offset ? NULL : y_sysptr[i];
+ }
+ }
+ return i;
+}
+
+void tiler_alloc_packed_nv12(s32 *count, u32 width, u32 height, void **y_sysptr,
+ void **uv_sysptr, void **y_allocptr,
+ void **uv_allocptr, s32 aligned)
+{
+ /* optimized packing table */
+ /* we read this table from beginning to end, and determine whether
+ the optimization meets our requirement (e.g. allocating at least
+ i buffers, with max w y-width, and alignment a. If not, we get
+ to the next element. Otherwise we do the allocation. The table
+ is constructed in such a way that if an interim tiler allocation
+ fails, the next matching rule for the scenario will be able to
+ use the buffers already allocated. */
+
+#define MAX_BUFS_TO_PACK 3
+ void *buf[MAX_BUFS_TO_PACK];
+ int n_buf, buf_w[MAX_BUFS_TO_PACK];
+
+ char packing[] = {
+ /* min(i), max(w), aligned, buffers to alloc */
+ 5, 16, 0, 2,
+ /* buffer widths in a + b * w(y) + c * w(uv) */
+ 64, 0, 0, 64, 0, 0,
+ /* tiler-page offsets in
+ a + b * w(y) + c * w(uv) */
+ 0, 0, 0, 32, 0, 0,
+ 16, 0, 0, 40, 0, 0,
+ 64, 0, 0, 96, 0, 0,
+ 80, 0, 0, 104, 0, 0,
+ 112, 0, 0, 56, 0, 0,
+
+ 2, 16, 0, 1,
+ 32, 0, 2,
+ 0, 0, 0, 32, 0, 0,
+ 0, 0, 2, 32, 0, 1,
+
+ 2, 20, 0, 1,
+ 42, 1, 0,
+ 0, 0, 0, 32, 0, 0,
+ 42, 0, 0, 21, 0, 0,
+
+ 3, 24, 0, 2,
+ 48, 0, 1, 32, 1, 0,
+ 0, 0, 0, 64, 0, 0,
+ 24, 0, 0, 76, 0, 0,
+ 96, 0, 0, 48, 0, 0,
+
+ 4, 32, 0, 3,
+ 48, 0, 1, 32, 1, 0, 32, 1, 0,
+ 0, 0, 0, 32, 0, 0,
+ 96, 0, 0, 48, 0, 0,
+ 64, 0, 0, 128, 0, 0,
+ 160, 0, 0, 144, 0, 0,
+
+ /* this is needed for soft landing if prior allocation fails
+ after two buffers */
+ 2, 32, 1, 2,
+ 32, 0, 1, 32, 0, 1,
+ 0, 0, 0, 32, 0, 0,
+ 64, 0, 0, 96, 0, 0,
+
+ 1, 32, 1, 1,
+ 32, 0, 1,
+ 0, 0, 0, 32, 0, 0,
+
+ 2, 64, 1, 3,
+ 0, 1, 0, 32, 0, 1, 0, 1, 0,
+ 0, 0, 0, 64, 0, 0,
+ 128, 0, 0, 96, 0, 0,
+ /* this is the basic NV12 allocation using 2 buffers */
+ 1, 0, 1, 2,
+ 0, 1, 0, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0 };
+ int y_width, uv_width, i = 0;
+
+ /* Check input parameters for correctness */
+ if (!width || !height || !y_sysptr || !y_allocptr || !count ||
+ !uv_sysptr || !uv_allocptr || *count <= 0) {
+ if (count)
+ *count = 0;
+ return;
+ }
+
+ y_width = DIVIDE_UP(width, 64);
+ uv_width = DIVIDE_UP(width >> 1, 64);
+
+ while (i < *count) {
+ int n_alloc = *count - i;
+ char *p = packing;
+ n_buf = 0;
+
+ /* skip packings that do not apply */
+ while (*p) {
+ /* see if this packing applies */
+ if (p[0] <= n_alloc &&
+ (!p[1] || p[1] >= y_width) &&
+ (!aligned || p[2])) {
+
+ /* allocate buffers */
+ while (n_buf < p[3]) {
+ buf_w[n_buf] = p[4 + 3 * n_buf] +
+ y_width * p[5 + 3 * n_buf] +
+ uv_width * p[6 + 3 * n_buf];
+
+ if (0 != tiler_alloc(
+ TILFMT_8BIT, buf_w[n_buf] * 64,
+ height, (u32 *)buf + n_buf))
+ break;
+ n_buf++;
+ }
+
+ /* if successfully allocated buffers */
+ if (n_buf >= p[3]) {
+ i = layout_packed_nv12(p + 4 + 3 * p[3],
+ y_width,
+ uv_width,
+ buf, 2 * p[0], i,
+ y_sysptr,
+ uv_sysptr,
+ y_allocptr,
+ uv_allocptr);
+ break;
+ }
+ }
+
+ p += 4 + 3 * p[3] + 6 * p[0];
+ }
+
+ /* if allocation failed free any outstanding buffers and stop */
+ if (!*p) {
+ while (n_buf > 0)
+ tiler_free((unsigned long)(buf[--n_buf]));
+ break;
+ }
+ }
+
+ /* mark how many buffers we allocated */
+ *count = i;
+}
+EXPORT_SYMBOL(tiler_alloc_packed_nv12);
diff --git a/drivers/media/video/tiler/tiler_rot.c b/drivers/media/video/tiler/tiler_rot.c
new file mode 100644
index 000000000000..aa38d72187db
--- /dev/null
+++ b/drivers/media/video/tiler/tiler_rot.c
@@ -0,0 +1,239 @@
+/*
+ * tiler_rot.c
+ *
+ * TILER driver support functions for TI OMAP processors.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <mach/tiler.h>
+#include "tiler_def.h"
+
+#define DMM_SHIFT_PER_X_8 0
+#define DMM_SHIFT_PER_Y_8 0
+#define DMM_SHIFT_PER_X_16 0
+#define DMM_SHIFT_PER_Y_16 1
+#define DMM_SHIFT_PER_X_32 1
+#define DMM_SHIFT_PER_Y_32 1
+#define DMM_SHIFT_PER_X_PAGE 6
+#define DMM_SHIFT_PER_Y_PAGE 6
+
+#define DMM_TILER_THE(NAME) (1 << DMM_TILER_##NAME##_BITS)
+#define DMM_TILER_THE_(N, NAME) (1 << DMM_TILER_##NAME##_BITS_(N))
+
+#define DMM_TILER_CONT_WIDTH_BITS 14
+#define DMM_TILER_CONT_HEIGHT_BITS 13
+
+#define DMM_SHIFT_PER_P_(N) (DMM_SHIFT_PER_X_##N + DMM_SHIFT_PER_Y_##N)
+
+#define DMM_TILER_CONT_HEIGHT_BITS_(N) \
+ (DMM_TILER_CONT_HEIGHT_BITS - DMM_SHIFT_PER_Y_##N)
+#define DMM_TILER_CONT_WIDTH_BITS_(N) \
+ (DMM_TILER_CONT_WIDTH_BITS - DMM_SHIFT_PER_X_##N)
+
+#define DMM_TILER_MASK(bits) ((1 << (bits)) - 1)
+
+#define DMM_TILER_GET_OFFSET_(N, var) \
+ ((((u32) var) & DMM_TILER_MASK(DMM_TILER_CONT_WIDTH_BITS + \
+ DMM_TILER_CONT_HEIGHT_BITS)) >> DMM_SHIFT_PER_P_(N))
+
+#define DMM_TILER_GET_0_X_(N, var) \
+ (DMM_TILER_GET_OFFSET_(N, var) & \
+ DMM_TILER_MASK(DMM_TILER_CONT_WIDTH_BITS_(N)))
+#define DMM_TILER_GET_0_Y_(N, var) \
+ (DMM_TILER_GET_OFFSET_(N, var) >> DMM_TILER_CONT_WIDTH_BITS_(N))
+#define DMM_TILER_GET_90_X_(N, var) \
+ (DMM_TILER_GET_OFFSET_(N, var) & \
+ DMM_TILER_MASK(DMM_TILER_CONT_HEIGHT_BITS_(N)))
+#define DMM_TILER_GET_90_Y_(N, var) \
+ (DMM_TILER_GET_OFFSET_(N, var) >> DMM_TILER_CONT_HEIGHT_BITS_(N))
+
+#define DMM_TILER_STRIDE_0_(N) \
+ (DMM_TILER_THE(CONT_WIDTH) << DMM_SHIFT_PER_Y_##N)
+#define DMM_TILER_STRIDE_90_(N) \
+ (DMM_TILER_THE(CONT_HEIGHT) << DMM_SHIFT_PER_X_##N)
+
+void tiler_get_natural_xy(u32 tsptr, u32 *x, u32 *y)
+{
+ u32 x_bits, y_bits, offset;
+ enum tiler_fmt fmt;
+
+ fmt = TILER_GET_ACC_MODE(tsptr);
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(8);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(8);
+ offset = DMM_TILER_GET_OFFSET_(8, tsptr);
+ break;
+ case TILFMT_16BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(16);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(16);
+ offset = DMM_TILER_GET_OFFSET_(16, tsptr);
+ break;
+ case TILFMT_32BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(32);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(32);
+ offset = DMM_TILER_GET_OFFSET_(32, tsptr);
+ break;
+ case TILFMT_PAGE:
+ default:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(PAGE);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(PAGE);
+ offset = DMM_TILER_GET_OFFSET_(PAGE, tsptr);
+ break;
+ }
+
+ if (DMM_GET_ROTATED(tsptr)) {
+ *x = offset >> y_bits;
+ *y = offset & DMM_TILER_MASK(y_bits);
+ } else {
+ *x = offset & DMM_TILER_MASK(x_bits);
+ *y = offset >> x_bits;
+ }
+
+ if (DMM_GET_X_INVERTED(tsptr))
+ *x ^= DMM_TILER_MASK(x_bits);
+ if (DMM_GET_Y_INVERTED(tsptr))
+ *y ^= DMM_TILER_MASK(y_bits);
+}
+
+u32 tiler_get_address(struct tiler_view_orient orient,
+ enum tiler_fmt fmt, u32 x, u32 y)
+{
+ u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment;
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(8);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(8);
+ alignment = DMM_SHIFT_PER_P_(8);
+ break;
+ case TILFMT_16BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(16);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(16);
+ alignment = DMM_SHIFT_PER_P_(16);
+ break;
+ case TILFMT_32BIT:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(32);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(32);
+ alignment = DMM_SHIFT_PER_P_(32);
+ break;
+ case TILFMT_PAGE:
+ default:
+ x_bits = DMM_TILER_CONT_WIDTH_BITS_(PAGE);
+ y_bits = DMM_TILER_CONT_HEIGHT_BITS_(PAGE);
+ alignment = DMM_SHIFT_PER_P_(PAGE);
+ break;
+ }
+
+ x_mask = DMM_TILER_MASK(x_bits);
+ y_mask = DMM_TILER_MASK(y_bits);
+ if (x < 0 || x > x_mask || y < 0 || y > y_mask)
+ return 0;
+
+ if (orient.x_invert)
+ x ^= x_mask;
+ if (orient.y_invert)
+ y ^= y_mask;
+
+ if (orient.rotate_90)
+ tmp = ((x << y_bits) + y);
+ else
+ tmp = ((y << x_bits) + x);
+
+ return (u32)
+ TIL_ADDR((tmp << alignment), (orient.rotate_90 ? 1 : 0),
+ (orient.y_invert ? 1 : 0), (orient.x_invert ? 1 : 0),
+ (fmt - 1));
+}
+
+u32 tiler_reorient_addr(u32 tsptr, struct tiler_view_orient orient)
+{
+ u32 x, y;
+
+ tiler_get_natural_xy(tsptr, &x, &y);
+ return tiler_get_address(orient, TILER_GET_ACC_MODE(tsptr), x, y);
+}
+EXPORT_SYMBOL(tiler_reorient_addr);
+
+u32 tiler_get_natural_addr(void *sys_ptr)
+{
+ return (u32)sys_ptr & DMM_ALIAS_VIEW_CLEAR;
+}
+EXPORT_SYMBOL(tiler_get_natural_addr);
+
+u32 tiler_reorient_topleft(u32 tsptr, struct tiler_view_orient orient,
+ u32 width, u32 height)
+{
+ enum tiler_fmt fmt;
+ u32 x, y;
+
+ fmt = TILER_GET_ACC_MODE(tsptr);
+
+ tiler_get_natural_xy(tsptr, &x, &y);
+
+ if (DMM_GET_X_INVERTED(tsptr))
+ x -= width - 1;
+ if (DMM_GET_Y_INVERTED(tsptr))
+ y -= height - 1;
+
+ if (orient.x_invert)
+ x += width - 1;
+ if (orient.y_invert)
+ y += height - 1;
+
+ return tiler_get_address(orient, fmt, x, y);
+}
+EXPORT_SYMBOL(tiler_reorient_topleft);
+
+u32 tiler_stride(u32 tsptr)
+{
+ enum tiler_fmt fmt;
+
+ fmt = TILER_GET_ACC_MODE(tsptr);
+
+ switch (fmt) {
+ case TILFMT_8BIT:
+ return DMM_GET_ROTATED(tsptr) ?
+ DMM_TILER_STRIDE_90_(8) : DMM_TILER_STRIDE_0_(8);
+ case TILFMT_16BIT:
+ return DMM_GET_ROTATED(tsptr) ?
+ DMM_TILER_STRIDE_90_(16) : DMM_TILER_STRIDE_0_(16);
+ case TILFMT_32BIT:
+ return DMM_GET_ROTATED(tsptr) ?
+ DMM_TILER_STRIDE_90_(32) : DMM_TILER_STRIDE_0_(32);
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(tiler_stride);
+
+void tiler_rotate_view(struct tiler_view_orient *orient, u32 rotation)
+{
+ rotation = (rotation / 90) & 3;
+
+ if (rotation & 2) {
+ orient->x_invert = !orient->x_invert;
+ orient->y_invert = !orient->y_invert;
+ }
+
+ if (rotation & 1) {
+ if (orient->rotate_90)
+ orient->y_invert = !orient->y_invert;
+ else
+ orient->x_invert = !orient->x_invert;
+ orient->rotate_90 = !orient->rotate_90;
+ }
+}
+EXPORT_SYMBOL(tiler_rotate_view);