1
2#include <linux/config.h>
3#include <linux/module.h>
4#include <linux/kernel.h>
5#include <linux/errno.h>
6#include <linux/string.h>
7#include <linux/tty.h>
8#include <linux/delay.h>
9#include <linux/interrupt.h>
10#include <linux/init.h>
11#include <linux/pm.h>
12#include <linux/fb.h>
13#include <video/fbcon.h>
14#include <video/fbcon-mfb.h>
15#include <video/fbcon-cfb2.h>
16#include <video/fbcon-cfb4.h>
17#include <video/fbcon-cfb8.h>
18#include <asm/io.h>
19#include <asm/bootinfo.h>
20#include <asm/uaccess.h>
21#include <asm/tx3912.h>
22#include "tx3912fb.h"
23
24/*
25 * Frame buffer, palette and console structures
26 */
27static struct fb_info fb_info;
28static struct { u_char red, green, blue, pad; } palette[256];
29#ifdef FBCON_HAS_CFB8
30static union { u16 cfb8[16]; } fbcon_cmap;
31#endif
32static struct display global_disp;
33static int currcon = 0;
34
35/*
36 * Interface used by the world
37 */
38static int tx3912fb_get_fix(struct fb_fix_screeninfo *fix, int con,
39				struct fb_info *info);
40static int tx3912fb_get_var(struct fb_var_screeninfo *var, int con,
41				struct fb_info *info);
42static int tx3912fb_set_var(struct fb_var_screeninfo *var, int con,
43				struct fb_info *info);
44static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
45				struct fb_info *info);
46static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
47				struct fb_info *info);
48static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
49				u_long arg, int con, struct fb_info *info);
50
51/*
52 * Interface used by console driver
53 */
54int tx3912fb_init(void);
55static int tx3912fbcon_switch(int con, struct fb_info *info);
56static int tx3912fbcon_updatevar(int con, struct fb_info *info);
57static void tx3912fbcon_blank(int blank, struct fb_info *info);
58
59/*
60 * Macros
61 */
62#define get_line_length(xres_virtual, bpp) \
63		(u_long) (((int) xres_virtual * (int) bpp + 7) >> 3)
64
65/*
66 * Internal routines
67 */
68static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green,
69			u_int *blue, u_int *transp, struct fb_info *info);
70static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
71			u_int transp, struct fb_info *info);
72static void tx3912fb_install_cmap(int con, struct fb_info *info);
73
74
75/*
76 * Frame buffer operations structure used by console driver
77 */
78static struct fb_ops tx3912fb_ops = {
79	owner: THIS_MODULE,
80	fb_get_fix: tx3912fb_get_fix,
81	fb_get_var: tx3912fb_get_var,
82	fb_set_var: tx3912fb_set_var,
83	fb_get_cmap: tx3912fb_get_cmap,
84	fb_set_cmap: tx3912fb_set_cmap,
85	fb_ioctl: tx3912fb_ioctl,
86};
87
88
89/*
90 *  Get fixed display data
91 */
92static int tx3912fb_get_fix(struct fb_fix_screeninfo *fix, int con,
93				struct fb_info *info)
94{
95	struct display *display;
96
97	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
98	strcpy(fix->id, TX3912FB_NAME);
99
100	if (con == -1)
101		display = &global_disp;
102	else
103		display = &fb_display[con];
104
105	fix->smem_start		= tx3912fb_vaddr;
106	fix->smem_len		= tx3912fb_size;
107	fix->type		= display->type;
108	fix->type_aux		= display->type_aux;
109	fix->xpanstep		= 0;
110	fix->ypanstep		= display->ypanstep;
111	fix->ywrapstep		= display->ywrapstep;
112	fix->visual		= display->visual;
113	fix->line_length	= display->line_length;
114	fix->accel		= FB_ACCEL_NONE;
115
116	return 0;
117}
118
119/*
120 * Get user display data
121 */
122static int tx3912fb_get_var(struct fb_var_screeninfo *var, int con,
123				struct fb_info *info)
124{
125	if (con == -1)
126		*var = tx3912fb_info;
127	else
128		*var = fb_display[con].var;
129
130	return 0;
131}
132
133/*
134 *  Set user display data
135 */
136static int tx3912fb_set_var(struct fb_var_screeninfo *var, int con,
137				struct fb_info *info)
138{
139	int err, activate = var->activate;
140	int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
141	u_long line_length;
142	struct display *display;
143
144	if (con == -1)
145		display = &global_disp;
146	else
147		display = &fb_display[con];
148
149	/*
150	 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal
151	 * as FB_VMODE_SMOOTH_XPAN is only used internally
152	 */
153	if (var->vmode & FB_VMODE_CONUPDATE) {
154		var->xoffset = display->var.xoffset;
155		var->yoffset = display->var.yoffset;
156		var->vmode |= FB_VMODE_YWRAP;
157	}
158
159	/*
160	 * Make sure values are in range
161	 */
162	if (!var->xres)
163		var->xres = 1;
164	if (!var->yres)
165		var->yres = 1;
166	if (var->xres > var->xres_virtual)
167		var->xres_virtual = var->xres;
168	if (var->yres > var->yres_virtual)
169		var->yres_virtual = var->yres;
170	if (var->bits_per_pixel <= 1)
171		var->bits_per_pixel = 1;
172	else if (var->bits_per_pixel <= 2)
173		var->bits_per_pixel = 2;
174	else if (var->bits_per_pixel <= 4)
175		var->bits_per_pixel = 4;
176	else if (var->bits_per_pixel <= 8)
177		var->bits_per_pixel = 8;
178	else
179		return -EINVAL;
180
181	/*
182	 * Memory limit
183	 */
184	line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
185	if ((line_length * var->yres_virtual) > tx3912fb_size)
186		return -ENOMEM;
187
188	/*
189	 * This is only for color and we only support 8-bit color
190	 */
191	if (var->bits_per_pixel) {
192		/* RGB 332 */
193		var->red.offset = 5;
194		var->red.length = 3;
195		var->green.offset = 2;
196		var->green.length = 3;
197		var->blue.offset = 0;
198		var->blue.length = 2;
199		var->transp.offset = 0;
200		var->transp.length = 0;
201	}
202	var->red.msb_right = 0;
203	var->green.msb_right = 0;
204	var->blue.msb_right = 0;
205	var->transp.msb_right = 0;
206
207	/*
208	 * Make changes if necessary
209	 */
210	if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
211
212		oldxres = display->var.xres;
213		oldyres = display->var.yres;
214		oldvxres = display->var.xres_virtual;
215		oldvyres = display->var.yres_virtual;
216		oldbpp = display->var.bits_per_pixel;
217		display->var = *var;
218
219		if (oldxres != var->xres || oldyres != var->yres ||
220		    oldvxres != var->xres_virtual ||
221		    oldvyres != var->yres_virtual ||
222		    oldbpp != var->bits_per_pixel) {
223
224			display->screen_base = (u_char *) tx3912fb_vaddr;
225
226			switch (var->bits_per_pixel) {
227			case 1:
228				display->visual = FB_VISUAL_MONO10;
229				break;
230			case 2:
231				display->visual = FB_VISUAL_PSEUDOCOLOR;
232			case 4:
233			case 8:
234				display->visual = FB_VISUAL_TRUECOLOR;
235				break;
236			}
237
238			display->type = FB_TYPE_PACKED_PIXELS;
239			display->type_aux = 0;
240			display->ypanstep = 0;
241			display->ywrapstep = 0;
242			display->next_line =
243			display->line_length =
244				get_line_length(var->xres_virtual,
245					var->bits_per_pixel);
246			display->can_soft_blank = 0;
247			display->inverse = FB_IS_INVERSE;
248
249			switch (var->bits_per_pixel) {
250#ifdef CONFIG_FBCON_MFB
251			case 1:
252				display->dispsw = &fbcon_mfb;
253				break;
254#endif
255#ifdef CONFIG_FBCON_CFB2
256			case 2:
257				display->dispsw = &fbcon_cfb2;
258				break;
259#endif
260#ifdef CONFIG_FBCON_CFB4
261			case 4:
262				display->dispsw = &fbcon_cfb4;
263				break;
264#endif
265#ifdef CONFIG_FBCON_CFB8
266			case 8:
267				display->dispsw = &fbcon_cfb8;
268				display->dispsw_data = fbcon_cmap.cfb8;
269				break;
270#endif
271			default:
272				display->dispsw = &fbcon_dummy;
273				break;
274	    		}
275
276			if (fb_info.changevar)
277				(*fb_info.changevar)(con);
278		}
279
280		if (oldbpp != var->bits_per_pixel) {
281			if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
282				return err;
283			tx3912fb_install_cmap(con, info);
284		}
285	}
286
287	return 0;
288}
289
290/*
291 *  Get the colormap
292 */
293static int tx3912fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
294			struct fb_info *info)
295{
296	if (con == currcon)
297		return fb_get_cmap(cmap, kspc, tx3912fb_getcolreg, info);
298	else if (fb_display[con].cmap.len) /* non default colormap? */
299		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
300	else
301		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
302
303	return 0;
304}
305
306/*
307 *  Set the Colormap
308 */
309static int tx3912fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
310			struct fb_info *info)
311{
312	int err;
313
314	if (!fb_display[con].cmap.len)
315		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
316				1<<fb_display[con].var.bits_per_pixel, 0)))
317			return err;
318
319	if (con == currcon)
320		return fb_set_cmap(cmap, kspc, tx3912fb_setcolreg, info);
321	else
322		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
323
324	return 0;
325}
326
327/*
328 *  Framebuffer ioctl
329 */
330static int tx3912fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
331				u_long arg, int con, struct fb_info *info)
332{
333	return -EINVAL;
334}
335
336/*
337 * Initialization of the framebuffer
338 */
339int __init tx3912fb_init(void)
340{
341	/* Disable the video logic */
342	outl(inl(TX3912_VIDEO_CTRL1) &
343		~(TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
344		TX3912_VIDEO_CTRL1);
345	udelay(200);
346
347	/* Set start address for DMA transfer */
348	outl(tx3912fb_paddr, TX3912_VIDEO_CTRL3);
349
350	/* Set end address for DMA transfer */
351	outl((tx3912fb_paddr + tx3912fb_size + 1), TX3912_VIDEO_CTRL4);
352
353	/* Set the pixel depth */
354	switch (tx3912fb_info.bits_per_pixel) {
355	case 1:
356		/* Monochrome */
357		outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK,
358			TX3912_VIDEO_CTRL1);
359		break;
360	case 4:
361		/* 4-bit gray */
362		outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK,
363			TX3912_VIDEO_CTRL1);
364		outl(inl(TX3912_VIDEO_CTRL1) |
365			TX3912_VIDEO_CTRL1_BITSEL_4BIT_GRAY,
366			TX3912_VIDEO_CTRL1);
367		break;
368	case 8:
369		/* 8-bit color */
370		outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK,
371			TX3912_VIDEO_CTRL1);
372		outl(inl(TX3912_VIDEO_CTRL1) |
373			TX3912_VIDEO_CTRL1_BITSEL_8BIT_COLOR,
374			TX3912_VIDEO_CTRL1);
375		break;
376	case 2:
377	default:
378		/* 2-bit gray */
379		outl(inl(TX3912_VIDEO_CTRL1) & ~TX3912_VIDEO_CTRL1_BITSEL_MASK,
380			TX3912_VIDEO_CTRL1);
381		outl(inl(TX3912_VIDEO_CTRL1) |
382			TX3912_VIDEO_CTRL1_BITSEL_2BIT_GRAY,
383			TX3912_VIDEO_CTRL1);
384		break;
385	}
386
387	/* Enable the video clock */
388	outl(inl(TX3912_CLK_CTRL) | TX3912_CLK_CTRL_ENVIDCLK,
389		TX3912_CLK_CTRL);
390
391	/* Unfreeze video logic and enable DF toggle */
392	outl(inl(TX3912_VIDEO_CTRL1) &
393		~(TX3912_VIDEO_CTRL1_ENFREEZEFRAME | TX3912_VIDEO_CTRL1_DFMODE),
394		TX3912_VIDEO_CTRL1);
395	udelay(200);
396
397	/* Clear the framebuffer */
398	memset((void *) tx3912fb_vaddr, 0xff, tx3912fb_size);
399	udelay(200);
400
401	/* Enable the video logic */
402	outl(inl(TX3912_VIDEO_CTRL1) |
403		(TX3912_VIDEO_CTRL1_ENVID | TX3912_VIDEO_CTRL1_DISPON),
404		TX3912_VIDEO_CTRL1);
405
406	strcpy(fb_info.modename, TX3912FB_NAME);
407	fb_info.changevar = NULL;
408	fb_info.node = -1;
409	fb_info.fbops = &tx3912fb_ops;
410	fb_info.disp = &global_disp;
411	fb_info.switch_con = &tx3912fbcon_switch;
412	fb_info.updatevar = &tx3912fbcon_updatevar;
413	fb_info.blank = &tx3912fbcon_blank;
414	fb_info.flags = FBINFO_FLAG_DEFAULT;
415
416	tx3912fb_set_var(&tx3912fb_info, -1, &fb_info);
417
418	if (register_framebuffer(&fb_info) < 0)
419		return -1;
420
421	printk (KERN_INFO "fb%d: TX3912 frame buffer using %uKB.\n",
422		GET_FB_IDX(fb_info.node), (u_int) (tx3912fb_size >> 10));
423
424	return 0;
425}
426
427/*
428 * Switch the console to be the framebuffer
429 */
430static int tx3912fbcon_switch(int con, struct fb_info *info)
431{
432	/* Save off the color map if needed */
433	if (fb_display[currcon].cmap.len)
434		fb_get_cmap(&fb_display[currcon].cmap, 1,
435			tx3912fb_getcolreg, info);
436
437	/* Make the switch */
438	currcon = con;
439
440	/* Install new colormap */
441	tx3912fb_install_cmap(con, info);
442
443	return 0;
444}
445
446/*
447 * Update variable structure
448 */
449static int tx3912fbcon_updatevar(int con, struct fb_info *info)
450{
451	/* Nothing */
452	return 0;
453}
454
455/*
456 * Blank the display
457 */
458static void tx3912fbcon_blank(int blank, struct fb_info *info)
459{
460	printk("tx3912fbcon_blank\n");
461}
462
463/*
464 * Read a single color register
465 */
466static int tx3912fb_getcolreg(u_int regno, u_int *red, u_int *green,
467			u_int *blue, u_int *transp, struct fb_info *info)
468{
469	if (regno > 255)
470		return 1;
471
472#if FB_IS_GREY
473	{
474		u_int grey;
475
476		grey = regno * 255 / 15;
477
478#if FB_IS_INVERSE
479		grey ^= 255;
480#endif
481		grey |= grey << 8;
482		*red = grey;
483		*green = grey;
484		*blue = grey;
485	}
486#else
487	*red = (palette[regno].red<<8) | palette[regno].red;
488	*green = (palette[regno].green<<8) | palette[regno].green;
489	*blue = (palette[regno].blue<<8) | palette[regno].blue;
490#endif
491	*transp = 0;
492
493	return 0;
494}
495
496/*
497 * Set a single color register
498 */
499static int tx3912fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
500				u_int transp, struct fb_info *info)
501{
502	if (regno > 255)
503		return 1;
504
505#ifdef FBCON_HAS_CFB8
506	if( regno < 16 )
507		fbcon_cmap.cfb8[regno] = ((red & 0xe000) >> 8)
508					| ((green & 0xe000) >> 11)
509					| ((blue & 0xc000) >> 14);
510#endif
511
512	red >>= 8;
513	green >>= 8;
514	blue >>= 8;
515	palette[regno].red = red;
516	palette[regno].green = green;
517	palette[regno].blue = blue;
518
519	return 0;
520}
521
522/*
523 * Install the color map
524 */
525static void tx3912fb_install_cmap(int con, struct fb_info *info)
526{
527	if (con != currcon)
528		return;
529
530	if (fb_display[con].cmap.len)
531		fb_set_cmap(&fb_display[con].cmap, 1, tx3912fb_setcolreg, info);
532	else
533		fb_set_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel), 1, tx3912fb_setcolreg, info);
534}
535
536MODULE_LICENSE("GPL");
537