summaryrefslogtreecommitdiff
path: root/drivers/video/fsl_diu_fb.c
blob: 2c21e293a2c32b5b168bfbabde9eeaee96af4203 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc.
 * Authors: York Sun <yorksun@freescale.com>
 *          Timur Tabi <timur@freescale.com>
 *
 * FSL DIU Framebuffer driver
 */

#include <common.h>
#include <malloc.h>
#include <asm/io.h>

#include "videomodes.h"
#include <video_fb.h>
#include <fsl_diu_fb.h>
#include <linux/list.h>
#include <linux/fb.h>

/* This setting is used for the ifm pdm360ng with PRIMEVIEW PM070WL3 */
static struct fb_videomode fsl_diu_mode_800_480 = {
	.name		= "800x480-60",
	.refresh	= 60,
	.xres		= 800,
	.yres		= 480,
	.pixclock	= 31250,
	.left_margin	= 86,
	.right_margin	= 42,
	.upper_margin	= 33,
	.lower_margin	= 10,
	.hsync_len	= 128,
	.vsync_len	= 2,
	.sync		= 0,
	.vmode		= FB_VMODE_NONINTERLACED
};

/* For the SHARP LQ084S3LG01, used on the P1022DS board */
static struct fb_videomode fsl_diu_mode_800_600 = {
	.name		= "800x600-60",
	.refresh	= 60,
	.xres		= 800,
	.yres		= 600,
	.pixclock	= 25000,
	.left_margin	= 88,
	.right_margin	= 40,
	.upper_margin	= 23,
	.lower_margin	= 1,
	.hsync_len	= 128,
	.vsync_len	= 4,
	.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.vmode		= FB_VMODE_NONINTERLACED
};

/*
 * These parameters give default parameters
 * for video output 1024x768,
 * FIXME - change timing to proper amounts
 * hsync 31.5kHz, vsync 60Hz
 */
static struct fb_videomode fsl_diu_mode_1024_768 = {
	.name		= "1024x768-60",
	.refresh	= 60,
	.xres		= 1024,
	.yres		= 768,
	.pixclock	= 15385,
	.left_margin	= 160,
	.right_margin	= 24,
	.upper_margin	= 29,
	.lower_margin	= 3,
	.hsync_len	= 136,
	.vsync_len	= 6,
	.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.vmode		= FB_VMODE_NONINTERLACED
};

static struct fb_videomode fsl_diu_mode_1280_1024 = {
	.name		= "1280x1024-60",
	.refresh	= 60,
	.xres		= 1280,
	.yres		= 1024,
	.pixclock	= 9375,
	.left_margin	= 38,
	.right_margin	= 128,
	.upper_margin	= 2,
	.lower_margin	= 7,
	.hsync_len	= 216,
	.vsync_len	= 37,
	.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.vmode		= FB_VMODE_NONINTERLACED
};

static struct fb_videomode fsl_diu_mode_1280_720 = {
	.name		= "1280x720-60",
	.refresh	= 60,
	.xres		= 1280,
	.yres		= 720,
	.pixclock	= 13426,
	.left_margin	= 192,
	.right_margin	= 64,
	.upper_margin	= 22,
	.lower_margin	= 1,
	.hsync_len	= 136,
	.vsync_len	= 3,
	.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.vmode		= FB_VMODE_NONINTERLACED
};

static struct fb_videomode fsl_diu_mode_1920_1080 = {
	.name		= "1920x1080-60",
	.refresh	= 60,
	.xres		= 1920,
	.yres		= 1080,
	.pixclock	= 5787,
	.left_margin	= 328,
	.right_margin	= 120,
	.upper_margin	= 34,
	.lower_margin	= 1,
	.hsync_len	= 208,
	.vsync_len	= 3,
	.sync		= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
	.vmode		= FB_VMODE_NONINTERLACED
};

/*
 * These are the fields of area descriptor(in DDR memory) for every plane
 */
struct diu_ad {
	/* Word 0(32-bit) in DDR memory */
	__le32 pix_fmt; /* hard coding pixel format */
	/* Word 1(32-bit) in DDR memory */
	__le32 addr;
	/* Word 2(32-bit) in DDR memory */
	__le32 src_size_g_alpha;
	/* Word 3(32-bit) in DDR memory */
	__le32 aoi_size;
	/* Word 4(32-bit) in DDR memory */
	__le32 offset_xyi;
	/* Word 5(32-bit) in DDR memory */
	__le32 offset_xyd;
	/* Word 6(32-bit) in DDR memory */
	__le32 ckmax_r:8;
	__le32 ckmax_g:8;
	__le32 ckmax_b:8;
	__le32 res9:8;
	/* Word 7(32-bit) in DDR memory */
	__le32 ckmin_r:8;
	__le32 ckmin_g:8;
	__le32 ckmin_b:8;
	__le32 res10:8;
	/* Word 8(32-bit) in DDR memory */
	__le32 next_ad;
	/* Word 9(32-bit) in DDR memory, just for 64-bit aligned */
	__le32 res[3];
} __attribute__ ((packed));

/*
 * DIU register map
 */
struct diu {
	__be32 desc[3];
	__be32 gamma;
	__be32 pallete;
	__be32 cursor;
	__be32 curs_pos;
	__be32 diu_mode;
	__be32 bgnd;
	__be32 bgnd_wb;
	__be32 disp_size;
	__be32 wb_size;
	__be32 wb_mem_addr;
	__be32 hsyn_para;
	__be32 vsyn_para;
	__be32 syn_pol;
	__be32 thresholds;
	__be32 int_status;
	__be32 int_mask;
	__be32 colorbar[8];
	__be32 filling;
	__be32 plut;
} __attribute__ ((packed));

struct diu_addr {
	void *vaddr;		/* Virtual address */
	u32 paddr;		/* 32-bit physical address */
	unsigned int offset;	/* Alignment offset */
};

static struct fb_info info;

/*
 * Align to 64-bit(8-byte), 32-byte, etc.
 */
static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
{
	u32 offset, ssize;
	u32 mask;

	ssize = size + bytes_align;
	buf->vaddr = malloc(ssize);
	if (!buf->vaddr)
		return -1;

	memset(buf->vaddr, 0, ssize);
	mask = bytes_align - 1;
	offset = (u32)buf->vaddr & mask;
	if (offset) {
		buf->offset = bytes_align - offset;
		buf->vaddr += offset;
	} else
		buf->offset = 0;

	buf->paddr = virt_to_phys(buf->vaddr);
	return 0;
}

/*
 * Allocate a framebuffer and an Area Descriptor that points to it.  Both
 * are created in the same memory block.  The Area Descriptor is updated to
 * point to the framebuffer memory. Memory is aligned as needed.
 */
static struct diu_ad *allocate_fb(unsigned int xres, unsigned int yres,
				  unsigned int depth, char **fb)
{
	unsigned long size = xres * yres * depth;
	struct diu_addr addr;
	struct diu_ad *ad;
	size_t ad_size = roundup(sizeof(struct diu_ad), 32);

	/*
	 * Allocate a memory block that holds the Area Descriptor and the
	 * frame buffer right behind it.  To keep the code simple, everything
	 * is aligned on a 32-byte address.
	 */
	if (allocate_buf(&addr, ad_size + size, 32) < 0)
		return NULL;

	ad = addr.vaddr;
	ad->addr = cpu_to_le32(addr.paddr + ad_size);
	ad->aoi_size = cpu_to_le32((yres << 16) | xres);
	ad->src_size_g_alpha = cpu_to_le32((yres << 12) | xres);
	ad->offset_xyi = 0;
	ad->offset_xyd = 0;

	if (fb)
		*fb = addr.vaddr + ad_size;

	return ad;
}

int fsl_diu_init(u16 xres, u16 yres, u32 pixel_format, int gamma_fix)
{
	struct fb_videomode *fsl_diu_mode_db;
	struct diu_ad *ad;
	struct diu *hw = (struct diu *)CONFIG_SYS_DIU_ADDR;
	u8 *gamma_table_base;
	unsigned int i, j;
	struct diu_addr gamma;
	struct diu_addr cursor;

/* Convert the X,Y resolution pair into a single number */
#define RESOLUTION(x, y) (((u32)(x) << 16) | (y))

	switch (RESOLUTION(xres, yres)) {
	case RESOLUTION(800, 480):
		fsl_diu_mode_db = &fsl_diu_mode_800_480;
		break;
	case RESOLUTION(800, 600):
		fsl_diu_mode_db = &fsl_diu_mode_800_600;
		break;
	case RESOLUTION(1024, 768):
		fsl_diu_mode_db = &fsl_diu_mode_1024_768;
		break;
	case RESOLUTION(1280, 1024):
		fsl_diu_mode_db = &fsl_diu_mode_1280_1024;
		break;
	case RESOLUTION(1280, 720):
		fsl_diu_mode_db = &fsl_diu_mode_1280_720;
		break;
	case RESOLUTION(1920, 1080):
		fsl_diu_mode_db = &fsl_diu_mode_1920_1080;
		break;
	default:
		printf("DIU:   Unsupported resolution %ux%u\n", xres, yres);
		return -1;
	}

	/* read mode info */
	info.var.xres = fsl_diu_mode_db->xres;
	info.var.yres = fsl_diu_mode_db->yres;
	info.var.bits_per_pixel = 32;
	info.var.pixclock = fsl_diu_mode_db->pixclock;
	info.var.left_margin = fsl_diu_mode_db->left_margin;
	info.var.right_margin = fsl_diu_mode_db->right_margin;
	info.var.upper_margin = fsl_diu_mode_db->upper_margin;
	info.var.lower_margin = fsl_diu_mode_db->lower_margin;
	info.var.hsync_len = fsl_diu_mode_db->hsync_len;
	info.var.vsync_len = fsl_diu_mode_db->vsync_len;
	info.var.sync = fsl_diu_mode_db->sync;
	info.var.vmode = fsl_diu_mode_db->vmode;
	info.fix.line_length = info.var.xres * info.var.bits_per_pixel / 8;

	/* Memory allocation for framebuffer */
	info.screen_size =
		info.var.xres * info.var.yres * (info.var.bits_per_pixel / 8);
	ad = allocate_fb(info.var.xres, info.var.yres,
			 info.var.bits_per_pixel / 8, &info.screen_base);
	if (!ad) {
		printf("DIU:   Out of memory\n");
		return -1;
	}

	ad->pix_fmt = pixel_format;

	/* Disable chroma keying function */
	ad->ckmax_r = 0;
	ad->ckmax_g = 0;
	ad->ckmax_b = 0;

	ad->ckmin_r = 255;
	ad->ckmin_g = 255;
	ad->ckmin_b = 255;

	/* Initialize the gamma table */
	if (allocate_buf(&gamma, 256 * 3, 32) < 0) {
		printf("DIU:   Out of memory\n");
		return -1;
	}
	gamma_table_base = gamma.vaddr;
	for (i = 0; i <= 2; i++)
		for (j = 0; j < 256; j++)
			*gamma_table_base++ = j;

	if (gamma_fix == 1) {	/* fix the gamma */
		gamma_table_base = gamma.vaddr;
		for (i = 0; i < 256 * 3; i++) {
			gamma_table_base[i] = (gamma_table_base[i] << 2)
				| ((gamma_table_base[i] >> 6) & 0x03);
		}
	}

	/* Initialize the cursor */
	if (allocate_buf(&cursor, 32 * 32 * 2, 32) < 0) {
		printf("DIU:   Can't alloc cursor data\n");
		return -1;
	}

	/* Program DIU registers */
	out_be32(&hw->diu_mode, 0);	/* Temporarily disable the DIU */

	out_be32(&hw->gamma, gamma.paddr);
	out_be32(&hw->cursor, cursor.paddr);
	out_be32(&hw->bgnd, 0x007F7F7F);
	out_be32(&hw->disp_size, info.var.yres << 16 | info.var.xres);
	out_be32(&hw->hsyn_para, info.var.left_margin << 22 |
			info.var.hsync_len << 11 |
			info.var.right_margin);

	out_be32(&hw->vsyn_para, info.var.upper_margin << 22 |
			info.var.vsync_len << 11 |
			info.var.lower_margin);

	/* Pixel Clock configuration */
	diu_set_pixel_clock(info.var.pixclock);

	/* Set the frame buffers */
	out_be32(&hw->desc[0], virt_to_phys(ad));
	out_be32(&hw->desc[1], 0);
	out_be32(&hw->desc[2], 0);

	/* Enable the DIU, set display to all three planes */
	out_be32(&hw->diu_mode, 1);

	return 0;
}

void *video_hw_init(void)
{
	static GraphicDevice ctfb;
	const char *options;
	unsigned int depth = 0, freq = 0;

	if (!video_get_video_mode(&ctfb.winSizeX, &ctfb.winSizeY, &depth, &freq,
				  &options))
		return NULL;

	/* Find the monitor port, which is a required option */
	if (!options)
		return NULL;
	if (strncmp(options, "monitor=", 8) != 0)
		return NULL;

	if (platform_diu_init(ctfb.winSizeX, ctfb.winSizeY, options + 8) < 0)
		return NULL;

	/* fill in Graphic device struct */
	sprintf(ctfb.modeIdent, "%ix%ix%i %ikHz %iHz",
		ctfb.winSizeX, ctfb.winSizeY, depth, 64, freq);

	ctfb.frameAdrs = (unsigned int)info.screen_base;
	ctfb.plnSizeX = ctfb.winSizeX;
	ctfb.plnSizeY = ctfb.winSizeY;

	ctfb.gdfBytesPP = 4;
	ctfb.gdfIndex = GDF_32BIT_X888RGB;

	ctfb.isaBase = 0;
	ctfb.pciBase = 0;
	ctfb.memSize = info.screen_size;

	/* Cursor Start Address */
	ctfb.dprBase = 0;
	ctfb.vprBase = 0;
	ctfb.cprBase = 0;

	return &ctfb;
}