📄 pxafb.c
字号:
/* * linux/drivers/video/pxafb.c * * Copyright (C) 1999 Eric A. Thomas. * Copyright (C) 2004 Jean-Frederic Clere. * Copyright (C) 2004 Ian Campbell. * Copyright (C) 2004 Jeff Lackey. * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas * which in turn is * Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Intel PXA250/210 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * * linux-arm-kernel@lists.arm.linux.org.uk * * Add support for overlay1 and overlay2 based on pxafb_overlay.c: * * Copyright (C) 2004, Intel Corporation * * 2003/08/27: <yu.tang@intel.com> * 2004/03/10: <stanley.cai@intel.com> * 2004/10/28: <yan.yin@intel.com> * * Copyright (C) 2006-2008 Marvell International Ltd. * All Rights Reserved */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/cpufreq.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <linux/err.h>#include <linux/completion.h>#include <linux/mutex.h>#include <linux/kthread.h>#include <linux/freezer.h>#include <mach/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/div64.h>#include <mach/pxa-regs.h>#include <mach/bitfield.h>#include <mach/pxafb.h>/* * Complain if VAR is out of range. */#define DEBUG_VAR 1#include "pxafb.h"/* Bits which should not be set in machine configuration structures */#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ LCCR0_SFM | LCCR0_LDM | LCCR0_ENB)#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ LCCR3_PCD | LCCR3_BPP(0xf))static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);static void setup_base_frame(struct pxafb_info *fbi, int branch);static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, unsigned long offset, size_t size);static unsigned long video_mem_size = 0;static inline unsigned longlcd_readl(struct pxafb_info *fbi, unsigned int off){ return __raw_readl(fbi->mmio_base + off);}static inline voidlcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val){ __raw_writel(val, fbi->mmio_base + off);}static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state){ unsigned long flags; local_irq_save(flags); /* * We need to handle two requests being made at the same time. * There are two important cases: * 1. When we are changing VT (C_REENABLE) while unblanking * (C_ENABLE) We must perform the unblanking, which will * do our REENABLE for us. * 2. When we are blanking, but immediately unblank before * we have blanked. We do the "REENABLE" thing here as * well, just to be sure. */ if (fbi->task_state == C_ENABLE && state == C_REENABLE) state = (u_int) -1; if (fbi->task_state == C_DISABLE && state == C_ENABLE) state = C_REENABLE; if (state != (u_int)-1) { fbi->task_state = state; schedule_work(&fbi->task); } local_irq_restore(flags);}static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;}static intpxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; u_int val; if (regno >= fbi->palette_size) return 1; if (fbi->fb.var.grayscale) { fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); return 0; } switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { case LCCR4_PAL_FOR_0: val = ((red >> 0) & 0xf800); val |= ((green >> 5) & 0x07e0); val |= ((blue >> 11) & 0x001f); fbi->palette_cpu[regno] = val; break; case LCCR4_PAL_FOR_1: val = ((red << 8) & 0x00f80000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000f8); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_2: val = ((red << 8) & 0x00fc0000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000fc); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_3: val = ((red << 8) & 0x00ff0000); val |= ((green >> 0) & 0x0000ff00); val |= ((blue >> 8) & 0x000000ff); ((u32 *)(fbi->palette_cpu))[regno] = val; break; } return 0;}static intpxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; unsigned int val; int ret = 1; /* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested. */ if (fbi->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; } /* * If greyscale is true, then we convert the RGB value * to greyscale no matter what visual we are using. */ if (fbi->fb.var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; switch (fbi->fb.fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information. */ if (regno < 16) { u32 *pal = fbi->fb.pseudo_palette; val = chan_to_field(red, &fbi->fb.var.red); val |= chan_to_field(green, &fbi->fb.var.green); val |= chan_to_field(blue, &fbi->fb.var.blue); pal[regno] = val; ret = 0; } break; case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR: ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); break; } return ret;}/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */static inline int var_to_depth(struct fb_var_screeninfo *var){ return var->red.length + var->green.length + var->blue.length + var->transp.length;}/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */static int pxafb_var_to_bpp(struct fb_var_screeninfo *var){ int bpp = -EINVAL; switch (var->bits_per_pixel) { case 1: bpp = 0; break; case 2: bpp = 1; break; case 4: bpp = 2; break; case 8: bpp = 3; break; case 16: bpp = 4; break; case 24: switch (var_to_depth(var)) { case 18: bpp = 6; break; /* 18-bits/pixel packed */ case 19: bpp = 8; break; /* 19-bits/pixel packed */ case 24: bpp = 9; break; } break; case 32: switch (var_to_depth(var)) { case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ case 25: bpp = 10; break; } break; } return bpp;}/* * pxafb_var_to_lccr3(): * Convert a bits per pixel value to the correct bit pattern for LCCR3 * * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an * implication of the acutal use of transparency bit, which we handle it * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. * * Transparency for palette pixel formats is not supported at the moment. */static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var){ int bpp = pxafb_var_to_bpp(var); uint32_t lccr3; if (bpp < 0) return 0; lccr3 = LCCR3_BPP(bpp); switch (var_to_depth(var)) { case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; case 18: lccr3 |= LCCR3_PDFOR_3; break; case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; break; case 19: case 25: lccr3 |= LCCR3_PDFOR_0; break; } return lccr3;}#define SET_PIXFMT(v, r, g, b, t) \({ \ (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ (v)->transp.length = (t) ? (t) : 0; \ (v)->blue.length = (b); (v)->blue.offset = 0; \ (v)->green.length = (g); (v)->green.offset = (b); \ (v)->red.length = (r); (v)->red.offset = (b) + (g); \})/* set the RGBT bitfields of fb_var_screeninf according to * var->bits_per_pixel and given depth */static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth){ if (depth == 0) depth = var->bits_per_pixel; if (var->bits_per_pixel < 16) { /* indexed pixel formats */ var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 8; } switch (depth) { case 16: var->transp.length ? SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ case 24: var->transp.length ? SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ }}#ifdef CONFIG_CPU_FREQ/* * pxafb_display_dma_period() * Calculate the minimum period (in picoseconds) between two DMA * requests for the LCD controller. If we hit this, it means we're * doing nothing but LCD DMA. */static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var){ /* * Period = pixclock * bits_per_byte * bytes_per_transfer * / memory_bits_per_pixel; */ return var->pixclock * 8 * 16 / var->bits_per_pixel;}#endif/* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up. */static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var){ struct pxafb_mode_info *mode = NULL; struct pxafb_mode_info *modelist = mach->modes; unsigned int best_x = 0xffffffff, best_y = 0xffffffff; unsigned int i; for (i = 0; i < mach->num_modes; i++) { if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres && modelist[i].xres < best_x && modelist[i].yres < best_y && modelist[i].bpp >= var->bits_per_pixel) { best_x = modelist[i].xres; best_y = modelist[i].yres; mode = &modelist[i]; } } return mode;}static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode){ var->xres = mode->xres; var->yres = mode->yres; var->bits_per_pixel = mode->bpp; var->pixclock = mode->pixclock; var->hsync_len = mode->hsync_len; var->left_margin = mode->left_margin; var->right_margin = mode->right_margin; var->vsync_len = mode->vsync_len; var->upper_margin = mode->upper_margin; var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; /* set the initial RGBA bitfields */ pxafb_set_pixfmt(var, mode->depth);}static int pxafb_adjust_timing(struct pxafb_info *fbi, struct fb_var_screeninfo *var){ int line_length; var->xres = max_t(int, var->xres, MIN_XRES); var->yres = max_t(int, var->yres, MIN_YRES); if (!(fbi->lccr0 & LCCR0_LCDT)) { clamp_val(var->hsync_len, 1, 64); clamp_val(var->vsync_len, 1, 64); clamp_val(var->left_margin, 1, 255); clamp_val(var->right_margin, 1, 255); clamp_val(var->upper_margin, 1, 255); clamp_val(var->lower_margin, 1, 255); } /* make sure each line is aligned on word boundary */ line_length = var->xres * var->bits_per_pixel / 8; line_length = ALIGN(line_length, 4); var->xres = line_length * 8 / var->bits_per_pixel; /* we don't support xpan, force xres_virtual to be equal to xres */ var->xres_virtual = var->xres; if (var->accel_flags & FB_ACCELF_TEXT) var->yres_virtual = fbi->fb.fix.smem_len / line_length; else var->yres_virtual = max(var->yres_virtual, var->yres); /* check for limits */ if (var->xres > MAX_XRES || var->yres > MAX_YRES) return -EINVAL; if (var->yres > var->yres_virtual) return -EINVAL; return 0;}/* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. * * Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. */static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; struct pxafb_mach_info *inf = fbi->dev->platform_data; int err; if (inf->fixed_modes) { struct pxafb_mode_info *mode; mode = pxafb_getmode(inf, var); if (!mode) return -EINVAL; pxafb_setmode(var, mode); } /* do a test conversion to BPP fields to check the color formats */ err = pxafb_var_to_bpp(var); if (err < 0) return err; pxafb_set_pixfmt(var, var_to_depth(var)); err = pxafb_adjust_timing(fbi, var); if (err) return err;#ifdef CONFIG_CPU_FREQ pr_debug("pxafb: dma period = %d ps\n", pxafb_display_dma_period(var));#endif return 0;}/* * pxafb_set_par(): * Set the user defined part of the display for the specified console */static int pxafb_set_par(struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; struct fb_var_screeninfo *var = &info->var; if (var->bits_per_pixel >= 16) fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; else if (!fbi->cmap_static) fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else { /* * Some people have weird ideas about wanting static * pseudocolor maps. I suspect their user space * applications are broken. */ fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; } fbi->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; if (var->bits_per_pixel >= 16) fbi->palette_size = 0; else fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; if (fbi->fb.var.bits_per_pixel >= 16) fb_dealloc_cmap(&fbi->fb.cmap); else fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); pxafb_activate_var(var, fbi); return 0;}static int pxafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; int dma = DMA_MAX + DMA_BASE; if (fbi->state != C_ENABLE) return 0; setup_base_frame(fbi, 1); if (fbi->lccr0 & LCCR0_SDS) lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1); return 0;}/* * pxafb_blank(): * Blank the display by setting all palette values to zero. Note, the * 16 bpp mode does not really use the palette, so this will not * blank the display in all modes. */static int pxafb_blank(int blank, struct fb_info *info){ struct pxafb_info *fbi = (struct pxafb_info *)info; int i; switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL: if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -