summaryrefslogtreecommitdiff
path: root/drivers/video/i810/i810_accel.c
blob: 64cd1c827cf00bbd241fe59f2efbc57929ecf71d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
/*-*- linux-c -*-
 *  linux/drivers/video/i810_accel.c -- Hardware Acceleration
 *
 *      Copyright (C) 2001 Antonino Daplas<adaplas@pol.net>
 *      All Rights Reserved      
 *
 *  This file is subject to the terms and conditions of the GNU General Public
 *  License. See the file COPYING in the main directory of this archive for
 *  more details.
 */
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/fb.h>

#include "i810_regs.h"
#include "i810.h"

static u32 i810fb_rop[] = {
	COLOR_COPY_ROP, /* ROP_COPY */
	XOR_ROP         /* ROP_XOR  */
};

/* Macros */
#define PUT_RING(n) {                                        \
	i810_writel(par->cur_tail, par->iring.virtual, n);   \
        par->cur_tail += 4;                                  \
        par->cur_tail &= RING_SIZE_MASK;                     \
}                                                                      

extern void flush_cache(void);

/************************************************************/

/* BLT Engine Routines */
static inline void i810_report_error(u8 __iomem *mmio)
{
	printk("IIR     : 0x%04x\n"
	       "EIR     : 0x%04x\n"
	       "PGTBL_ER: 0x%04x\n"
	       "IPEIR   : 0x%04x\n"
	       "IPEHR   : 0x%04x\n",
	       i810_readw(IIR, mmio),
	       i810_readb(EIR, mmio),
	       i810_readl(PGTBL_ER, mmio),
	       i810_readl(IPEIR, mmio), 
	       i810_readl(IPEHR, mmio));
}

/**
 * wait_for_space - check ring buffer free space
 * @space: amount of ringbuffer space needed in bytes
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * The function waits until a free space from the ringbuffer
 * is available 
 */	
static inline int wait_for_space(struct fb_info *info, u32 space)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u32 head, count = WAIT_COUNT, tail;
	u8 __iomem *mmio = par->mmio_start_virtual;

	tail = par->cur_tail;
	while (count--) {
		head = i810_readl(IRING + 4, mmio) & RBUFFER_HEAD_MASK;	
		if ((tail == head) || 
		    (tail > head && 
		     (par->iring.size - tail + head) >= space) || 
		    (tail < head && (head - tail) >= space)) {
			return 0;	
		}
	}
	printk("ringbuffer lockup!!!\n");
	i810_report_error(mmio); 
	par->dev_flags |= LOCKUP;
	info->pixmap.scan_align = 1;
	return 1;
}

/** 
 * wait_for_engine_idle - waits for all hardware engines to finish
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * This waits for lring(0), iring(1), and batch(3), etc to finish and
 * waits until ringbuffer is empty.
 */
static inline int wait_for_engine_idle(struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u8 __iomem *mmio = par->mmio_start_virtual;
	int count = WAIT_COUNT;

	if (wait_for_space(info, par->iring.size)) /* flush */
		return 1;

	while((i810_readw(INSTDONE, mmio) & 0x7B) != 0x7B && --count); 
	if (count) return 0;

	printk("accel engine lockup!!!\n");
	printk("INSTDONE: 0x%04x\n", i810_readl(INSTDONE, mmio));
	i810_report_error(mmio); 
	par->dev_flags |= LOCKUP;
	info->pixmap.scan_align = 1;
	return 1;
}

/* begin_iring - prepares the ringbuffer 
 * @space: length of sequence in dwords
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * Checks/waits for sufficent space in ringbuffer of size
 * space.  Returns the tail of the buffer
 */ 
static inline u32 begin_iring(struct fb_info *info, u32 space)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;

	if (par->dev_flags & ALWAYS_SYNC) 
		wait_for_engine_idle(info);
	return wait_for_space(info, space);
}

/**
 * end_iring - advances the buffer
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * This advances the tail of the ringbuffer, effectively
 * beginning the execution of the graphics instruction sequence.
 */
static inline void end_iring(struct i810fb_par *par)
{
	u8 __iomem *mmio = par->mmio_start_virtual;

	i810_writel(IRING, mmio, par->cur_tail);
}

/**
 * source_copy_blit - BLIT transfer operation
 * @dwidth: width of rectangular graphics data
 * @dheight: height of rectangular graphics data
 * @dpitch: bytes per line of destination buffer
 * @xdir: direction of copy (left to right or right to left)
 * @src: address of first pixel to read from
 * @dest: address of first pixel to write to
 * @from: source address
 * @where: destination address
 * @rop: raster operation
 * @blit_bpp: pixel format which can be different from the 
 *            framebuffer's pixelformat
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * This is a BLIT operation typically used when doing
 * a 'Copy and Paste'
 */
static inline void source_copy_blit(int dwidth, int dheight, int dpitch, 
				    int xdir, int src, int dest, int rop, 
				    int blit_bpp, struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;

	if (begin_iring(info, 24 + IRING_PAD)) return;

	PUT_RING(BLIT | SOURCE_COPY_BLIT | 4);
	PUT_RING(xdir | rop << 16 | dpitch | DYN_COLOR_EN | blit_bpp);
	PUT_RING(dheight << 16 | dwidth);
	PUT_RING(dest);
	PUT_RING(dpitch);
	PUT_RING(src);

	end_iring(par);
}	

/**
 * color_blit - solid color BLIT operation
 * @width: width of destination
 * @height: height of destination
 * @pitch: pixels per line of the buffer
 * @dest: address of first pixel to write to
 * @where: destination
 * @rop: raster operation
 * @what: color to transfer
 * @blit_bpp: pixel format which can be different from the 
 *            framebuffer's pixelformat
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * A BLIT operation which can be used for  color fill/rectangular fill
 */
static inline void color_blit(int width, int height, int pitch,  int dest, 
			      int rop, int what, int blit_bpp, 
			      struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;

	if (begin_iring(info, 24 + IRING_PAD)) return;

	PUT_RING(BLIT | COLOR_BLT | 3);
	PUT_RING(rop << 16 | pitch | SOLIDPATTERN | DYN_COLOR_EN | blit_bpp);
	PUT_RING(height << 16 | width);
	PUT_RING(dest);
	PUT_RING(what);
	PUT_RING(NOP);

	end_iring(par);
}
 
/**
 * mono_src_copy_imm_blit - color expand from system memory to framebuffer
 * @dwidth: width of destination
 * @dheight: height of destination
 * @dpitch: pixels per line of the buffer
 * @dsize: size of bitmap in double words
 * @dest: address of first byte of pixel;
 * @rop: raster operation
 * @blit_bpp: pixelformat to use which can be different from the 
 *            framebuffer's pixelformat
 * @src: address of image data
 * @bg: backgound color
 * @fg: forground color
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * A color expand operation where the  source data is placed in the 
 * ringbuffer itself. Useful for drawing text. 
 *
 * REQUIREMENT:
 * The end of a scanline must be padded to the next word.
 */
static inline void mono_src_copy_imm_blit(int dwidth, int dheight, int dpitch,
					  int dsize, int blit_bpp, int rop,
					  int dest, const u32 *src, int bg,
					  int fg, struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;

	if (begin_iring(info, 24 + (dsize << 2) + IRING_PAD)) return;

	PUT_RING(BLIT | MONO_SOURCE_COPY_IMMEDIATE | (4 + dsize));
	PUT_RING(DYN_COLOR_EN | blit_bpp | rop << 16 | dpitch);
	PUT_RING(dheight << 16 | dwidth);
	PUT_RING(dest);
	PUT_RING(bg);
	PUT_RING(fg);
	while (dsize--) 
		PUT_RING(*src++);

	end_iring(par);
}

static inline void load_front(int offset, struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;

	if (begin_iring(info, 8 + IRING_PAD)) return;

	PUT_RING(PARSER | FLUSH);
	PUT_RING(NOP);

	end_iring(par);

	if (begin_iring(info, 8 + IRING_PAD)) return;

	PUT_RING(PARSER | FRONT_BUFFER | ((par->pitch >> 3) << 8));
	PUT_RING((par->fb.offset << 12) + offset);

	end_iring(par);
}

/**
 * i810fb_iring_enable - enables/disables the ringbuffer
 * @mode: enable or disable
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * Enables or disables the ringbuffer, effectively enabling or
 * disabling the instruction/acceleration engine.
 */
static inline void i810fb_iring_enable(struct i810fb_par *par, u32 mode)
{
	u32 tmp;
	u8 __iomem *mmio = par->mmio_start_virtual;

	tmp = i810_readl(IRING + 12, mmio);
	if (mode == OFF) 
		tmp &= ~1;
	else 
		tmp |= 1;
	flush_cache();
	i810_writel(IRING + 12, mmio, tmp);
}       

void i810fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u32 dx, dy, width, height, dest, rop = 0, color = 0;

	if (!info->var.accel_flags || par->dev_flags & LOCKUP ||
	    par->depth == 4) 
		return cfb_fillrect(info, rect);

	if (par->depth == 1) 
		color = rect->color;
	else 
		color = ((u32 *) (info->pseudo_palette))[rect->color];

	rop = i810fb_rop[rect->rop];

	dx = rect->dx * par->depth;
	width = rect->width * par->depth;
	dy = rect->dy;
	height = rect->height;

	dest = info->fix.smem_start + (dy * info->fix.line_length) + dx;
	color_blit(width, height, info->fix.line_length, dest, rop, color, 
		   par->blit_bpp, info);
}
	
void i810fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) 
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u32 sx, sy, dx, dy, pitch, width, height, src, dest, xdir;

	if (!info->var.accel_flags || par->dev_flags & LOCKUP ||
	    par->depth == 4)
		return cfb_copyarea(info, region);

	dx = region->dx * par->depth;
	sx = region->sx * par->depth;
	width = region->width * par->depth;
	sy = region->sy;
	dy = region->dy;
	height = region->height;

	if (dx <= sx) {
		xdir = INCREMENT;
	}
	else {
		xdir = DECREMENT;
		sx += width - 1;
		dx += width - 1;
	}
	if (dy <= sy) {
		pitch = info->fix.line_length;
	}
	else {
		pitch = (-(info->fix.line_length)) & 0xFFFF;
		sy += height - 1;
		dy += height - 1;
	}
	src = info->fix.smem_start + (sy * info->fix.line_length) + sx;
	dest = info->fix.smem_start + (dy * info->fix.line_length) + dx;

	source_copy_blit(width, height, pitch, xdir, src, dest,
			 PAT_COPY_ROP, par->blit_bpp, info);
}

void i810fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u32 fg = 0, bg = 0, size, dst;
	
	if (!info->var.accel_flags || par->dev_flags & LOCKUP ||
	    par->depth == 4 || image->depth != 1) 
		return cfb_imageblit(info, image);

	switch (info->var.bits_per_pixel) {
	case 8:
		fg = image->fg_color;
		bg = image->bg_color;
		break;
	case 16:
	case 24:
		fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
		bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
		break;
	}	
	
	dst = info->fix.smem_start + (image->dy * info->fix.line_length) + 
		(image->dx * par->depth);

	size = (image->width+7)/8 + 1;
	size &= ~1;
	size *= image->height;
	size += 7;
	size &= ~7;
	mono_src_copy_imm_blit(image->width * par->depth, 
			       image->height, info->fix.line_length, 
			       size/4, par->blit_bpp,
			       PAT_COPY_ROP, dst, (u32 *) image->data, 
			       bg, fg, info);
} 

int i810fb_sync(struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	
	if (!info->var.accel_flags || par->dev_flags & LOCKUP)
		return 0;

	return wait_for_engine_idle(info);
}

void i810fb_load_front(u32 offset, struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u8 __iomem *mmio = par->mmio_start_virtual;

	if (!info->var.accel_flags || par->dev_flags & LOCKUP)
		i810_writel(DPLYBASE, mmio, par->fb.physical + offset);
	else 
		load_front(offset, info);
}

/**
 * i810fb_init_ringbuffer - initialize the ringbuffer
 * @par: pointer to i810fb_par structure
 *
 * DESCRIPTION:
 * Initializes the ringbuffer by telling the device the
 * size and location of the ringbuffer.  It also sets 
 * the head and tail pointers = 0
 */
void i810fb_init_ringbuffer(struct fb_info *info)
{
	struct i810fb_par *par = (struct i810fb_par *) info->par;
	u32 tmp1, tmp2;
	u8 __iomem *mmio = par->mmio_start_virtual;
	
	wait_for_engine_idle(info);
	i810fb_iring_enable(par, OFF);
	i810_writel(IRING, mmio, 0);
	i810_writel(IRING + 4, mmio, 0);
	par->cur_tail = 0;

	tmp2 = i810_readl(IRING + 8, mmio) & ~RBUFFER_START_MASK; 
	tmp1 = par->iring.physical;
	i810_writel(IRING + 8, mmio, tmp2 | tmp1);

	tmp1 = i810_readl(IRING + 12, mmio);
	tmp1 &= ~RBUFFER_SIZE_MASK;
	tmp2 = (par->iring.size - I810_PAGESIZE) & RBUFFER_SIZE_MASK;
	i810_writel(IRING + 12, mmio, tmp1 | tmp2);
	i810fb_iring_enable(par, ON);
}