📄 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?) * * FIXME: (things broken by the "new improved" FBCON API) * - Blanking 8bpp displays with VIDC */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/ctype.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/fb.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <mach/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/mach-types.h>#include <asm/pgtable.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#define DEFAULT_BPP 4/* * 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 = { { /* TV */ .hfmin = 15469, .hfmax = 15781, .vfmin = 49, .vfmax = 51, }, { /* Multi Freq */ .hfmin = 0, .hfmax = 99999, .vfmin = 0, .vfmax = 199, }, { /* Hi-res mono */ .hfmin = 58608, .hfmax = 58608, .vfmin = 64, .vfmax = 64, }, { /* VGA */ .hfmin = 30000, .hfmax = 70000, .vfmin = 60, .vfmax = 60, }, { /* SVGA */ .hfmin = 30000, .hfmax = 70000, .vfmin = 56, .vfmax = 75, }, { .hfmin = 30000, .hfmax = 70000, .vfmin = 60, .vfmax = 60, }};static struct fb_info fb_info;static struct acornfb_par current_par;static struct vidc_timing current_vidc;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 */};static struct pixclock *acornfb_valid_pixrate(struct fb_var_screeninfo *var){ u_long pixclock = var->pixclock; u_int i; if (!var->pixclock) return NULL; 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; 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); 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 intacornfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info){ union palette pal; if (regno >= current_par.palette_size) return 1; pal.p = 0; pal.vidc.reg = regno; pal.vidc.red = red >> 12; pal.vidc.green = green >> 12; pal.vidc.blue = blue >> 12; current_par.palette[regno] = pal; vidc_writel(pal.p); return 0;}#endif#ifdef HAS_VIDC20#include <mach/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 void acornfb_set_timing(struct fb_info *info){ struct fb_var_screeninfo *var = &info->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 && info->fix.smem_len == 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -