📄 acornfb.c
字号:
/* * linux/drivers/video/acornfb.c * * Copyright (C) 1998-2001 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Frame buffer code for Acorn platforms * * NOTE: Most of the modes with X!=640 will disappear shortly. * NOTE: Startup setting of HS & VS polarity not supported. * (do we need to support it if we're coming up in 640x480?) */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/fb.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/mach-types.h>#include <asm/uaccess.h>#include <video/fbcon.h>#include <video/fbcon-mfb.h>#include <video/fbcon-cfb2.h>#include <video/fbcon-cfb4.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb32.h>#include "acornfb.h"/* * VIDC machines can't do 16 or 32BPP modes. */#ifdef HAS_VIDC#undef FBCON_HAS_CFB16#undef FBCON_HAS_CFB32#endif/* * Default resolution. * NOTE that it has to be supported in the table towards * the end of this file. */#define DEFAULT_XRES 640#define DEFAULT_YRES 480/* * The order here defines which BPP we * pick depending on which resolutions * we have configured. */#if defined(FBCON_HAS_CFB4)# define DEFAULT_BPP 4#elif defined(FBCON_HAS_CFB8)# define DEFAULT_BPP 8#elif defined(FBCON_HAS_CFB16)# define DEFAULT_BPP 16#elif defined(FBCON_HAS_CFB2)# define DEFAULT_BPP 2#elif defined(FBCON_HAS_MFB)# define DEFAULT_BPP 1#else#error No suitable framebuffers configured#endif/* * define this to debug the video mode selection */#undef DEBUG_MODE_SELECTION/* * Translation from RISC OS monitor types to actual * HSYNC and VSYNC frequency ranges. These are * probably not right, but they're the best info I * have. Allow 1% either way on the nominal for TVs. */#define NR_MONTYPES 6static struct fb_monspecs monspecs[NR_MONTYPES] __initdata = { { 15469, 15781, 49, 51, 0 }, /* TV */ { 0, 99999, 0, 99, 0 }, /* Multi Freq */ { 58608, 58608, 64, 64, 0 }, /* Hi-res mono */ { 30000, 70000, 60, 60, 0 }, /* VGA */ { 30000, 70000, 56, 75, 0 }, /* SVGA */ { 30000, 70000, 60, 60, 0 }};static struct display global_disp;static struct fb_info fb_info;static struct acornfb_par current_par;static struct vidc_timing current_vidc;static struct fb_var_screeninfo __initdata init_var = {};extern int acornfb_depth; /* set by setup.c */extern unsigned int vram_size; /* set by setup.c */#ifdef HAS_VIDC#define MAX_SIZE 480*1024/* CTL VIDC Actual * 24.000 0 8.000 * 25.175 0 8.392 * 36.000 0 12.000 * 24.000 1 12.000 * 25.175 1 12.588 * 24.000 2 16.000 * 25.175 2 16.783 * 36.000 1 18.000 * 24.000 3 24.000 * 36.000 2 24.000 * 25.175 3 25.175 * 36.000 3 36.000 */struct pixclock { u_long min_clock; u_long max_clock; u_int vidc_ctl; u_int vid_ctl;};static struct pixclock arc_clocks[] = { /* we allow +/-1% on these */ { 123750, 126250, VIDC_CTRL_DIV3, VID_CTL_24MHz }, /* 8.000MHz */ { 82500, 84167, VIDC_CTRL_DIV2, VID_CTL_24MHz }, /* 12.000MHz */ { 61875, 63125, VIDC_CTRL_DIV1_5, VID_CTL_24MHz }, /* 16.000MHz */ { 41250, 42083, VIDC_CTRL_DIV1, VID_CTL_24MHz }, /* 24.000MHz */};#ifdef CONFIG_ARCH_A5Kstatic struct pixclock a5k_clocks[] = { { 117974, 120357, VIDC_CTRL_DIV3, VID_CTL_25MHz }, /* 8.392MHz */ { 78649, 80238, VIDC_CTRL_DIV2, VID_CTL_25MHz }, /* 12.588MHz */ { 58987, 60178, VIDC_CTRL_DIV1_5, VID_CTL_25MHz }, /* 16.588MHz */ { 55000, 56111, VIDC_CTRL_DIV2, VID_CTL_36MHz }, /* 18.000MHz */ { 39325, 40119, VIDC_CTRL_DIV1, VID_CTL_25MHz }, /* 25.175MHz */ { 27500, 28055, VIDC_CTRL_DIV1, VID_CTL_36MHz }, /* 36.000MHz */};#endifstatic struct pixclock *acornfb_valid_pixrate(u_long pixclock){ u_int i; for (i = 0; i < ARRAY_SIZE(arc_clocks); i++) if (pixclock > arc_clocks[i].min_clock && pixclock < arc_clocks[i].max_clock) return arc_clocks + i;#ifdef CONFIG_ARCH_A5K if (machine_is_a5k()) { for (i = 0; i < ARRAY_SIZE(a5k_clocks); i++) if (pixclock > a5k_clocks[i].min_clock && pixclock < a5k_clocks[i].max_clock) return a5k_clocks + i; }#endif return NULL;}/* VIDC Rules: * hcr : must be even (interlace, hcr/2 must be even) * hswr : must be even * hdsr : must be odd * hder : must be odd * * vcr : must be odd * vswr : >= 1 * vdsr : >= 1 * vder : >= vdsr * if interlaced, then hcr/2 must be even */static voidacornfb_set_timing(struct fb_var_screeninfo *var){ struct pixclock *pclk; struct vidc_timing vidc; u_int horiz_correction; u_int sync_len, display_start, display_end, cycle; u_int is_interlaced; u_int vid_ctl, vidc_ctl; u_int bandwidth; memset(&vidc, 0, sizeof(vidc)); pclk = acornfb_valid_pixrate(var->pixclock); vidc_ctl = pclk->vidc_ctl; vid_ctl = pclk->vid_ctl; bandwidth = var->pixclock * 8 / var->bits_per_pixel; /* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */ if (bandwidth > 143500) vidc_ctl |= VIDC_CTRL_FIFO_3_7; else if (bandwidth > 71750) vidc_ctl |= VIDC_CTRL_FIFO_2_6; else if (bandwidth > 35875) vidc_ctl |= VIDC_CTRL_FIFO_1_5; else vidc_ctl |= VIDC_CTRL_FIFO_0_4; switch (var->bits_per_pixel) { case 1: horiz_correction = 19; vidc_ctl |= VIDC_CTRL_1BPP; break; case 2: horiz_correction = 11; vidc_ctl |= VIDC_CTRL_2BPP; break; case 4: horiz_correction = 7; vidc_ctl |= VIDC_CTRL_4BPP; break; default: case 8: horiz_correction = 5; vidc_ctl |= VIDC_CTRL_8BPP; break; } if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */ vidc_ctl |= VIDC_CTRL_CSYNC; else { if (!(var->sync & FB_SYNC_HOR_HIGH_ACT)) vid_ctl |= VID_CTL_HS_NHSYNC; if (!(var->sync & FB_SYNC_VERT_HIGH_ACT)) vid_ctl |= VID_CTL_VS_NVSYNC; } sync_len = var->hsync_len; display_start = sync_len + var->left_margin; display_end = display_start + var->xres; cycle = display_end + var->right_margin; /* if interlaced, then hcr/2 must be even */ is_interlaced = (var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED; if (is_interlaced) { vidc_ctl |= VIDC_CTRL_INTERLACE; if (cycle & 2) { cycle += 2; var->right_margin += 2; } } vidc.h_cycle = (cycle - 2) / 2; vidc.h_sync_width = (sync_len - 2) / 2; vidc.h_border_start = (display_start - 1) / 2; vidc.h_display_start = (display_start - horiz_correction) / 2; vidc.h_display_end = (display_end - horiz_correction) / 2; vidc.h_border_end = (display_end - 1) / 2; vidc.h_interlace = (vidc.h_cycle + 1) / 2; sync_len = var->vsync_len; display_start = sync_len + var->upper_margin; display_end = display_start + var->yres; cycle = display_end + var->lower_margin; if (is_interlaced) cycle = (cycle - 3) / 2; else cycle = cycle - 1; vidc.v_cycle = cycle; vidc.v_sync_width = sync_len - 1; vidc.v_border_start = display_start - 1; vidc.v_display_start = vidc.v_border_start; vidc.v_display_end = display_end - 1; vidc.v_border_end = vidc.v_display_end; if (machine_is_a5k()) __raw_writeb(vid_ctl, IOEB_VID_CTL); if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) { current_vidc = vidc; vidc_writel(0xe0000000 | vidc_ctl); vidc_writel(0x80000000 | (vidc.h_cycle << 14)); vidc_writel(0x84000000 | (vidc.h_sync_width << 14)); vidc_writel(0x88000000 | (vidc.h_border_start << 14)); vidc_writel(0x8c000000 | (vidc.h_display_start << 14)); vidc_writel(0x90000000 | (vidc.h_display_end << 14)); vidc_writel(0x94000000 | (vidc.h_border_end << 14)); vidc_writel(0x98000000); vidc_writel(0x9c000000 | (vidc.h_interlace << 14)); vidc_writel(0xa0000000 | (vidc.v_cycle << 14)); vidc_writel(0xa4000000 | (vidc.v_sync_width << 14)); vidc_writel(0xa8000000 | (vidc.v_border_start << 14)); vidc_writel(0xac000000 | (vidc.v_display_start << 14)); vidc_writel(0xb0000000 | (vidc.v_display_end << 14)); vidc_writel(0xb4000000 | (vidc.v_border_end << 14)); vidc_writel(0xb8000000); vidc_writel(0xbc000000); }#ifdef DEBUG_MODE_SELECTION printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres, var->yres, var->bits_per_pixel); printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle); printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width); printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start); printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start); printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end); printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end); printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace); printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle); printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width); printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start); printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start); printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end); printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end); printk(KERN_DEBUG " VIDC Ctrl (E) : 0x%08X\n", vidc_ctl); printk(KERN_DEBUG " IOEB Ctrl : 0x%08X\n", vid_ctl);#endif}static inline voidacornfb_palette_write(u_int regno, union palette pal){ vidc_writel(pal.p);}static inline union paletteacornfb_palette_encode(u_int regno, u_int red, u_int green, u_int blue, u_int trans){ union palette pal; pal.p = 0; pal.vidc.reg = regno; pal.vidc.red = red >> 12; pal.vidc.green = green >> 12; pal.vidc.blue = blue >> 12; return pal;}static voidacornfb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans){ *red = EXTEND4(current_par.palette[regno].vidc.red); *green = EXTEND4(current_par.palette[regno].vidc.green); *blue = EXTEND4(current_par.palette[regno].vidc.blue); *trans = current_par.palette[regno].vidc.trans ? -1 : 0;}#endif#ifdef HAS_VIDC20#include <asm/arch/acornfb.h>#define MAX_SIZE 2*1024*1024/* VIDC20 has a different set of rules from the VIDC: * hcr : must be multiple of 4 * hswr : must be even * hdsr : must be even * hder : must be even * vcr : >= 2, (interlace, must be odd) * vswr : >= 1 * vdsr : >= 1 * vder : >= vdsr */static voidacornfb_set_timing(struct fb_var_screeninfo *var){ struct vidc_timing vidc; u_int vcr, fsize; u_int ext_ctl, dat_ctl; u_int words_per_line; memset(&vidc, 0, sizeof(vidc)); vidc.h_sync_width = var->hsync_len - 8; vidc.h_border_start = vidc.h_sync_width + var->left_margin + 8 - 12; vidc.h_display_start = vidc.h_border_start + 12 - 18; vidc.h_display_end = vidc.h_display_start + var->xres; vidc.h_border_end = vidc.h_display_end + 18 - 12; vidc.h_cycle = vidc.h_border_end + var->right_margin + 12 - 8; vidc.h_interlace = vidc.h_cycle / 2; vidc.v_sync_width = var->vsync_len - 1; vidc.v_border_start = vidc.v_sync_width + var->upper_margin; vidc.v_display_start = vidc.v_border_start; vidc.v_display_end = vidc.v_display_start + var->yres; vidc.v_border_end = vidc.v_display_end; vidc.control = acornfb_default_control(); vcr = var->vsync_len + var->upper_margin + var->yres + var->lower_margin; if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { vidc.v_cycle = (vcr - 3) / 2; vidc.control |= VIDC20_CTRL_INT; } else vidc.v_cycle = vcr - 2; switch (var->bits_per_pixel) { case 1: vidc.control |= VIDC20_CTRL_1BPP; break; case 2: vidc.control |= VIDC20_CTRL_2BPP; break; case 4: vidc.control |= VIDC20_CTRL_4BPP; break; default: case 8: vidc.control |= VIDC20_CTRL_8BPP; break; case 16: vidc.control |= VIDC20_CTRL_16BPP; break; case 32: vidc.control |= VIDC20_CTRL_32BPP; break; } acornfb_vidc20_find_rates(&vidc, var); fsize = var->vsync_len + var->upper_margin + var->lower_margin - 1; if (memcmp(¤t_vidc, &vidc, sizeof(vidc))) { current_vidc = vidc; vidc_writel(VIDC20_CTRL| vidc.control); vidc_writel(0xd0000000 | vidc.pll_ctl); vidc_writel(0x80000000 | vidc.h_cycle); vidc_writel(0x81000000 | vidc.h_sync_width); vidc_writel(0x82000000 | vidc.h_border_start); vidc_writel(0x83000000 | vidc.h_display_start); vidc_writel(0x84000000 | vidc.h_display_end); vidc_writel(0x85000000 | vidc.h_border_end); vidc_writel(0x86000000); vidc_writel(0x87000000 | vidc.h_interlace); vidc_writel(0x90000000 | vidc.v_cycle); vidc_writel(0x91000000 | vidc.v_sync_width); vidc_writel(0x92000000 | vidc.v_border_start); vidc_writel(0x93000000 | vidc.v_display_start); vidc_writel(0x94000000 | vidc.v_display_end); vidc_writel(0x95000000 | vidc.v_border_end); vidc_writel(0x96000000); vidc_writel(0x97000000); } iomd_writel(fsize, IOMD_FSIZE); ext_ctl = acornfb_default_econtrol(); if (var->sync & FB_SYNC_COMP_HIGH_ACT) /* should be FB_SYNC_COMP */ ext_ctl |= VIDC20_ECTL_HS_NCSYNC | VIDC20_ECTL_VS_NCSYNC; else { if (var->sync & FB_SYNC_HOR_HIGH_ACT) ext_ctl |= VIDC20_ECTL_HS_HSYNC; else ext_ctl |= VIDC20_ECTL_HS_NHSYNC; if (var->sync & FB_SYNC_VERT_HIGH_ACT) ext_ctl |= VIDC20_ECTL_VS_VSYNC; else ext_ctl |= VIDC20_ECTL_VS_NVSYNC; } vidc_writel(VIDC20_ECTL | ext_ctl); words_per_line = var->xres * var->bits_per_pixel / 32; if (current_par.using_vram && current_par.screen_size == 2048*1024) words_per_line /= 2; /* RiscPC doesn't use the VIDC's VRAM control. */ dat_ctl = VIDC20_DCTL_VRAM_DIS | VIDC20_DCTL_SNA | words_per_line; /* The data bus width is dependent on both the type * and amount of video memory. * DRAM 32bit low * 1MB VRAM 32bit * 2MB VRAM 64bit */ if (current_par.using_vram && current_par.vram_half_sam == 2048) { dat_ctl |= VIDC20_DCTL_BUS_D63_0; } else dat_ctl |= VIDC20_DCTL_BUS_D31_0; vidc_writel(VIDC20_DCTL | dat_ctl);#ifdef DEBUG_MODE_SELECTION printk(KERN_DEBUG "VIDC registers for %dx%dx%d:\n", var->xres, var->yres, var->bits_per_pixel); printk(KERN_DEBUG " H-cycle : %d\n", vidc.h_cycle); printk(KERN_DEBUG " H-sync-width : %d\n", vidc.h_sync_width); printk(KERN_DEBUG " H-border-start : %d\n", vidc.h_border_start); printk(KERN_DEBUG " H-display-start : %d\n", vidc.h_display_start); printk(KERN_DEBUG " H-display-end : %d\n", vidc.h_display_end); printk(KERN_DEBUG " H-border-end : %d\n", vidc.h_border_end); printk(KERN_DEBUG " H-interlace : %d\n", vidc.h_interlace); printk(KERN_DEBUG " V-cycle : %d\n", vidc.v_cycle); printk(KERN_DEBUG " V-sync-width : %d\n", vidc.v_sync_width); printk(KERN_DEBUG " V-border-start : %d\n", vidc.v_border_start); printk(KERN_DEBUG " V-display-start : %d\n", vidc.v_display_start); printk(KERN_DEBUG " V-display-end : %d\n", vidc.v_display_end); printk(KERN_DEBUG " V-border-end : %d\n", vidc.v_border_end); printk(KERN_DEBUG " Ext Ctrl (C) : 0x%08X\n", ext_ctl); printk(KERN_DEBUG " PLL Ctrl (D) : 0x%08X\n", vidc.pll_ctl); printk(KERN_DEBUG " Ctrl (E) : 0x%08X\n", vidc.control); printk(KERN_DEBUG " Data Ctrl (F) : 0x%08X\n", dat_ctl); printk(KERN_DEBUG " Fsize : 0x%08X\n", fsize);#endif}static inline voidacornfb_palette_write(u_int regno, union palette pal){ vidc_writel(0x10000000 | regno); vidc_writel(pal.p);}static inline union paletteacornfb_palette_encode(u_int regno, u_int red, u_int green, u_int blue, u_int trans){ union palette pal; pal.p = 0; pal.vidc20.red = red >> 8; pal.vidc20.green = green >> 8; pal.vidc20.blue = blue >> 8; return pal;}static voidacornfb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans){ *red = EXTEND8(current_par.palette[regno].vidc20.red); *green = EXTEND8(current_par.palette[regno].vidc20.green); *blue = EXTEND8(current_par.palette[regno].vidc20.blue); *trans = EXTEND4(current_par.palette[regno].vidc20.ext);}#endif/* * Before selecting the timing parameters, adjust * the resolution to fit the rules. */static intacornfb_adjust_timing(struct fb_var_screeninfo *var, int con){ u_int font_line_len; u_int fontht; u_int sam_size, min_size, size; u_int nr_y; /* xres must be even */ var->xres = (var->xres + 1) & ~1; /* * We don't allow xres_virtual to differ from xres */ var->xres_virtual = var->xres; var->xoffset = 0; /* * Find the font height */ if (con == -1) fontht = fontheight(&global_disp); else fontht = fontheight(fb_display + con); if (fontht == 0) fontht = 8; if (current_par.using_vram) sam_size = current_par.vram_half_sam * 2; else sam_size = 16; /* * Now, find a value for yres_virtual which allows * us to do ywrap scrolling. The value of * yres_virtual must be such that the end of the * displayable frame buffer must be aligned with * the start of a font line. */ font_line_len = var->xres * var->bits_per_pixel * fontht / 8; min_size = var->xres * var->yres * var->bits_per_pixel / 8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -