aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndy Gross <andy.gross@ti.com>2011-12-05 20:19:21 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-12-08 18:03:54 -0500
commit71e8831f6407542afd1d8888d3cca13e677034ef (patch)
tree15e531b52349d81137b796b3ba8e292f2a195439 /drivers
parente0134715aa136088c649d0d2f833710c7c9e28b0 (diff)
drm/omap: DMM/TILER support for OMAP4+ platform
Dynamic Memory Manager (DMM) is a hardware block in the OMAP4+ processor that contains at least one TILER instance. TILER, or Tiling and Isometric Lightweight Engine for Rotation, provides IOMMU capabilities through the use of a physical address translation table. The TILER also provides zero cost rotation and mirroring. The TILER provides both 1D and 2D access by providing different views or address ranges that can be used to access the physical memory that has been mapped in through the PAT. Access to the 1D view results in linear access to the underlying memory. Access to the 2D views result in tiled access to the underlying memory resulted in increased efficiency. The TILER address space is managed by a tiler container manager (TCM) and allocates the address space through the use of the Simple Tiler Allocation algorithm (SiTA). The purpose of the algorithm is to keep fragmentation of the address space as low as possible. Signed-off-by: Andy Gross <andy.gross@ti.com> Signed-off-by: Rob Clark <rob@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/omapdrm/Makefile10
-rw-r--r--drivers/staging/omapdrm/TODO1
-rw-r--r--drivers/staging/omapdrm/omap_dmm_priv.h187
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.c672
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.h130
-rw-r--r--drivers/staging/omapdrm/omap_drm.h2
-rw-r--r--drivers/staging/omapdrm/omap_drv.c21
-rw-r--r--drivers/staging/omapdrm/omap_priv.h7
-rw-r--r--drivers/staging/omapdrm/tcm-sita.c703
-rw-r--r--drivers/staging/omapdrm/tcm-sita.h95
-rw-r--r--drivers/staging/omapdrm/tcm.h326
11 files changed, 2143 insertions, 11 deletions
diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
index 4aa9a2f34d4a..275054a3451e 100644
--- a/drivers/staging/omapdrm/Makefile
+++ b/drivers/staging/omapdrm/Makefile
@@ -4,7 +4,15 @@
4# 4#
5 5
6ccflags-y := -Iinclude/drm -Werror 6ccflags-y := -Iinclude/drm -Werror
7omapdrm-y := omap_drv.o omap_crtc.o omap_encoder.o omap_connector.o omap_fb.o omap_fbdev.o omap_gem.o 7omapdrm-y := omap_drv.o \
8 omap_crtc.o \
9 omap_encoder.o \
10 omap_connector.o \
11 omap_fb.o \
12 omap_fbdev.o \
13 omap_gem.o \
14 omap_dmm_tiler.o \
15 tcm-sita.o
8 16
9# temporary: 17# temporary:
10omapdrm-y += omap_gem_helpers.o 18omapdrm-y += omap_gem_helpers.o
diff --git a/drivers/staging/omapdrm/TODO b/drivers/staging/omapdrm/TODO
index 17781c954a83..18677e777368 100644
--- a/drivers/staging/omapdrm/TODO
+++ b/drivers/staging/omapdrm/TODO
@@ -22,6 +22,7 @@ TODO
22. Review DSS vs KMS mismatches. The omap_dss_device is sort of part encoder, 22. Review DSS vs KMS mismatches. The omap_dss_device is sort of part encoder,
23 part connector. Which results in a bit of duct tape to fwd calls from 23 part connector. Which results in a bit of duct tape to fwd calls from
24 encoder to connector. Possibly this could be done a bit better. 24 encoder to connector. Possibly this could be done a bit better.
25. Add debugfs information for DMM/TILER
25 26
26Userspace: 27Userspace:
27. git://github.com/robclark/xf86-video-omap.git 28. git://github.com/robclark/xf86-video-omap.git
diff --git a/drivers/staging/omapdrm/omap_dmm_priv.h b/drivers/staging/omapdrm/omap_dmm_priv.h
new file mode 100644
index 000000000000..65b990c8e16a
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_dmm_priv.h
@@ -0,0 +1,187 @@
1/*
2 *
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation version 2.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16#ifndef OMAP_DMM_PRIV_H
17#define OMAP_DMM_PRIV_H
18
19#define DMM_REVISION 0x000
20#define DMM_HWINFO 0x004
21#define DMM_LISA_HWINFO 0x008
22#define DMM_DMM_SYSCONFIG 0x010
23#define DMM_LISA_LOCK 0x01C
24#define DMM_LISA_MAP__0 0x040
25#define DMM_LISA_MAP__1 0x044
26#define DMM_TILER_HWINFO 0x208
27#define DMM_TILER_OR__0 0x220
28#define DMM_TILER_OR__1 0x224
29#define DMM_PAT_HWINFO 0x408
30#define DMM_PAT_GEOMETRY 0x40C
31#define DMM_PAT_CONFIG 0x410
32#define DMM_PAT_VIEW__0 0x420
33#define DMM_PAT_VIEW__1 0x424
34#define DMM_PAT_VIEW_MAP__0 0x440
35#define DMM_PAT_VIEW_MAP_BASE 0x460
36#define DMM_PAT_IRQ_EOI 0x478
37#define DMM_PAT_IRQSTATUS_RAW 0x480
38#define DMM_PAT_IRQSTATUS 0x490
39#define DMM_PAT_IRQENABLE_SET 0x4A0
40#define DMM_PAT_IRQENABLE_CLR 0x4B0
41#define DMM_PAT_STATUS__0 0x4C0
42#define DMM_PAT_STATUS__1 0x4C4
43#define DMM_PAT_STATUS__2 0x4C8
44#define DMM_PAT_STATUS__3 0x4CC
45#define DMM_PAT_DESCR__0 0x500
46#define DMM_PAT_DESCR__1 0x510
47#define DMM_PAT_DESCR__2 0x520
48#define DMM_PAT_DESCR__3 0x530
49#define DMM_PEG_HWINFO 0x608
50#define DMM_PEG_PRIO 0x620
51#define DMM_PEG_PRIO_PAT 0x640
52
53#define DMM_IRQSTAT_DST (1<<0)
54#define DMM_IRQSTAT_LST (1<<1)
55#define DMM_IRQSTAT_ERR_INV_DSC (1<<2)
56#define DMM_IRQSTAT_ERR_INV_DATA (1<<3)
57#define DMM_IRQSTAT_ERR_UPD_AREA (1<<4)
58#define DMM_IRQSTAT_ERR_UPD_CTRL (1<<5)
59#define DMM_IRQSTAT_ERR_UPD_DATA (1<<6)
60#define DMM_IRQSTAT_ERR_LUT_MISS (1<<7)
61
62#define DMM_IRQSTAT_ERR_MASK (DMM_IRQ_STAT_ERR_INV_DSC | \
63 DMM_IRQ_STAT_ERR_INV_DATA | \
64 DMM_IRQ_STAT_ERR_UPD_AREA | \
65 DMM_IRQ_STAT_ERR_UPD_CTRL | \
66 DMM_IRQ_STAT_ERR_UPD_DATA | \
67 DMM_IRQ_STAT_ERR_LUT_MISS)
68
69#define DMM_PATSTATUS_READY (1<<0)
70#define DMM_PATSTATUS_VALID (1<<1)
71#define DMM_PATSTATUS_RUN (1<<2)
72#define DMM_PATSTATUS_DONE (1<<3)
73#define DMM_PATSTATUS_LINKED (1<<4)
74#define DMM_PATSTATUS_BYPASSED (1<<7)
75#define DMM_PATSTATUS_ERR_INV_DESCR (1<<10)
76#define DMM_PATSTATUS_ERR_INV_DATA (1<<11)
77#define DMM_PATSTATUS_ERR_UPD_AREA (1<<12)
78#define DMM_PATSTATUS_ERR_UPD_CTRL (1<<13)
79#define DMM_PATSTATUS_ERR_UPD_DATA (1<<14)
80#define DMM_PATSTATUS_ERR_ACCESS (1<<15)
81
82#define DMM_PATSTATUS_ERR (DMM_PATSTATUS_ERR_INV_DESCR | \
83 DMM_PATSTATUS_ERR_INV_DATA | \
84 DMM_PATSTATUS_ERR_UPD_AREA | \
85 DMM_PATSTATUS_ERR_UPD_CTRL | \
86 DMM_PATSTATUS_ERR_UPD_DATA | \
87 DMM_PATSTATUS_ERR_ACCESS)
88
89
90
91enum {
92 PAT_STATUS,
93 PAT_DESCR
94};
95
96struct pat_ctrl {
97 u32 start:4;
98 u32 dir:4;
99 u32 lut_id:8;
100 u32 sync:12;
101 u32 ini:4;
102};
103
104struct pat {
105 uint32_t next_pa;
106 struct pat_area area;
107 struct pat_ctrl ctrl;
108 uint32_t data_pa;
109};
110
111#define DMM_FIXED_RETRY_COUNT 1000
112
113/* create refill buffer big enough to refill all slots, plus 3 descriptors..
114 * 3 descriptors is probably the worst-case for # of 2d-slices in a 1d area,
115 * but I guess you don't hit that worst case at the same time as full area
116 * refill
117 */
118#define DESCR_SIZE 128
119#define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE))
120
121struct dmm;
122
123struct dmm_txn {
124 void *engine_handle;
125 struct tcm *tcm;
126
127 uint8_t *current_va;
128 dma_addr_t current_pa;
129
130 struct pat *last_pat;
131};
132
133struct refill_engine {
134 int id;
135 struct dmm *dmm;
136 struct tcm *tcm;
137
138 uint8_t *refill_va;
139 dma_addr_t refill_pa;
140
141 /* only one trans per engine for now */
142 struct dmm_txn txn;
143
144 /* offset to lut associated with container */
145 u32 *lut_offset;
146
147 wait_queue_head_t wait_for_refill;
148
149 struct list_head idle_node;
150};
151
152struct dmm {
153 struct device *dev;
154 void __iomem *base;
155 int irq;
156
157 struct page *dummy_page;
158 dma_addr_t dummy_pa;
159
160 void *refill_va;
161 dma_addr_t refill_pa;
162
163 /* refill engines */
164 struct semaphore engine_sem;
165 struct list_head idle_head;
166 struct refill_engine *engines;
167 int num_engines;
168
169 /* container information */
170 int container_width;
171 int container_height;
172 int lut_width;
173 int lut_height;
174 int num_lut;
175
176 /* array of LUT - TCM containers */
177 struct tcm **tcm;
178
179 /* LUT table storage */
180 u32 *lut;
181
182 /* allocation list and lock */
183 struct list_head alloc_head;
184 spinlock_t list_lock;
185};
186
187#endif
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c
new file mode 100644
index 000000000000..9ed5215c65ac
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.c
@@ -0,0 +1,672 @@
1/*
2 * DMM IOMMU driver support functions for TI OMAP processors.
3 *
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation version 2.
12 *
13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14 * kind, whether express or implied; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/platform_device.h> /* platform_device() */
21#include <linux/errno.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/interrupt.h>
25#include <linux/dma-mapping.h>
26#include <linux/slab.h>
27#include <linux/vmalloc.h>
28#include <linux/delay.h>
29#include <linux/mm.h>
30#include <linux/time.h>
31#include <linux/list.h>
32#include <linux/semaphore.h>
33
34#include "omap_dmm_tiler.h"
35#include "omap_dmm_priv.h"
36
37/* mappings for associating views to luts */
38static struct tcm *containers[TILFMT_NFORMATS];
39static struct dmm *omap_dmm;
40
41/* Geometry table */
42#define GEOM(xshift, yshift, bytes_per_pixel) { \
43 .x_shft = (xshift), \
44 .y_shft = (yshift), \
45 .cpp = (bytes_per_pixel), \
46 .slot_w = 1 << (SLOT_WIDTH_BITS - (xshift)), \
47 .slot_h = 1 << (SLOT_HEIGHT_BITS - (yshift)), \
48 }
49
50static const struct {
51 uint32_t x_shft; /* unused X-bits (as part of bpp) */
52 uint32_t y_shft; /* unused Y-bits (as part of bpp) */
53 uint32_t cpp; /* bytes/chars per pixel */
54 uint32_t slot_w; /* width of each slot (in pixels) */
55 uint32_t slot_h; /* height of each slot (in pixels) */
56} geom[TILFMT_NFORMATS] = {
57 [TILFMT_8BIT] = GEOM(0, 0, 1),
58 [TILFMT_16BIT] = GEOM(0, 1, 2),
59 [TILFMT_32BIT] = GEOM(1, 1, 4),
60 [TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
61};
62
63
64/* lookup table for registers w/ per-engine instances */
65static const uint32_t reg[][4] = {
66 [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
67 DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
68 [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
69 DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
70};
71
72/* simple allocator to grab next 16 byte aligned memory from txn */
73static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa)
74{
75 void *ptr;
76 struct refill_engine *engine = txn->engine_handle;
77
78 /* dmm programming requires 16 byte aligned addresses */
79 txn->current_pa = round_up(txn->current_pa, 16);
80 txn->current_va = (void *)round_up((long)txn->current_va, 16);
81
82 ptr = txn->current_va;
83 *pa = txn->current_pa;
84
85 txn->current_pa += sz;
86 txn->current_va += sz;
87
88 BUG_ON((txn->current_va - engine->refill_va) > REFILL_BUFFER_SIZE);
89
90 return ptr;
91}
92
93/* check status and spin until wait_mask comes true */
94static int wait_status(struct refill_engine *engine, uint32_t wait_mask)
95{
96 struct dmm *dmm = engine->dmm;
97 uint32_t r = 0, err, i;
98
99 i = DMM_FIXED_RETRY_COUNT;
100 while (true) {
101 r = readl(dmm->base + reg[PAT_STATUS][engine->id]);
102 err = r & DMM_PATSTATUS_ERR;
103 if (err)
104 return -EFAULT;
105
106 if ((r & wait_mask) == wait_mask)
107 break;
108
109 if (--i == 0)
110 return -ETIMEDOUT;
111
112 udelay(1);
113 }
114
115 return 0;
116}
117
118irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
119{
120 struct dmm *dmm = arg;
121 uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS);
122 int i;
123
124 /* ack IRQ */
125 writel(status, dmm->base + DMM_PAT_IRQSTATUS);
126
127 for (i = 0; i < dmm->num_engines; i++) {
128 if (status & DMM_IRQSTAT_LST)
129 wake_up_interruptible(&dmm->engines[i].wait_for_refill);
130
131 status >>= 8;
132 }
133
134 return IRQ_HANDLED;
135}
136
137/**
138 * Get a handle for a DMM transaction
139 */
140static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm)
141{
142 struct dmm_txn *txn = NULL;
143 struct refill_engine *engine = NULL;
144
145 down(&dmm->engine_sem);
146
147 /* grab an idle engine */
148 spin_lock(&dmm->list_lock);
149 if (!list_empty(&dmm->idle_head)) {
150 engine = list_entry(dmm->idle_head.next, struct refill_engine,
151 idle_node);
152 list_del(&engine->idle_node);
153 }
154 spin_unlock(&dmm->list_lock);
155
156 BUG_ON(!engine);
157
158 txn = &engine->txn;
159 engine->tcm = tcm;
160 txn->engine_handle = engine;
161 txn->last_pat = NULL;
162 txn->current_va = engine->refill_va;
163 txn->current_pa = engine->refill_pa;
164
165 return txn;
166}
167
168/**
169 * Add region to DMM transaction. If pages or pages[i] is NULL, then the
170 * corresponding slot is cleared (ie. dummy_pa is programmed)
171 */
172static int dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
173 struct page **pages)
174{
175 dma_addr_t pat_pa = 0;
176 uint32_t *data;
177 struct pat *pat;
178 struct refill_engine *engine = txn->engine_handle;
179 int columns = (1 + area->x1 - area->x0);
180 int rows = (1 + area->y1 - area->y0);
181 int i = columns*rows;
182 u32 *lut = omap_dmm->lut + (engine->tcm->lut_id * omap_dmm->lut_width *
183 omap_dmm->lut_height) +
184 (area->y0 * omap_dmm->lut_width) + area->x0;
185
186 pat = alloc_dma(txn, sizeof(struct pat), &pat_pa);
187
188 if (txn->last_pat)
189 txn->last_pat->next_pa = (uint32_t)pat_pa;
190
191 pat->area = *area;
192 pat->ctrl = (struct pat_ctrl){
193 .start = 1,
194 .lut_id = engine->tcm->lut_id,
195 };
196
197 data = alloc_dma(txn, 4*i, &pat->data_pa);
198
199 while (i--) {
200 data[i] = (pages && pages[i]) ?
201 page_to_phys(pages[i]) : engine->dmm->dummy_pa;
202 }
203
204 /* fill in lut with new addresses */
205 for (i = 0; i < rows; i++, lut += omap_dmm->lut_width)
206 memcpy(lut, &data[i*columns], columns * sizeof(u32));
207
208 txn->last_pat = pat;
209
210 return 0;
211}
212
213/**
214 * Commit the DMM transaction.
215 */
216static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
217{
218 int ret = 0;
219 struct refill_engine *engine = txn->engine_handle;
220 struct dmm *dmm = engine->dmm;
221
222 if (!txn->last_pat) {
223 dev_err(engine->dmm->dev, "need at least one txn\n");
224 ret = -EINVAL;
225 goto cleanup;
226 }
227
228 txn->last_pat->next_pa = 0;
229
230 /* write to PAT_DESCR to clear out any pending transaction */
231 writel(0x0, dmm->base + reg[PAT_DESCR][engine->id]);
232
233 /* wait for engine ready: */
234 ret = wait_status(engine, DMM_PATSTATUS_READY);
235 if (ret) {
236 ret = -EFAULT;
237 goto cleanup;
238 }
239
240 /* kick reload */
241 writel(engine->refill_pa,
242 dmm->base + reg[PAT_DESCR][engine->id]);
243
244 if (wait) {
245 if (wait_event_interruptible_timeout(engine->wait_for_refill,
246 wait_status(engine, DMM_PATSTATUS_READY) == 0,
247 msecs_to_jiffies(1)) <= 0) {
248 dev_err(dmm->dev, "timed out waiting for done\n");
249 ret = -ETIMEDOUT;
250 }
251 }
252
253cleanup:
254 spin_lock(&dmm->list_lock);
255 list_add(&engine->idle_node, &dmm->idle_head);
256 spin_unlock(&dmm->list_lock);
257
258 up(&omap_dmm->engine_sem);
259 return ret;
260}
261
262/*
263 * DMM programming
264 */
265static int fill(struct tcm_area *area, struct page **pages, bool wait)
266{
267 int ret = 0;
268 struct tcm_area slice, area_s;
269 struct dmm_txn *txn;
270
271 txn = dmm_txn_init(omap_dmm, area->tcm);
272 if (IS_ERR_OR_NULL(txn))
273 return PTR_ERR(txn);
274
275 tcm_for_each_slice(slice, *area, area_s) {
276 struct pat_area p_area = {
277 .x0 = slice.p0.x, .y0 = slice.p0.y,
278 .x1 = slice.p1.x, .y1 = slice.p1.y,
279 };
280
281 ret = dmm_txn_append(txn, &p_area, pages);
282 if (ret)
283 goto fail;
284
285 if (pages)
286 pages += tcm_sizeof(slice);
287 }
288
289 ret = dmm_txn_commit(txn, wait);
290
291fail:
292 return ret;
293}
294
295/*
296 * Pin/unpin
297 */
298
299/* note: slots for which pages[i] == NULL are filled w/ dummy page
300 */
301int tiler_pin(struct tiler_block *block, struct page **pages, bool wait)
302{
303 int ret;
304
305 ret = fill(&block->area, pages, wait);
306
307 if (ret)
308 tiler_unpin(block);
309
310 return ret;
311}
312
313int tiler_unpin(struct tiler_block *block)
314{
315 return fill(&block->area, NULL, false);
316}
317
318/*
319 * Reserve/release
320 */
321struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
322 uint16_t h, uint16_t align)
323{
324 struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
325 u32 min_align = 128;
326 int ret;
327
328 BUG_ON(!validfmt(fmt));
329
330 /* convert width/height to slots */
331 w = DIV_ROUND_UP(w, geom[fmt].slot_w);
332 h = DIV_ROUND_UP(h, geom[fmt].slot_h);
333
334 /* convert alignment to slots */
335 min_align = max(min_align, (geom[fmt].slot_w * geom[fmt].cpp));
336 align = ALIGN(align, min_align);
337 align /= geom[fmt].slot_w * geom[fmt].cpp;
338
339 block->fmt = fmt;
340
341 ret = tcm_reserve_2d(containers[fmt], w, h, align, &block->area);
342 if (ret) {
343 kfree(block);
344 return 0;
345 }
346
347 /* add to allocation list */
348 spin_lock(&omap_dmm->list_lock);
349 list_add(&block->alloc_node, &omap_dmm->alloc_head);
350 spin_unlock(&omap_dmm->list_lock);
351
352 return block;
353}
354
355struct tiler_block *tiler_reserve_1d(size_t size)
356{
357 struct tiler_block *block = kzalloc(sizeof(*block), GFP_KERNEL);
358 int num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
359
360 if (!block)
361 return 0;
362
363 block->fmt = TILFMT_PAGE;
364
365 if (tcm_reserve_1d(containers[TILFMT_PAGE], num_pages,
366 &block->area)) {
367 kfree(block);
368 return 0;
369 }
370
371 spin_lock(&omap_dmm->list_lock);
372 list_add(&block->alloc_node, &omap_dmm->alloc_head);
373 spin_unlock(&omap_dmm->list_lock);
374
375 return block;
376}
377
378/* note: if you have pin'd pages, you should have already unpin'd first! */
379int tiler_release(struct tiler_block *block)
380{
381 int ret = tcm_free(&block->area);
382
383 if (block->area.tcm)
384 dev_err(omap_dmm->dev, "failed to release block\n");
385
386 spin_lock(&omap_dmm->list_lock);
387 list_del(&block->alloc_node);
388 spin_unlock(&omap_dmm->list_lock);
389
390 kfree(block);
391 return ret;
392}
393
394/*
395 * Utils
396 */
397
398/* calculate the tiler space address of a pixel in a view orientation */
399static u32 tiler_get_address(u32 orient, enum tiler_fmt fmt, u32 x, u32 y)
400{
401 u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment;
402
403 x_bits = CONT_WIDTH_BITS - geom[fmt].x_shft;
404 y_bits = CONT_HEIGHT_BITS - geom[fmt].y_shft;
405 alignment = geom[fmt].x_shft + geom[fmt].y_shft;
406
407 /* validate coordinate */
408 x_mask = MASK(x_bits);
409 y_mask = MASK(y_bits);
410
411 if (x < 0 || x > x_mask || y < 0 || y > y_mask)
412 return 0;
413
414 /* account for mirroring */
415 if (orient & MASK_X_INVERT)
416 x ^= x_mask;
417 if (orient & MASK_Y_INVERT)
418 y ^= y_mask;
419
420 /* get coordinate address */
421 if (orient & MASK_XY_FLIP)
422 tmp = ((x << y_bits) + y);
423 else
424 tmp = ((y << x_bits) + x);
425
426 return TIL_ADDR((tmp << alignment), orient, fmt);
427}
428
429dma_addr_t tiler_ssptr(struct tiler_block *block)
430{
431 BUG_ON(!validfmt(block->fmt));
432
433 return TILVIEW_8BIT + tiler_get_address(0, block->fmt,
434 block->area.p0.x * geom[block->fmt].slot_w,
435 block->area.p0.y * geom[block->fmt].slot_h);
436}
437
438void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h)
439{
440 BUG_ON(!validfmt(fmt));
441 *w = round_up(*w, geom[fmt].slot_w);
442 *h = round_up(*h, geom[fmt].slot_h);
443}
444
445uint32_t tiler_stride(enum tiler_fmt fmt)
446{
447 BUG_ON(!validfmt(fmt));
448
449 return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft);
450}
451
452size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h)
453{
454 tiler_align(fmt, &w, &h);
455 return geom[fmt].cpp * w * h;
456}
457
458size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h)
459{
460 BUG_ON(!validfmt(fmt));
461 return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
462}
463
464int omap_dmm_remove(void)
465{
466 struct tiler_block *block, *_block;
467 int i;
468
469 if (omap_dmm) {
470 /* free all area regions */
471 spin_lock(&omap_dmm->list_lock);
472 list_for_each_entry_safe(block, _block, &omap_dmm->alloc_head,
473 alloc_node) {
474 list_del(&block->alloc_node);
475 kfree(block);
476 }
477 spin_unlock(&omap_dmm->list_lock);
478
479 for (i = 0; i < omap_dmm->num_lut; i++)
480 if (omap_dmm->tcm && omap_dmm->tcm[i])
481 omap_dmm->tcm[i]->deinit(omap_dmm->tcm[i]);
482 kfree(omap_dmm->tcm);
483
484 kfree(omap_dmm->engines);
485 if (omap_dmm->refill_va)
486 dma_free_coherent(omap_dmm->dev,
487 REFILL_BUFFER_SIZE * omap_dmm->num_engines,
488 omap_dmm->refill_va,
489 omap_dmm->refill_pa);
490 if (omap_dmm->dummy_page)
491 __free_page(omap_dmm->dummy_page);
492
493 vfree(omap_dmm->lut);
494
495 if (omap_dmm->irq != -1)
496 free_irq(omap_dmm->irq, omap_dmm);
497
498 kfree(omap_dmm);
499 }
500
501 return 0;
502}
503
504int omap_dmm_init(struct drm_device *dev)
505{
506 int ret = -EFAULT, i;
507 struct tcm_area area = {0};
508 u32 hwinfo, pat_geom, lut_table_size;
509 struct omap_drm_platform_data *pdata = dev->dev->platform_data;
510
511 if (!pdata || !pdata->dmm_pdata) {
512 dev_err(dev->dev, "dmm platform data not present, skipping\n");
513 return ret;
514 }
515
516 omap_dmm = kzalloc(sizeof(*omap_dmm), GFP_KERNEL);
517 if (!omap_dmm) {
518 dev_err(dev->dev, "failed to allocate driver data section\n");
519 goto fail;
520 }
521
522 /* lookup hwmod data - base address and irq */
523 omap_dmm->base = pdata->dmm_pdata->base;
524 omap_dmm->irq = pdata->dmm_pdata->irq;
525 omap_dmm->dev = dev->dev;
526
527 if (!omap_dmm->base) {
528 dev_err(dev->dev, "failed to get dmm base address\n");
529 goto fail;
530 }
531
532 hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO);
533 omap_dmm->num_engines = (hwinfo >> 24) & 0x1F;
534 omap_dmm->num_lut = (hwinfo >> 16) & 0x1F;
535 omap_dmm->container_width = 256;
536 omap_dmm->container_height = 128;
537
538 /* read out actual LUT width and height */
539 pat_geom = readl(omap_dmm->base + DMM_PAT_GEOMETRY);
540 omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5;
541 omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5;
542
543 /* initialize DMM registers */
544 writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0);
545 writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1);
546 writel(0x80808080, omap_dmm->base + DMM_PAT_VIEW_MAP__0);
547 writel(0x80000000, omap_dmm->base + DMM_PAT_VIEW_MAP_BASE);
548 writel(0x88888888, omap_dmm->base + DMM_TILER_OR__0);
549 writel(0x88888888, omap_dmm->base + DMM_TILER_OR__1);
550
551 ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED,
552 "omap_dmm_irq_handler", omap_dmm);
553
554 if (ret) {
555 dev_err(dev->dev, "couldn't register IRQ %d, error %d\n",
556 omap_dmm->irq, ret);
557 omap_dmm->irq = -1;
558 goto fail;
559 }
560
561 /* enable some interrupts! */
562 writel(0xfefefefe, omap_dmm->base + DMM_PAT_IRQENABLE_SET);
563
564 lut_table_size = omap_dmm->lut_width * omap_dmm->lut_height *
565 omap_dmm->num_lut;
566
567 omap_dmm->lut = vmalloc(lut_table_size * sizeof(*omap_dmm->lut));
568 if (!omap_dmm->lut) {
569 dev_err(dev->dev, "could not allocate lut table\n");
570 ret = -ENOMEM;
571 goto fail;
572 }
573
574 omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32);
575 if (!omap_dmm->dummy_page) {
576 dev_err(dev->dev, "could not allocate dummy page\n");
577 ret = -ENOMEM;
578 goto fail;
579 }
580 omap_dmm->dummy_pa = page_to_phys(omap_dmm->dummy_page);
581
582 /* alloc refill memory */
583 omap_dmm->refill_va = dma_alloc_coherent(dev->dev,
584 REFILL_BUFFER_SIZE * omap_dmm->num_engines,
585 &omap_dmm->refill_pa, GFP_KERNEL);
586 if (!omap_dmm->refill_va) {
587 dev_err(dev->dev, "could not allocate refill memory\n");
588 goto fail;
589 }
590
591 /* alloc engines */
592 omap_dmm->engines = kzalloc(
593 omap_dmm->num_engines * sizeof(struct refill_engine),
594 GFP_KERNEL);
595 if (!omap_dmm->engines) {
596 dev_err(dev->dev, "could not allocate engines\n");
597 ret = -ENOMEM;
598 goto fail;
599 }
600
601 sema_init(&omap_dmm->engine_sem, omap_dmm->num_engines);
602 INIT_LIST_HEAD(&omap_dmm->idle_head);
603 for (i = 0; i < omap_dmm->num_engines; i++) {
604 omap_dmm->engines[i].id = i;
605 omap_dmm->engines[i].dmm = omap_dmm;
606 omap_dmm->engines[i].refill_va = omap_dmm->refill_va +
607 (REFILL_BUFFER_SIZE * i);
608 omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
609 (REFILL_BUFFER_SIZE * i);
610 init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
611
612 list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
613 }
614
615 omap_dmm->tcm = kzalloc(omap_dmm->num_lut * sizeof(*omap_dmm->tcm),
616 GFP_KERNEL);
617 if (!omap_dmm->tcm) {
618 dev_err(dev->dev, "failed to allocate lut ptrs\n");
619 ret = -ENOMEM;
620 goto fail;
621 }
622
623 /* init containers */
624 for (i = 0; i < omap_dmm->num_lut; i++) {
625 omap_dmm->tcm[i] = sita_init(omap_dmm->container_width,
626 omap_dmm->container_height,
627 NULL);
628
629 if (!omap_dmm->tcm[i]) {
630 dev_err(dev->dev, "failed to allocate container\n");
631 ret = -ENOMEM;
632 goto fail;
633 }
634
635 omap_dmm->tcm[i]->lut_id = i;
636 }
637
638 /* assign access mode containers to applicable tcm container */
639 /* OMAP 4 has 1 container for all 4 views */
640 containers[TILFMT_8BIT] = omap_dmm->tcm[0];
641 containers[TILFMT_16BIT] = omap_dmm->tcm[0];
642 containers[TILFMT_32BIT] = omap_dmm->tcm[0];
643 containers[TILFMT_PAGE] = omap_dmm->tcm[0];
644
645 INIT_LIST_HEAD(&omap_dmm->alloc_head);
646 spin_lock_init(&omap_dmm->list_lock);
647
648 area = (struct tcm_area) {
649 .is2d = true,
650 .tcm = NULL,
651 .p1.x = omap_dmm->container_width - 1,
652 .p1.y = omap_dmm->container_height - 1,
653 };
654
655 for (i = 0; i < lut_table_size; i++)
656 omap_dmm->lut[i] = omap_dmm->dummy_pa;
657
658 /* initialize all LUTs to dummy page entries */
659 for (i = 0; i < omap_dmm->num_lut; i++) {
660 area.tcm = omap_dmm->tcm[i];
661 if (fill(&area, NULL, true))
662 dev_err(omap_dmm->dev, "refill failed");
663 }
664
665 dev_info(omap_dmm->dev, "initialized all PAT entries\n");
666
667 return 0;
668
669fail:
670 omap_dmm_remove();
671 return ret;
672}
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.h b/drivers/staging/omapdrm/omap_dmm_tiler.h
new file mode 100644
index 000000000000..7e63b6be29f7
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.h
@@ -0,0 +1,130 @@
1/*
2 *
3 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4 * Author: Rob Clark <rob@ti.com>
5 * Andy Gross <andy.gross@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation version 2.
10 *
11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12 * kind, whether express or implied; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16#ifndef OMAP_DMM_TILER_H
17#define OMAP_DMM_TILER_H
18
19#include "omap_drv.h"
20#include "tcm.h"
21
22enum tiler_fmt {
23 TILFMT_8BIT = 0,
24 TILFMT_16BIT,
25 TILFMT_32BIT,
26 TILFMT_PAGE,
27 TILFMT_NFORMATS
28};
29
30struct pat_area {
31 u32 x0:8;
32 u32 y0:8;
33 u32 x1:8;
34 u32 y1:8;
35};
36
37struct tiler_block {
38 struct list_head alloc_node; /* node for global block list */
39 struct tcm_area area; /* area */
40 enum tiler_fmt fmt; /* format */
41};
42
43/* bits representing the same slot in DMM-TILER hw-block */
44#define SLOT_WIDTH_BITS 6
45#define SLOT_HEIGHT_BITS 6
46
47/* bits reserved to describe coordinates in DMM-TILER hw-block */
48#define CONT_WIDTH_BITS 14
49#define CONT_HEIGHT_BITS 13
50
51/* calculated constants */
52#define TILER_PAGE (1 << (SLOT_WIDTH_BITS + SLOT_HEIGHT_BITS))
53#define TILER_WIDTH (1 << (CONT_WIDTH_BITS - SLOT_WIDTH_BITS))
54#define TILER_HEIGHT (1 << (CONT_HEIGHT_BITS - SLOT_HEIGHT_BITS))
55
56/* tiler space addressing bitfields */
57#define MASK_XY_FLIP (1 << 31)
58#define MASK_Y_INVERT (1 << 30)
59#define MASK_X_INVERT (1 << 29)
60#define SHIFT_ACC_MODE 27
61#define MASK_ACC_MODE 3
62
63#define MASK(bits) ((1 << (bits)) - 1)
64
65#define TILVIEW_8BIT 0x60000000u
66#define TILVIEW_16BIT (TILVIEW_8BIT + VIEW_SIZE)
67#define TILVIEW_32BIT (TILVIEW_16BIT + VIEW_SIZE)
68#define TILVIEW_PAGE (TILVIEW_32BIT + VIEW_SIZE)
69#define TILVIEW_END (TILVIEW_PAGE + VIEW_SIZE)
70
71/* create tsptr by adding view orientation and access mode */
72#define TIL_ADDR(x, orient, a)\
73 ((u32) (x) | (orient) | ((a) << SHIFT_ACC_MODE))
74
75/* externally accessible functions */
76int omap_dmm_init(struct drm_device *dev);
77int omap_dmm_remove(void);
78
79/* pin/unpin */
80int tiler_pin(struct tiler_block *block, struct page **pages, bool wait);
81int tiler_unpin(struct tiler_block *block);
82
83/* reserve/release */
84struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, uint16_t h,
85 uint16_t align);
86struct tiler_block *tiler_reserve_1d(size_t size);
87int tiler_release(struct tiler_block *block);
88
89/* utilities */
90dma_addr_t tiler_ssptr(struct tiler_block *block);
91uint32_t tiler_stride(enum tiler_fmt fmt);
92size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
93size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
94void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
95
96
97/* GEM bo flags -> tiler fmt */
98static inline enum tiler_fmt gem2fmt(uint32_t flags)
99{
100 switch (flags & OMAP_BO_TILED) {
101 case OMAP_BO_TILED_8:
102 return TILFMT_8BIT;
103 case OMAP_BO_TILED_16:
104 return TILFMT_16BIT;
105 case OMAP_BO_TILED_32:
106 return TILFMT_32BIT;
107 default:
108 return TILFMT_PAGE;
109 }
110}
111
112static inline bool validfmt(enum tiler_fmt fmt)
113{
114 switch (fmt) {
115 case TILFMT_8BIT:
116 case TILFMT_16BIT:
117 case TILFMT_32BIT:
118 case TILFMT_PAGE:
119 return true;
120 default:
121 return false;
122 }
123}
124
125struct omap_dmm_platform_data {
126 void __iomem *base;
127 int irq;
128};
129
130#endif
diff --git a/drivers/staging/omapdrm/omap_drm.h b/drivers/staging/omapdrm/omap_drm.h
index 40167dd97589..f0ac34a8973e 100644
--- a/drivers/staging/omapdrm/omap_drm.h
+++ b/drivers/staging/omapdrm/omap_drm.h
@@ -20,7 +20,7 @@
20#ifndef __OMAP_DRM_H__ 20#ifndef __OMAP_DRM_H__
21#define __OMAP_DRM_H__ 21#define __OMAP_DRM_H__
22 22
23#include "drm.h" 23#include <drm/drm.h>
24 24
25/* Please note that modifications to all structs defined here are 25/* Please note that modifications to all structs defined here are
26 * subject to backwards-compatibility constraints. 26 * subject to backwards-compatibility constraints.
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index cee005043dbd..71de7cf34d89 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -290,6 +290,7 @@ static unsigned int detect_connectors(struct drm_device *dev)
290static int omap_modeset_init(struct drm_device *dev) 290static int omap_modeset_init(struct drm_device *dev)
291{ 291{
292 const struct omap_drm_platform_data *pdata = dev->dev->platform_data; 292 const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
293 struct omap_kms_platform_data *kms_pdata = NULL;
293 struct omap_drm_private *priv = dev->dev_private; 294 struct omap_drm_private *priv = dev->dev_private;
294 struct omap_dss_device *dssdev = NULL; 295 struct omap_dss_device *dssdev = NULL;
295 int i, j; 296 int i, j;
@@ -297,23 +298,27 @@ static int omap_modeset_init(struct drm_device *dev)
297 298
298 drm_mode_config_init(dev); 299 drm_mode_config_init(dev);
299 300
300 if (pdata) { 301 if (pdata && pdata->kms_pdata) {
302 kms_pdata = pdata->kms_pdata;
303
301 /* if platform data is provided by the board file, use it to 304 /* if platform data is provided by the board file, use it to
302 * control which overlays, managers, and devices we own. 305 * control which overlays, managers, and devices we own.
303 */ 306 */
304 for (i = 0; i < pdata->mgr_cnt; i++) { 307 for (i = 0; i < kms_pdata->mgr_cnt; i++) {
305 struct omap_overlay_manager *mgr = 308 struct omap_overlay_manager *mgr =
306 omap_dss_get_overlay_manager(pdata->mgr_ids[i]); 309 omap_dss_get_overlay_manager(
310 kms_pdata->mgr_ids[i]);
307 create_encoder(dev, mgr); 311 create_encoder(dev, mgr);
308 } 312 }
309 313
310 for (i = 0; i < pdata->dev_cnt; i++) { 314 for (i = 0; i < kms_pdata->dev_cnt; i++) {
311 struct omap_dss_device *dssdev = 315 struct omap_dss_device *dssdev =
312 omap_dss_find_device( 316 omap_dss_find_device(
313 (void *)pdata->dev_names[i], match_dev_name); 317 (void *)kms_pdata->dev_names[i],
318 match_dev_name);
314 if (!dssdev) { 319 if (!dssdev) {
315 dev_warn(dev->dev, "no such dssdev: %s\n", 320 dev_warn(dev->dev, "no such dssdev: %s\n",
316 pdata->dev_names[i]); 321 kms_pdata->dev_names[i]);
317 continue; 322 continue;
318 } 323 }
319 create_connector(dev, dssdev); 324 create_connector(dev, dssdev);
@@ -322,9 +327,9 @@ static int omap_modeset_init(struct drm_device *dev)
322 connected_connectors = detect_connectors(dev); 327 connected_connectors = detect_connectors(dev);
323 328
324 j = 0; 329 j = 0;
325 for (i = 0; i < pdata->ovl_cnt; i++) { 330 for (i = 0; i < kms_pdata->ovl_cnt; i++) {
326 struct omap_overlay *ovl = 331 struct omap_overlay *ovl =
327 omap_dss_get_overlay(pdata->ovl_ids[i]); 332 omap_dss_get_overlay(kms_pdata->ovl_ids[i]);
328 create_crtc(dev, ovl, &j, connected_connectors); 333 create_crtc(dev, ovl, &j, connected_connectors);
329 } 334 }
330 } else { 335 } else {
diff --git a/drivers/staging/omapdrm/omap_priv.h b/drivers/staging/omapdrm/omap_priv.h
index f482d1edb060..c324709aa9a1 100644
--- a/drivers/staging/omapdrm/omap_priv.h
+++ b/drivers/staging/omapdrm/omap_priv.h
@@ -30,7 +30,7 @@
30 * detected devices. This should be a good default behavior for most cases, 30 * detected devices. This should be a good default behavior for most cases,
31 * but yet there still might be times when you wish to do something different. 31 * but yet there still might be times when you wish to do something different.
32 */ 32 */
33struct omap_drm_platform_data { 33struct omap_kms_platform_data {
34 int ovl_cnt; 34 int ovl_cnt;
35 const int *ovl_ids; 35 const int *ovl_ids;
36 int mgr_cnt; 36 int mgr_cnt;
@@ -39,4 +39,9 @@ struct omap_drm_platform_data {
39 const char **dev_names; 39 const char **dev_names;
40}; 40};
41 41
42struct omap_drm_platform_data {
43 struct omap_kms_platform_data *kms_pdata;
44 struct omap_dmm_platform_data *dmm_pdata;
45};
46
42#endif /* __OMAP_DRM_H__ */ 47#endif /* __OMAP_DRM_H__ */
diff --git a/drivers/staging/omapdrm/tcm-sita.c b/drivers/staging/omapdrm/tcm-sita.c
new file mode 100644
index 000000000000..10d5ac3dae4b
--- /dev/null
+++ b/drivers/staging/omapdrm/tcm-sita.c
@@ -0,0 +1,703 @@
1/*
2 * tcm-sita.c
3 *
4 * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
5 *
6 * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
7 * Lajos Molnar <molnar@ti.com>
8 *
9 * Copyright (C) 2009-2010 Texas Instruments, Inc.
10 *
11 * This package is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 *
19 */
20#include <linux/slab.h>
21#include <linux/spinlock.h>
22
23#include "tcm-sita.h"
24
25#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
26
27/* Individual selection criteria for different scan areas */
28static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
29static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
30
31/*********************************************
32 * TCM API - Sita Implementation
33 *********************************************/
34static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
35 struct tcm_area *area);
36static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
37static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
38static void sita_deinit(struct tcm *tcm);
39
40/*********************************************
41 * Main Scanner functions
42 *********************************************/
43static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
44 struct tcm_area *area);
45
46static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
47 struct tcm_area *field, struct tcm_area *area);
48
49static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
50 struct tcm_area *field, struct tcm_area *area);
51
52static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
53 struct tcm_area *field, struct tcm_area *area);
54
55/*********************************************
56 * Support Infrastructure Methods
57 *********************************************/
58static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
59
60static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
61 struct tcm_area *field, s32 criteria,
62 struct score *best);
63
64static void get_nearness_factor(struct tcm_area *field,
65 struct tcm_area *candidate,
66 struct nearness_factor *nf);
67
68static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
69 struct neighbor_stats *stat);
70
71static void fill_area(struct tcm *tcm,
72 struct tcm_area *area, struct tcm_area *parent);
73
74
75/*********************************************/
76
77/*********************************************
78 * Utility Methods
79 *********************************************/
80struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
81{
82 struct tcm *tcm;
83 struct sita_pvt *pvt;
84 struct tcm_area area = {0};
85 s32 i;
86
87 if (width == 0 || height == 0)
88 return NULL;
89
90 tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
91 pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
92 if (!tcm || !pvt)
93 goto error;
94
95 memset(tcm, 0, sizeof(*tcm));
96 memset(pvt, 0, sizeof(*pvt));
97
98 /* Updating the pointers to SiTA implementation APIs */
99 tcm->height = height;
100 tcm->width = width;
101 tcm->reserve_2d = sita_reserve_2d;
102 tcm->reserve_1d = sita_reserve_1d;
103 tcm->free = sita_free;
104 tcm->deinit = sita_deinit;
105 tcm->pvt = (void *)pvt;
106
107 spin_lock_init(&(pvt->lock));
108
109 /* Creating tam map */
110 pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
111 if (!pvt->map)
112 goto error;
113
114 for (i = 0; i < tcm->width; i++) {
115 pvt->map[i] =
116 kmalloc(sizeof(**pvt->map) * tcm->height,
117 GFP_KERNEL);
118 if (pvt->map[i] == NULL) {
119 while (i--)
120 kfree(pvt->map[i]);
121 kfree(pvt->map);
122 goto error;
123 }
124 }
125
126 if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
127 pvt->div_pt.x = attr->x;
128 pvt->div_pt.y = attr->y;
129
130 } else {
131 /* Defaulting to 3:1 ratio on width for 2D area split */
132 /* Defaulting to 3:1 ratio on height for 2D and 1D split */
133 pvt->div_pt.x = (tcm->width * 3) / 4;
134 pvt->div_pt.y = (tcm->height * 3) / 4;
135 }
136
137 spin_lock(&(pvt->lock));
138 assign(&area, 0, 0, width - 1, height - 1);
139 fill_area(tcm, &area, NULL);
140 spin_unlock(&(pvt->lock));
141 return tcm;
142
143error:
144 kfree(tcm);
145 kfree(pvt);
146 return NULL;
147}
148
149static void sita_deinit(struct tcm *tcm)
150{
151 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
152 struct tcm_area area = {0};
153 s32 i;
154
155 area.p1.x = tcm->width - 1;
156 area.p1.y = tcm->height - 1;
157
158 spin_lock(&(pvt->lock));
159 fill_area(tcm, &area, NULL);
160 spin_unlock(&(pvt->lock));
161
162 for (i = 0; i < tcm->height; i++)
163 kfree(pvt->map[i]);
164 kfree(pvt->map);
165 kfree(pvt);
166}
167
168/**
169 * Reserve a 1D area in the container
170 *
171 * @param num_slots size of 1D area
172 * @param area pointer to the area that will be populated with the
173 * reserved area
174 *
175 * @return 0 on success, non-0 error value on failure.
176 */
177static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
178 struct tcm_area *area)
179{
180 s32 ret;
181 struct tcm_area field = {0};
182 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
183
184 spin_lock(&(pvt->lock));
185
186 /* Scanning entire container */
187 assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
188
189 ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
190 if (!ret)
191 /* update map */
192 fill_area(tcm, area, area);
193
194 spin_unlock(&(pvt->lock));
195 return ret;
196}
197
198/**
199 * Reserve a 2D area in the container
200 *
201 * @param w width
202 * @param h height
203 * @param area pointer to the area that will be populated with the reesrved
204 * area
205 *
206 * @return 0 on success, non-0 error value on failure.
207 */
208static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
209 struct tcm_area *area)
210{
211 s32 ret;
212 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
213
214 /* not supporting more than 64 as alignment */
215 if (align > 64)
216 return -EINVAL;
217
218 /* we prefer 1, 32 and 64 as alignment */
219 align = align <= 1 ? 1 : align <= 32 ? 32 : 64;
220
221 spin_lock(&(pvt->lock));
222 ret = scan_areas_and_find_fit(tcm, w, h, align, area);
223 if (!ret)
224 /* update map */
225 fill_area(tcm, area, area);
226
227 spin_unlock(&(pvt->lock));
228 return ret;
229}
230
231/**
232 * Unreserve a previously allocated 2D or 1D area
233 * @param area area to be freed
234 * @return 0 - success
235 */
236static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
237{
238 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
239
240 spin_lock(&(pvt->lock));
241
242 /* check that this is in fact an existing area */
243 WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
244 pvt->map[area->p1.x][area->p1.y] != area);
245
246 /* Clear the contents of the associated tiles in the map */
247 fill_area(tcm, area, NULL);
248
249 spin_unlock(&(pvt->lock));
250
251 return 0;
252}
253
254/**
255 * Note: In general the cordinates in the scan field area relevant to the can
256 * sweep directions. The scan origin (e.g. top-left corner) will always be
257 * the p0 member of the field. Therfore, for a scan from top-left p0.x <= p1.x
258 * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
259 * <= p0.y
260 */
261
262/**
263 * Raster scan horizontally right to left from top to bottom to find a place for
264 * a 2D area of given size inside a scan field.
265 *
266 * @param w width of desired area
267 * @param h height of desired area
268 * @param align desired area alignment
269 * @param area pointer to the area that will be set to the best position
270 * @param field area to scan (inclusive)
271 *
272 * @return 0 on success, non-0 error value on failure.
273 */
274static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
275 struct tcm_area *field, struct tcm_area *area)
276{
277 s32 x, y;
278 s16 start_x, end_x, start_y, end_y, found_x = -1;
279 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
280 struct score best = {{0}, {0}, {0}, 0};
281
282 start_x = field->p0.x;
283 end_x = field->p1.x;
284 start_y = field->p0.y;
285 end_y = field->p1.y;
286
287 /* check scan area co-ordinates */
288 if (field->p0.x < field->p1.x ||
289 field->p1.y < field->p0.y)
290 return -EINVAL;
291
292 /* check if allocation would fit in scan area */
293 if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
294 return -ENOSPC;
295
296 /* adjust start_x and end_y, as allocation would not fit beyond */
297 start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
298 end_y = end_y - h + 1;
299
300 /* check if allocation would still fit in scan area */
301 if (start_x < end_x)
302 return -ENOSPC;
303
304 /* scan field top-to-bottom, right-to-left */
305 for (y = start_y; y <= end_y; y++) {
306 for (x = start_x; x >= end_x; x -= align) {
307 if (is_area_free(map, x, y, w, h)) {
308 found_x = x;
309
310 /* update best candidate */
311 if (update_candidate(tcm, x, y, w, h, field,
312 CR_R2L_T2B, &best))
313 goto done;
314
315 /* change upper x bound */
316 end_x = x + 1;
317 break;
318 } else if (map[x][y] && map[x][y]->is2d) {
319 /* step over 2D areas */
320 x = ALIGN(map[x][y]->p0.x - w + 1, align);
321 }
322 }
323
324 /* break if you find a free area shouldering the scan field */
325 if (found_x == start_x)
326 break;
327 }
328
329 if (!best.a.tcm)
330 return -ENOSPC;
331done:
332 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
333 return 0;
334}
335
336/**
337 * Raster scan horizontally left to right from top to bottom to find a place for
338 * a 2D area of given size inside a scan field.
339 *
340 * @param w width of desired area
341 * @param h height of desired area
342 * @param align desired area alignment
343 * @param area pointer to the area that will be set to the best position
344 * @param field area to scan (inclusive)
345 *
346 * @return 0 on success, non-0 error value on failure.
347 */
348static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
349 struct tcm_area *field, struct tcm_area *area)
350{
351 s32 x, y;
352 s16 start_x, end_x, start_y, end_y, found_x = -1;
353 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
354 struct score best = {{0}, {0}, {0}, 0};
355
356 start_x = field->p0.x;
357 end_x = field->p1.x;
358 start_y = field->p0.y;
359 end_y = field->p1.y;
360
361 /* check scan area co-ordinates */
362 if (field->p1.x < field->p0.x ||
363 field->p1.y < field->p0.y)
364 return -EINVAL;
365
366 /* check if allocation would fit in scan area */
367 if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
368 return -ENOSPC;
369
370 start_x = ALIGN(start_x, align);
371
372 /* check if allocation would still fit in scan area */
373 if (w > LEN(end_x, start_x))
374 return -ENOSPC;
375
376 /* adjust end_x and end_y, as allocation would not fit beyond */
377 end_x = end_x - w + 1; /* + 1 to be inclusive */
378 end_y = end_y - h + 1;
379
380 /* scan field top-to-bottom, left-to-right */
381 for (y = start_y; y <= end_y; y++) {
382 for (x = start_x; x <= end_x; x += align) {
383 if (is_area_free(map, x, y, w, h)) {
384 found_x = x;
385
386 /* update best candidate */
387 if (update_candidate(tcm, x, y, w, h, field,
388 CR_L2R_T2B, &best))
389 goto done;
390 /* change upper x bound */
391 end_x = x - 1;
392
393 break;
394 } else if (map[x][y] && map[x][y]->is2d) {
395 /* step over 2D areas */
396 x = ALIGN_DOWN(map[x][y]->p1.x, align);
397 }
398 }
399
400 /* break if you find a free area shouldering the scan field */
401 if (found_x == start_x)
402 break;
403 }
404
405 if (!best.a.tcm)
406 return -ENOSPC;
407done:
408 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
409 return 0;
410}
411
412/**
413 * Raster scan horizontally right to left from bottom to top to find a place
414 * for a 1D area of given size inside a scan field.
415 *
416 * @param num_slots size of desired area
417 * @param align desired area alignment
418 * @param area pointer to the area that will be set to the best
419 * position
420 * @param field area to scan (inclusive)
421 *
422 * @return 0 on success, non-0 error value on failure.
423 */
424static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
425 struct tcm_area *field, struct tcm_area *area)
426{
427 s32 found = 0;
428 s16 x, y;
429 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
430 struct tcm_area *p;
431
432 /* check scan area co-ordinates */
433 if (field->p0.y < field->p1.y)
434 return -EINVAL;
435
436 /**
437 * Currently we only support full width 1D scan field, which makes sense
438 * since 1D slot-ordering spans the full container width.
439 */
440 if (tcm->width != field->p0.x - field->p1.x + 1)
441 return -EINVAL;
442
443 /* check if allocation would fit in scan area */
444 if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y))
445 return -ENOSPC;
446
447 x = field->p0.x;
448 y = field->p0.y;
449
450 /* find num_slots consecutive free slots to the left */
451 while (found < num_slots) {
452 if (y < 0)
453 return -ENOSPC;
454
455 /* remember bottom-right corner */
456 if (found == 0) {
457 area->p1.x = x;
458 area->p1.y = y;
459 }
460
461 /* skip busy regions */
462 p = pvt->map[x][y];
463 if (p) {
464 /* move to left of 2D areas, top left of 1D */
465 x = p->p0.x;
466 if (!p->is2d)
467 y = p->p0.y;
468
469 /* start over */
470 found = 0;
471 } else {
472 /* count consecutive free slots */
473 found++;
474 if (found == num_slots)
475 break;
476 }
477
478 /* move to the left */
479 if (x == 0)
480 y--;
481 x = (x ? : tcm->width) - 1;
482
483 }
484
485 /* set top-left corner */
486 area->p0.x = x;
487 area->p0.y = y;
488 return 0;
489}
490
491/**
492 * Find a place for a 2D area of given size inside a scan field based on its
493 * alignment needs.
494 *
495 * @param w width of desired area
496 * @param h height of desired area
497 * @param align desired area alignment
498 * @param area pointer to the area that will be set to the best position
499 *
500 * @return 0 on success, non-0 error value on failure.
501 */
502static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
503 struct tcm_area *area)
504{
505 s32 ret = 0;
506 struct tcm_area field = {0};
507 u16 boundary_x, boundary_y;
508 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
509
510 if (align > 1) {
511 /* prefer top-left corner */
512 boundary_x = pvt->div_pt.x - 1;
513 boundary_y = pvt->div_pt.y - 1;
514
515 /* expand width and height if needed */
516 if (w > pvt->div_pt.x)
517 boundary_x = tcm->width - 1;
518 if (h > pvt->div_pt.y)
519 boundary_y = tcm->height - 1;
520
521 assign(&field, 0, 0, boundary_x, boundary_y);
522 ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
523
524 /* scan whole container if failed, but do not scan 2x */
525 if (ret != 0 && (boundary_x != tcm->width - 1 ||
526 boundary_y != tcm->height - 1)) {
527 /* scan the entire container if nothing found */
528 assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
529 ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
530 }
531 } else if (align == 1) {
532 /* prefer top-right corner */
533 boundary_x = pvt->div_pt.x;
534 boundary_y = pvt->div_pt.y - 1;
535
536 /* expand width and height if needed */
537 if (w > (tcm->width - pvt->div_pt.x))
538 boundary_x = 0;
539 if (h > pvt->div_pt.y)
540 boundary_y = tcm->height - 1;
541
542 assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
543 ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
544
545 /* scan whole container if failed, but do not scan 2x */
546 if (ret != 0 && (boundary_x != 0 ||
547 boundary_y != tcm->height - 1)) {
548 /* scan the entire container if nothing found */
549 assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
550 ret = scan_r2l_t2b(tcm, w, h, align, &field,
551 area);
552 }
553 }
554
555 return ret;
556}
557
558/* check if an entire area is free */
559static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h)
560{
561 u16 x = 0, y = 0;
562 for (y = y0; y < y0 + h; y++) {
563 for (x = x0; x < x0 + w; x++) {
564 if (map[x][y])
565 return false;
566 }
567 }
568 return true;
569}
570
571/* fills an area with a parent tcm_area */
572static void fill_area(struct tcm *tcm, struct tcm_area *area,
573 struct tcm_area *parent)
574{
575 s32 x, y;
576 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
577 struct tcm_area a, a_;
578
579 /* set area's tcm; otherwise, enumerator considers it invalid */
580 area->tcm = tcm;
581
582 tcm_for_each_slice(a, *area, a_) {
583 for (x = a.p0.x; x <= a.p1.x; ++x)
584 for (y = a.p0.y; y <= a.p1.y; ++y)
585 pvt->map[x][y] = parent;
586
587 }
588}
589
590/**
591 * Compares a candidate area to the current best area, and if it is a better
592 * fit, it updates the best to this one.
593 *
594 * @param x0, y0, w, h top, left, width, height of candidate area
595 * @param field scan field
596 * @param criteria scan criteria
597 * @param best best candidate and its scores
598 *
599 * @return 1 (true) if the candidate area is known to be the final best, so no
600 * more searching should be performed
601 */
602static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
603 struct tcm_area *field, s32 criteria,
604 struct score *best)
605{
606 struct score me; /* score for area */
607
608 /*
609 * NOTE: For horizontal bias we always give the first found, because our
610 * scan is horizontal-raster-based and the first candidate will always
611 * have the horizontal bias.
612 */
613 bool first = criteria & CR_BIAS_HORIZONTAL;
614
615 assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
616
617 /* calculate score for current candidate */
618 if (!first) {
619 get_neighbor_stats(tcm, &me.a, &me.n);
620 me.neighs = me.n.edge + me.n.busy;
621 get_nearness_factor(field, &me.a, &me.f);
622 }
623
624 /* the 1st candidate is always the best */
625 if (!best->a.tcm)
626 goto better;
627
628 BUG_ON(first);
629
630 /* diagonal balance check */
631 if ((criteria & CR_DIAGONAL_BALANCE) &&
632 best->neighs <= me.neighs &&
633 (best->neighs < me.neighs ||
634 /* this implies that neighs and occupied match */
635 best->n.busy < me.n.busy ||
636 (best->n.busy == me.n.busy &&
637 /* check the nearness factor */
638 best->f.x + best->f.y > me.f.x + me.f.y)))
639 goto better;
640
641 /* not better, keep going */
642 return 0;
643
644better:
645 /* save current area as best */
646 memcpy(best, &me, sizeof(me));
647 best->a.tcm = tcm;
648 return first;
649}
650
651/**
652 * Calculate the nearness factor of an area in a search field. The nearness
653 * factor is smaller if the area is closer to the search origin.
654 */
655static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
656 struct nearness_factor *nf)
657{
658 /**
659 * Using signed math as field coordinates may be reversed if
660 * search direction is right-to-left or bottom-to-top.
661 */
662 nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
663 (field->p1.x - field->p0.x);
664 nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
665 (field->p1.y - field->p0.y);
666}
667
668/* get neighbor statistics */
669static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
670 struct neighbor_stats *stat)
671{
672 s16 x = 0, y = 0;
673 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
674
675 /* Clearing any exisiting values */
676 memset(stat, 0, sizeof(*stat));
677
678 /* process top & bottom edges */
679 for (x = area->p0.x; x <= area->p1.x; x++) {
680 if (area->p0.y == 0)
681 stat->edge++;
682 else if (pvt->map[x][area->p0.y - 1])
683 stat->busy++;
684
685 if (area->p1.y == tcm->height - 1)
686 stat->edge++;
687 else if (pvt->map[x][area->p1.y + 1])
688 stat->busy++;
689 }
690
691 /* process left & right edges */
692 for (y = area->p0.y; y <= area->p1.y; ++y) {
693 if (area->p0.x == 0)
694 stat->edge++;
695 else if (pvt->map[area->p0.x - 1][y])
696 stat->busy++;
697
698 if (area->p1.x == tcm->width - 1)
699 stat->edge++;
700 else if (pvt->map[area->p1.x + 1][y])
701 stat->busy++;
702 }
703}
diff --git a/drivers/staging/omapdrm/tcm-sita.h b/drivers/staging/omapdrm/tcm-sita.h
new file mode 100644
index 000000000000..0444f868671c
--- /dev/null
+++ b/drivers/staging/omapdrm/tcm-sita.h
@@ -0,0 +1,95 @@
1/*
2 * tcm_sita.h
3 *
4 * SImple Tiler Allocator (SiTA) private structures.
5 *
6 * Author: Ravi Ramachandra <r.ramachandra@ti.com>
7 *
8 * Copyright (C) 2009-2011 Texas Instruments, Inc.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * * Neither the name of Texas Instruments Incorporated nor the names of
23 * its contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifndef _TCM_SITA_H
40#define _TCM_SITA_H
41
42#include "tcm.h"
43
44/* length between two coordinates */
45#define LEN(a, b) ((a) > (b) ? (a) - (b) + 1 : (b) - (a) + 1)
46
47enum criteria {
48 CR_MAX_NEIGHS = 0x01,
49 CR_FIRST_FOUND = 0x10,
50 CR_BIAS_HORIZONTAL = 0x20,
51 CR_BIAS_VERTICAL = 0x40,
52 CR_DIAGONAL_BALANCE = 0x80
53};
54
55/* nearness to the beginning of the search field from 0 to 1000 */
56struct nearness_factor {
57 s32 x;
58 s32 y;
59};
60
61/*
62 * Statistics on immediately neighboring slots. Edge is the number of
63 * border segments that are also border segments of the scan field. Busy
64 * refers to the number of neighbors that are occupied.
65 */
66struct neighbor_stats {
67 u16 edge;
68 u16 busy;
69};
70
71/* structure to keep the score of a potential allocation */
72struct score {
73 struct nearness_factor f;
74 struct neighbor_stats n;
75 struct tcm_area a;
76 u16 neighs; /* number of busy neighbors */
77};
78
79struct sita_pvt {
80 spinlock_t lock; /* spinlock to protect access */
81 struct tcm_pt div_pt; /* divider point splitting container */
82 struct tcm_area ***map; /* pointers to the parent area for each slot */
83};
84
85/* assign coordinates to area */
86static inline
87void assign(struct tcm_area *a, u16 x0, u16 y0, u16 x1, u16 y1)
88{
89 a->p0.x = x0;
90 a->p0.y = y0;
91 a->p1.x = x1;
92 a->p1.y = y1;
93}
94
95#endif
diff --git a/drivers/staging/omapdrm/tcm.h b/drivers/staging/omapdrm/tcm.h
new file mode 100644
index 000000000000..d273e3ee0b4c
--- /dev/null
+++ b/drivers/staging/omapdrm/tcm.h
@@ -0,0 +1,326 @@
1/*
2 * tcm.h
3 *
4 * TILER container manager specification and support functions for TI
5 * TILER driver.
6 *
7 * Author: Lajos Molnar <molnar@ti.com>
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * * Neither the name of Texas Instruments Incorporated nor the names of
23 * its contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#ifndef TCM_H
40#define TCM_H
41
42struct tcm;
43
44/* point */
45struct tcm_pt {
46 u16 x;
47 u16 y;
48};
49
50/* 1d or 2d area */
51struct tcm_area {
52 bool is2d; /* whether area is 1d or 2d */
53 struct tcm *tcm; /* parent */
54 struct tcm_pt p0;
55 struct tcm_pt p1;
56};
57
58struct tcm {
59 u16 width, height; /* container dimensions */
60 int lut_id; /* Lookup table identifier */
61
62 /* 'pvt' structure shall contain any tcm details (attr) along with
63 linked list of allocated areas and mutex for mutually exclusive access
64 to the list. It may also contain copies of width and height to notice
65 any changes to the publicly available width and height fields. */
66 void *pvt;
67
68 /* function table */
69 s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u8 align,
70 struct tcm_area *area);
71 s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
72 s32 (*free) (struct tcm *tcm, struct tcm_area *area);
73 void (*deinit) (struct tcm *tcm);
74};
75
76/*=============================================================================
77 BASIC TILER CONTAINER MANAGER INTERFACE
78=============================================================================*/
79
80/*
81 * NOTE:
82 *
83 * Since some basic parameter checking is done outside the TCM algorithms,
84 * TCM implementation do NOT have to check the following:
85 *
86 * area pointer is NULL
87 * width and height fits within container
88 * number of pages is more than the size of the container
89 *
90 */
91
92struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr);
93
94
95/**
96 * Deinitialize tiler container manager.
97 *
98 * @param tcm Pointer to container manager.
99 *
100 * @return 0 on success, non-0 error value on error. The call
101 * should free as much memory as possible and meaningful
102 * even on failure. Some error codes: -ENODEV: invalid
103 * manager.
104 */
105static inline void tcm_deinit(struct tcm *tcm)
106{
107 if (tcm)
108 tcm->deinit(tcm);
109}
110
111/**
112 * Reserves a 2D area in the container.
113 *
114 * @param tcm Pointer to container manager.
115 * @param height Height(in pages) of area to be reserved.
116 * @param width Width(in pages) of area to be reserved.
117 * @param align Alignment requirement for top-left corner of area. Not
118 * all values may be supported by the container manager,
119 * but it must support 0 (1), 32 and 64.
120 * 0 value is equivalent to 1.
121 * @param area Pointer to where the reserved area should be stored.
122 *
123 * @return 0 on success. Non-0 error code on failure. Also,
124 * the tcm field of the area will be set to NULL on
125 * failure. Some error codes: -ENODEV: invalid manager,
126 * -EINVAL: invalid area, -ENOMEM: not enough space for
127 * allocation.
128 */
129static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
130 u16 align, struct tcm_area *area)
131{
132 /* perform rudimentary error checking */
133 s32 res = tcm == NULL ? -ENODEV :
134 (area == NULL || width == 0 || height == 0 ||
135 /* align must be a 2 power */
136 (align & (align - 1))) ? -EINVAL :
137 (height > tcm->height || width > tcm->width) ? -ENOMEM : 0;
138
139 if (!res) {
140 area->is2d = true;
141 res = tcm->reserve_2d(tcm, height, width, align, area);
142 area->tcm = res ? NULL : tcm;
143 }
144
145 return res;
146}
147
148/**
149 * Reserves a 1D area in the container.
150 *
151 * @param tcm Pointer to container manager.
152 * @param slots Number of (contiguous) slots to reserve.
153 * @param area Pointer to where the reserved area should be stored.
154 *
155 * @return 0 on success. Non-0 error code on failure. Also,
156 * the tcm field of the area will be set to NULL on
157 * failure. Some error codes: -ENODEV: invalid manager,
158 * -EINVAL: invalid area, -ENOMEM: not enough space for
159 * allocation.
160 */
161static inline s32 tcm_reserve_1d(struct tcm *tcm, u32 slots,
162 struct tcm_area *area)
163{
164 /* perform rudimentary error checking */
165 s32 res = tcm == NULL ? -ENODEV :
166 (area == NULL || slots == 0) ? -EINVAL :
167 slots > (tcm->width * (u32) tcm->height) ? -ENOMEM : 0;
168
169 if (!res) {
170 area->is2d = false;
171 res = tcm->reserve_1d(tcm, slots, area);
172 area->tcm = res ? NULL : tcm;
173 }
174
175 return res;
176}
177
178/**
179 * Free a previously reserved area from the container.
180 *
181 * @param area Pointer to area reserved by a prior call to
182 * tcm_reserve_1d or tcm_reserve_2d call, whether
183 * it was successful or not. (Note: all fields of
184 * the structure must match.)
185 *
186 * @return 0 on success. Non-0 error code on failure. Also, the tcm
187 * field of the area is set to NULL on success to avoid subsequent
188 * freeing. This call will succeed even if supplying
189 * the area from a failed reserved call.
190 */
191static inline s32 tcm_free(struct tcm_area *area)
192{
193 s32 res = 0; /* free succeeds by default */
194
195 if (area && area->tcm) {
196 res = area->tcm->free(area->tcm, area);
197 if (res == 0)
198 area->tcm = NULL;
199 }
200
201 return res;
202}
203
204/*=============================================================================
205 HELPER FUNCTION FOR ANY TILER CONTAINER MANAGER
206=============================================================================*/
207
208/**
209 * This method slices off the topmost 2D slice from the parent area, and stores
210 * it in the 'slice' parameter. The 'parent' parameter will get modified to
211 * contain the remaining portion of the area. If the whole parent area can
212 * fit in a 2D slice, its tcm pointer is set to NULL to mark that it is no
213 * longer a valid area.
214 *
215 * @param parent Pointer to a VALID parent area that will get modified
216 * @param slice Pointer to the slice area that will get modified
217 */
218static inline void tcm_slice(struct tcm_area *parent, struct tcm_area *slice)
219{
220 *slice = *parent;
221
222 /* check if we need to slice */
223 if (slice->tcm && !slice->is2d &&
224 slice->p0.y != slice->p1.y &&
225 (slice->p0.x || (slice->p1.x != slice->tcm->width - 1))) {
226 /* set end point of slice (start always remains) */
227 slice->p1.x = slice->tcm->width - 1;
228 slice->p1.y = (slice->p0.x) ? slice->p0.y : slice->p1.y - 1;
229 /* adjust remaining area */
230 parent->p0.x = 0;
231 parent->p0.y = slice->p1.y + 1;
232 } else {
233 /* mark this as the last slice */
234 parent->tcm = NULL;
235 }
236}
237
238/* Verify if a tcm area is logically valid */
239static inline bool tcm_area_is_valid(struct tcm_area *area)
240{
241 return area && area->tcm &&
242 /* coordinate bounds */
243 area->p1.x < area->tcm->width &&
244 area->p1.y < area->tcm->height &&
245 area->p0.y <= area->p1.y &&
246 /* 1D coordinate relationship + p0.x check */
247 ((!area->is2d &&
248 area->p0.x < area->tcm->width &&
249 area->p0.x + area->p0.y * area->tcm->width <=
250 area->p1.x + area->p1.y * area->tcm->width) ||
251 /* 2D coordinate relationship */
252 (area->is2d &&
253 area->p0.x <= area->p1.x));
254}
255
256/* see if a coordinate is within an area */
257static inline bool __tcm_is_in(struct tcm_pt *p, struct tcm_area *a)
258{
259 u16 i;
260
261 if (a->is2d) {
262 return p->x >= a->p0.x && p->x <= a->p1.x &&
263 p->y >= a->p0.y && p->y <= a->p1.y;
264 } else {
265 i = p->x + p->y * a->tcm->width;
266 return i >= a->p0.x + a->p0.y * a->tcm->width &&
267 i <= a->p1.x + a->p1.y * a->tcm->width;
268 }
269}
270
271/* calculate area width */
272static inline u16 __tcm_area_width(struct tcm_area *area)
273{
274 return area->p1.x - area->p0.x + 1;
275}
276
277/* calculate area height */
278static inline u16 __tcm_area_height(struct tcm_area *area)
279{
280 return area->p1.y - area->p0.y + 1;
281}
282
283/* calculate number of slots in an area */
284static inline u16 __tcm_sizeof(struct tcm_area *area)
285{
286 return area->is2d ?
287 __tcm_area_width(area) * __tcm_area_height(area) :
288 (area->p1.x - area->p0.x + 1) + (area->p1.y - area->p0.y) *
289 area->tcm->width;
290}
291#define tcm_sizeof(area) __tcm_sizeof(&(area))
292#define tcm_awidth(area) __tcm_area_width(&(area))
293#define tcm_aheight(area) __tcm_area_height(&(area))
294#define tcm_is_in(pt, area) __tcm_is_in(&(pt), &(area))
295
296/* limit a 1D area to the first N pages */
297static inline s32 tcm_1d_limit(struct tcm_area *a, u32 num_pg)
298{
299 if (__tcm_sizeof(a) < num_pg)
300 return -ENOMEM;
301 if (!num_pg)
302 return -EINVAL;
303
304 a->p1.x = (a->p0.x + num_pg - 1) % a->tcm->width;
305 a->p1.y = a->p0.y + ((a->p0.x + num_pg - 1) / a->tcm->width);
306 return 0;
307}
308
309/**
310 * Iterate through 2D slices of a valid area. Behaves
311 * syntactically as a for(;;) statement.
312 *
313 * @param var Name of a local variable of type 'struct
314 * tcm_area *' that will get modified to
315 * contain each slice.
316 * @param area Pointer to the VALID parent area. This
317 * structure will not get modified
318 * throughout the loop.
319 *
320 */
321#define tcm_for_each_slice(var, area, safe) \
322 for (safe = area, \
323 tcm_slice(&safe, &var); \
324 var.tcm; tcm_slice(&safe, &var))
325
326#endif