aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/emgd/emgd/include/plb/cmd.h
blob: 9f8034d8f2cd6501944c99266cd2ce30a4b4dd33 (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
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
/* -*- pse-c -*-
 *-----------------------------------------------------------------------------
 * Filename: cmd.h
 * $Revision: 1.9 $
 *-----------------------------------------------------------------------------
 * Copyright © 2002-2010, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *-----------------------------------------------------------------------------
 * Description:
 *  Device dependent header file for the command interface for poulsbo
 *  devices.
 *-----------------------------------------------------------------------------
 */

#ifndef _PLB_CMD_H
#define _PLB_CMD_H

#include <io.h>
#include <sched.h>

#include <instr_common.h>
#include <utils.h>

//#define TMP_PLB_SERIALIZE_2D
//#define TMP_PLB_SERIALIZE_RASTER
//#define TMP_PLB_SERIALIZE_BINNER
#define CONFIG_ENABLE_SYNC_SLEEP 0
#define CONFIG_ENABLE_CPU_SYNC 0
#define CONFIG_ENABLE_DIRECT_2D 1
#define CONFIG_ENABLE_DIRECT_BIN 1
#define CONFIG_ENABLE_DIRECT_VIDEO 1


#define CMD_QUEUE_SIZE_PLB (1024*128)
#define MAX_2D_COMMAND_PLB 0x60

/* Wait at most 2 second for a 2D, 3D, or Video operation to complete */
#define CMD_TIMEOUT_PLB 2000

/*
 * Bitfield to track which engines are possibly busy and in need
 * of sync.
 */
#define CMD_ENGINE_2D_PLB     0x01
#define CMD_ENGINE_BINNER_PLB 0x02
#define CMD_ENGINE_RASTER_PLB 0x04
#define CMD_ENGINE_VIDEO_PLB  0x08
#define CMD_ENGINE_CPU_PLB    0x10
#define CMD_ENGINE_WAIT_PLB   0x20
#define CMD_ENGINE_DPM_PLB    0x40
#define CMD_ENGINE_ALL_PLB    0x7f

typedef unsigned long cmd_engine_plb_t;

/*
 * Virtual Commands for the PLB Virtual Engine.
 * Note: They are numbered such that they are unlikely to happen in
 * a command stream and should then be easily spotted during debug.
 *
 * There is a special relationship between the 3D and RASTER numbers.
 * The command packet for these two is the same and the command is either
 * a binner + raster (3D) or just a raster alone (RASTER).
 * Sometimes the binner is processed out of order and the command ID is
 * converted from 3D to raster by or'ing in the extra bit.
 */
#define VCOMMAND_2D_PLB          0x12300000
#define VCOMMAND_3D_PLB          0x12310000
#define VCOMMAND_RASTER_PLB      0x12330000
#define VCOMMAND_VIDEO_PLB       0x12340000
#define VCOMMAND_REG_PLB         0x12350000
#define VCOMMAND_WAIT_PLB        0x12360000
#define VCOMMAND_SYNC_PLB        0x12370000
#define VCOMMAND_SW2D_PLB        0x12380000
#define VCOMMAND_EXIT_PLB        0x123e0000
#define VCOMMAND_WRAP_PLB        0x123f0000
#define VCOMMAND_ID_MASK_PLB     0xffff0000
#define VCOMMAND_SIZE_MASK_PLB   0x0000ffff

#define CMD_SYNC_NEED_IDLE_PLB (CMD_ENGINE_RASTER_PLB | CMD_ENGINE_DPM_PLB | \
		CMD_ENGINE_VIDEO_PLB | CMD_ENGINE_WAIT_PLB | CMD_ENGINE_2D_PLB)

#define CMD_2D_NEED_IDLE_PLB (CMD_ENGINE_RASTER_PLB | \
		CMD_ENGINE_VIDEO_PLB | CMD_ENGINE_WAIT_PLB)

#define CMD_BINNER_NEED_IDLE_PLB (CMD_ENGINE_BINNER_PLB | CMD_ENGINE_2D_PLB)

#define CMD_RASTER_NEED_IDLE_PLB (CMD_ENGINE_2D_PLB | CMD_ENGINE_VIDEO_PLB | \
		CMD_ENGINE_BINNER_PLB | CMD_ENGINE_RASTER_PLB | CMD_ENGINE_DPM_PLB | \
		CMD_ENGINE_WAIT_PLB)

#define CMD_VIDEO_NEED_IDLE_PLB (CMD_ENGINE_WAIT_PLB)

#define CMD_REG_NEED_IDLE_PLB (CMD_ENGINE_RASTER_PLB | CMD_ENGINE_DPM_PLB | \
		CMD_ENGINE_2D_PLB | CMD_ENGINE_VIDEO_PLB | CMD_ENGINE_WAIT_PLB)

#define CMD_CPU_NEED_IDLE_PLB (CMD_ENGINE_RASTER_PLB | CMD_ENGINE_DPM_PLB | \
		CMD_ENGINE_2D_PLB | CMD_ENGINE_VIDEO_PLB | CMD_ENGINE_WAIT_PLB)

#define CMD_WAIT_NEED_IDLE_PLB (CMD_ENGINE_WAIT_PLB)

/*
 * wait_flags
 * Flags to pass with the VCOMMAND_WAIT_PLB commands
 */
/*#define CMD_WAIT_VBLANK_PLB   1*/
/*#define CMD_WAIT_SCANLINE_PLB 2*/
#define CMD_WAIT_FLIP_A_PLB   4
#define CMD_WAIT_FLIP_B_PLB   8
#define CMD_WAIT_OVL_PLB      0x10
#define CMD_WAIT_OVL2_PLB     0x20
#define CMD_WAIT_VIDEO_SYNC_PLB 0x40

#define CMD_SW2D_SRC_COPY_BLT   1

typedef struct _cmd_queue_plb {
	/* 2D HW access */
	struct {
		unsigned char *avail_reg;
		unsigned char *status_reg;
		igd_command_t *base;
		igd_command_t *end;
		volatile unsigned int avail;
	} blt;

	/* Registers - Set at init*/
	unsigned char *event_status_reg;
	unsigned char *event_enable_reg;
	unsigned char *event_clear_reg;
	unsigned char *event_status2_reg;
	unsigned char *event_enable2_reg;
	unsigned char *event_clear2_reg;
	unsigned char *mtx_status_reg;
	unsigned long msvdx_fence;
	unsigned long msvdx_last_fence;
	/* HW status page virtual addr */
	volatile unsigned char *sync;

	/* Virtual command queue - accessed by both threads */
	volatile igd_command_t vqueue[CMD_QUEUE_SIZE_PLB];
	volatile igd_command_t * volatile head;
	volatile igd_command_t * volatile tail;
  	volatile igd_command_t * current_node;
	volatile igd_command_t * reservation;

	/* Only for main thread */
	unsigned long last_sync;

	/* Check lock */
	volatile igd_command_t * volatile last_head_value;
	volatile os_alarm_t last_head_time;

	/* Process thread */

	/* Accessed by both main thread and process thread */
	unsigned long wait_flags;
	volatile cmd_engine_plb_t busy_engines;

	igd_appcontext_h appcontext;
	struct _igd_context *context;

	/* Flag used by the main thread in cmd_reserve when
	 * a 2d instruction is being written directly into
	 * the hardware.
	 * Can only be set if is_thread_waiting = 1.
	 * Purpose is for update to know this is a 2d instr
	 * directly to the HW.
	 */
	int direct_access_2d;

	/* Mutex/Conditions */
	/* The process thread will set this to 1 when it is
	 * sleeping and the main thread will set to 0 when awake
	 */
	volatile int is_thread_waiting;
	os_pthread_mutex_t thread_mutex;
	os_pthread_cond_t  thread_waiting;

	/*
	 * The main thread will sleep on the sync_waiting condition variable
	 * when waiting for a sync tom complete. The sync_waiting_id will
	 * identify the sync number that the main thread is waiting for.
	 */
	volatile unsigned long sync_waiting_id;
	volatile int sync_thread_waiting;
	os_pthread_cond_t sync_waiting_cond;

	/* This protects main thread and process thread when calling
	 * check_lock_plb.
	 */
	os_pthread_mutex_t check_lock_mutex;
} cmd_queue_plb_t;

#if 0  /* Removing all the command vqueue stuff */
extern os_pthread_t *cmd_vqueue_thread_plb;
extern void *process_vqueue_plb(void *data);

#define CMD_GET_QUEUE_PLB(d, p) \
	((cmd_queue_plb_t *)((igd_display_pipe_t *)			\
		((igd_display_context_t *)d)->pipe)->queue[p])

/*
 * This is a clever trick so that we get inlined functions for normal
 * builds and a single instance of this function for debug builds.
 */
#if !defined(DEBUG_BUILD_TYPE) || defined(IS_CMD_PLB_C)
OPT_DEBUG_INLINE int cmd_reserve_plb(cmd_queue_plb_t *queue,
	unsigned long size,
	igd_command_t type)
{
	volatile igd_command_t *needed_start;
	volatile igd_command_t *needed_end;
	int first = 1;
	int expired = 0;
//	int wrap = 0;
	os_alarm_t timeout = (os_alarm_t)0;

	EMGD_TRACE_ENTER;

	/* Since this is not a 2d instruction, ensure update knows this */
	queue->direct_access_2d = 0;

	/*
	 * Always leave space for 1 extra command so that we can put
	 * the wrap command in without waiting on the head. Also remember
	 * to leave space at the end of the queue.
	 */
	needed_start = queue->tail;
	needed_end = queue->tail + size + 2;
	if(needed_end >= &queue->vqueue[CMD_QUEUE_SIZE_PLB]) {
		/* Doesn't fit so wrap to beginning */
		needed_start = queue->vqueue;
		needed_end = queue->vqueue + size + 2;
		*queue->tail = VCOMMAND_WRAP_PLB;
		//wrap = 0;
	}

	while(1) {
		if((queue->head <= needed_start) ||
			(queue->head >= needed_end)) {
			//if(wrap) {
			//	queue->tail = queue->vqueue;
			//}
			*needed_start++ = type | size;
			queue->current_node = needed_start;
			queue->reservation = needed_start + size;
			EMGD_TRACE_EXIT;
			return 0;
		}
		if(first) {
			timeout = OS_SET_ALARM(CMD_TIMEOUT_PLB);
			first = 0;
		}
		if(expired) {
			break;
		}
		OS_SCHEDULE();
		expired = OS_TEST_ALARM(timeout);
	}

	EMGD_ERROR_EXIT("Timeout waiting for Virtual Queue Space");
	return -IGD_ERROR_BUSY;
}
#else
extern int cmd_reserve_plb(cmd_queue_plb_t *queue,
	unsigned long size, igd_command_t type);
#endif

extern int cmd_ready_plb(cmd_queue_plb_t *queue, cmd_engine_plb_t need_idle);

#if !defined(DEBUG_BUILD_TYPE) || defined(IS_CMD_PLB_C)
OPT_DEBUG_INLINE int cmd_update_plb(cmd_queue_plb_t *queue)
{
	int ret;

	EMGD_ASSERT(queue, "Null Queue", -IGD_ERROR_INVAL);
	EMGD_ASSERT(queue->current_node <= queue->reservation,
		"Queue use exceeds reservation", -IGD_ERROR_INVAL);

	/*
	 * If the queue is set up for direct access then fire off the
	 * blit engine and return.
	 */
	if(queue->direct_access_2d) {
		unsigned int tmp;
		/*
		 * Need to read the last dword back to trigger the blit engine.
		 */
		tmp = EMGD_READ32(queue->blt.end - 1);
		queue->current_node = NULL;
		queue->reservation = NULL;

#ifdef TMP_PLB_SERIALIZE_2D
		cmd_ready_plb(queue, CMD_CPU_NEED_IDLE_PLB);
#endif

		EMGD_TRACE_EXIT;
		return 0;
	}


	/*
	 * Commands were placed in the ring so move the tail. Then if we
	 * have threads signal the ring thread to go. If we don't have
	 * threads we will just process the commands that were placed in the
	 * ring.
	 */
	ret = OS_PTHREAD_MUTEX_LOCK(&queue->thread_mutex);
	queue->tail = queue->current_node;

	/*
	 * Null these out so that any invalid uses will get caught
	 */
	queue->current_node = NULL;
	queue->reservation = NULL;

	if(cmd_vqueue_thread_plb) {
			queue->is_thread_waiting = 0;
		ret = OS_PTHREAD_COND_SIGNAL(&queue->thread_waiting);
		OS_PTHREAD_MUTEX_UNLOCK(&queue->thread_mutex);
	} else {
		OS_PTHREAD_MUTEX_UNLOCK(&queue->thread_mutex);
		/* For syncronous use */
		process_vqueue_plb(queue);
	}

	return 0;
}
#else
extern int cmd_update_plb(cmd_queue_plb_t *queue);
#endif

/*
 * 2D Engine has a on-chip queue. As long as there is space
 * in the queue the 2d commands can be written starting at the
 * base register.
 *
 * 2D Engine runs asynchronously with other engines so for IEGD
 * we ensure that other engines are idle when inserting 2d commands.
 */
static __inline int cmd_2d_direct_reserve_plb(cmd_queue_plb_t *queue,
	unsigned int size)
{
	int ret;
	os_alarm_t timeout = (os_alarm_t)0;
	int expired = 0;
	int first = 1;

	EMGD_TRACE_ENTER;
	EMGD_ASSERT(queue, "Null Queue", -IGD_ERROR_INVAL);
	EMGD_ASSERT( size < MAX_2D_COMMAND_PLB, "2D Command too large",
		-IGD_ERROR_INVAL);

	queue->direct_access_2d = 1;

	if(queue->busy_engines & CMD_2D_NEED_IDLE_PLB) {
		ret = cmd_ready_plb(queue, CMD_2D_NEED_IDLE_PLB);
		/* Do not return if cmd_ready_plb returns an error.
		 * this may be a sync and not putting it in the command queue
		 * will cause problems.  Since the failure was on a previous
		 * command, this should be ok.
		if(ret) {
			return ret;
		}*/
	}

	queue->busy_engines |= CMD_ENGINE_2D_PLB;

	queue->current_node = queue->blt.base;
	queue->blt.end = queue->blt.base + size;
	queue->reservation = queue->blt.end;
	if (size <= queue->blt.avail) {
		queue->blt.avail -= size;
		EMGD_TRACE_EXIT;
		return 0;
	}
	while(1) {
		queue->blt.avail = EMGD_READ32(queue->blt.avail_reg);
		if(size <= queue->blt.avail) {
			break;
		}
		if(first) {
			timeout = OS_SET_ALARM(CMD_TIMEOUT_PLB);
			first = 0;
		}
		if(expired) {
			EMGD_ERROR_EXIT("Timeout waiting for 2D Hardware");
			return -IGD_ERROR_HWERROR;
		}
		expired = OS_TEST_ALARM(timeout);
	}
	queue->blt.avail -= size;

	EMGD_TRACE_EXIT;
	return 0;
}

/*
 * Reserve Space for a 2D command. This function will set up the
 * queue to directly send the 2d command to the hardware when there
 * are no engines to wait for and will instead set up the queue to
 * put the 2d command in the virtual ring buffer when we would
 * have to wait.
 * In this manner we get the benefit of direct command placement
 * with 0 latency when we can use it and non-blocking behavior
 * always.
 */
#if !defined(DEBUG_BUILD_TYPE) || defined(IS_CMD_PLB_C)
OPT_DEBUG_INLINE int cmd_2d_reserve_plb(cmd_queue_plb_t *queue,
	unsigned int size)
{
	int ret;

	EMGD_TRACE_ENTER;
	EMGD_ASSERT(queue, "Null Queue", -IGD_ERROR_INVAL);
	EMGD_ASSERT( size < MAX_2D_COMMAND_PLB, "2D Command too large",
		-IGD_ERROR_INVAL);

	/*
	 * Only do a direct reserve of the 2D command if the virtual queue
	 * is empty AND this won't have to wait for another engine
	 * to complete.  Otherwise this stalls waiting on that engine.
	 */
	if(CONFIG_ENABLE_DIRECT_2D && (queue->is_thread_waiting == 1) &&
			!(queue->busy_engines & CMD_2D_NEED_IDLE_PLB)) {
		ret = cmd_2d_direct_reserve_plb(queue, size);
		EMGD_TRACE_EXIT;
		return ret;
	}

	ret = cmd_reserve_plb(queue, size, VCOMMAND_2D_PLB);
	EMGD_TRACE_EXIT;
	return ret;
}
#else
extern int cmd_2d_reserve_plb(cmd_queue_plb_t *queue,
	unsigned int size);
#endif

#define CMD_2D_RESERVE_PLB(q, s) cmd_2d_reserve_plb(q, s)
#define CMD_2D_WRITE_PLB(q, dw) INSTR_WRITE(dw, q->current_node)
#define CMD_2D_UPDATE_PLB(q) cmd_update_plb(q)


/*
 * The sync command uses the 2D engine directly when there are no
 * dependent engines running. Otherwise it puts a sync command in the
 * vqueue which is processed using the CPU in the cmd thread.
 */
#if !defined(DEBUG_BUILD_TYPE) || defined(IS_CMD_PLB_C)
OPT_DEBUG_INLINE int cmd_sync_reserve_plb(cmd_queue_plb_t *queue,
	unsigned int size)
{
	int ret;

	EMGD_TRACE_ENTER;
	EMGD_ASSERT(queue, "Null Queue", -IGD_ERROR_INVAL);
	EMGD_ASSERT( size < MAX_2D_COMMAND_PLB, "2D Command too large",
		-IGD_ERROR_INVAL);

	/*
	 * Only do a direct reserve of the sync if the virtual queue
	 * is empty AND this won't have to wait for another engine
	 * to complete.  Otherwise this stalls waiting on that engine.
	 */
	if((queue->is_thread_waiting == 1) &&
			!(queue->busy_engines & CMD_2D_NEED_IDLE_PLB)) {
		ret = cmd_2d_direct_reserve_plb(queue, size);
		EMGD_TRACE_EXIT;
		return ret;
	}

	ret = cmd_reserve_plb(queue, size, VCOMMAND_SYNC_PLB);
	EMGD_TRACE_EXIT;
	return ret;
}
#else
extern int cmd_sync_reserve_plb(cmd_queue_plb_t *queue);
#endif

#define CMD_SYNC_RESERVE_PLB(q, s) cmd_sync_reserve_plb(q, s)
#define CMD_SYNC_WRITE_PLB(q, dw) INSTR_WRITE(dw, q->current_node)
#define CMD_SYNC_UPDATE_PLB(q) cmd_update_plb(q)
#endif


#endif