📄 fsl-diu-fb.c
字号:
/* * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. * * Freescale DIU Frame Buffer device driver * * Authors: Hongjun Chen <hong-jun.chen@freescale.com> * Paul Widmer <paul.widmer@freescale.com> * Srikanth Srinivasan <srikanth.srinivasan@freescale.com> * York Sun <yorksun@freescale.com> * * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/platform_device.h>#include <linux/interrupt.h>#include <linux/clk.h>#include <linux/uaccess.h>#include <linux/vmalloc.h>#include <linux/of_platform.h>#include <sysdev/fsl_soc.h>#include "fsl-diu-fb.h"/* * These parameters give default parameters * for video output 1024x768, * FIXME - change timing to proper amounts * hsync 31.5kHz, vsync 60Hz */static struct fb_videomode __devinitdata fsl_diu_default_mode = { .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 __devinitdata fsl_diu_mode_db[] = { { .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 }, { .name = "1024x768-70", .refresh = 70, .xres = 1024, .yres = 768, .pixclock = 16886, .left_margin = 3, .right_margin = 3, .upper_margin = 2, .lower_margin = 2, .hsync_len = 40, .vsync_len = 18, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, { .name = "1024x768-75", .refresh = 75, .xres = 1024, .yres = 768, .pixclock = 15009, .left_margin = 3, .right_margin = 3, .upper_margin = 2, .lower_margin = 2, .hsync_len = 80, .vsync_len = 32, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, { .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 }, { .name = "1280x1024-70", .refresh = 70, .xres = 1280, .yres = 1024, .pixclock = 9380, .left_margin = 6, .right_margin = 6, .upper_margin = 4, .lower_margin = 4, .hsync_len = 60, .vsync_len = 94, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, { .name = "1280x1024-75", .refresh = 75, .xres = 1280, .yres = 1024, .pixclock = 9380, .left_margin = 6, .right_margin = 6, .upper_margin = 4, .lower_margin = 4, .hsync_len = 60, .vsync_len = 15, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, { .name = "320x240", /* for AOI only */ .refresh = 60, .xres = 320, .yres = 240, .pixclock = 15385, .left_margin = 0, .right_margin = 0, .upper_margin = 0, .lower_margin = 0, .hsync_len = 0, .vsync_len = 0, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }, { .name = "1280x480-60", .refresh = 60, .xres = 1280, .yres = 480, .pixclock = 18939, .left_margin = 353, .right_margin = 47, .upper_margin = 39, .lower_margin = 4, .hsync_len = 8, .vsync_len = 2, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED },};static char *fb_mode = "1024x768-32@60";static unsigned long default_bpp = 32;static int monitor_port;#if defined(CONFIG_NOT_COHERENT_CACHE)static u8 *coherence_data;static size_t coherence_data_size;static unsigned int d_cache_line_size;#endifstatic DEFINE_SPINLOCK(diu_lock);struct fsl_diu_data { struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1]; /*FSL_AOI_NUM has one dummy AOI */ struct device_attribute dev_attr; struct diu_ad *dummy_ad; void *dummy_aoi_virt; unsigned int irq; int fb_enabled; int monitor_port;};struct mfb_info { int index; int type; char *id; int registered; int blank; unsigned long pseudo_palette[16]; struct diu_ad *ad; int cursor_reset; unsigned char g_alpha; unsigned int count; int x_aoi_d; /* aoi display x offset to physical screen */ int y_aoi_d; /* aoi display y offset to physical screen */ struct fsl_diu_data *parent;};static struct mfb_info mfb_template[] = { { /* AOI 0 for plane 0 */ .index = 0, .type = MFB_TYPE_OUTPUT, .id = "Panel0", .registered = 0, .count = 0, .x_aoi_d = 0, .y_aoi_d = 0, }, { /* AOI 0 for plane 1 */ .index = 1, .type = MFB_TYPE_OUTPUT, .id = "Panel1 AOI0", .registered = 0, .g_alpha = 0xff, .count = 0, .x_aoi_d = 0, .y_aoi_d = 0, }, { /* AOI 1 for plane 1 */ .index = 2, .type = MFB_TYPE_OUTPUT, .id = "Panel1 AOI1", .registered = 0, .g_alpha = 0xff, .count = 0, .x_aoi_d = 0, .y_aoi_d = 480, }, { /* AOI 0 for plane 2 */ .index = 3, .type = MFB_TYPE_OUTPUT, .id = "Panel2 AOI0", .registered = 0, .g_alpha = 0xff, .count = 0, .x_aoi_d = 640, .y_aoi_d = 0, }, { /* AOI 1 for plane 2 */ .index = 4, .type = MFB_TYPE_OUTPUT, .id = "Panel2 AOI1", .registered = 0, .g_alpha = 0xff, .count = 0, .x_aoi_d = 640, .y_aoi_d = 480, },};static struct diu_hw dr = { .mode = MFB_MODE1, .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock),};static struct diu_pool pool;/** * fsl_diu_alloc - allocate memory for the DIU * @size: number of bytes to allocate * @param: returned physical address of memory * * This function allocates a physically-contiguous block of memory. */static void *fsl_diu_alloc(size_t size, phys_addr_t *phys){ void *virt; pr_debug("size=%zu\n", size); virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO); if (virt) { *phys = virt_to_phys(virt); pr_debug("virt=%p phys=%llx\n", virt, (unsigned long long)*phys); } return virt;}/** * fsl_diu_free - release DIU memory * @virt: pointer returned by fsl_diu_alloc() * @size: number of bytes allocated by fsl_diu_alloc() * * This function releases memory allocated by fsl_diu_alloc(). */static void fsl_diu_free(void *virt, size_t size){ pr_debug("virt=%p size=%zu\n", virt, size); if (virt && size) free_pages_exact(virt, size);}static int fsl_diu_enable_panel(struct fb_info *info){ struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; struct diu *hw = dr.diu_reg; struct diu_ad *ad = mfbi->ad; struct fsl_diu_data *machine_data = mfbi->parent; int res = 0; pr_debug("enable_panel index %d\n", mfbi->index); if (mfbi->type != MFB_TYPE_OFF) { switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != ad->paddr) out_be32(&hw->desc[0], ad->paddr); break; case 1: /* plane 1 AOI 0 */ cmfbi = machine_data->fsl_diu_info[2]->par; if (hw->desc[1] != ad->paddr) { /* AOI0 closed */ if (cmfbi->count > 0) /* AOI1 open */ ad->next_ad = cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; out_be32(&hw->desc[1], ad->paddr); } break; case 3: /* plane 2 AOI 0 */ cmfbi = machine_data->fsl_diu_info[4]->par; if (hw->desc[2] != ad->paddr) { /* AOI0 closed */ if (cmfbi->count > 0) /* AOI1 open */ ad->next_ad = cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; out_be32(&hw->desc[2], ad->paddr); } break; case 2: /* plane 1 AOI 1 */ pmfbi = machine_data->fsl_diu_info[1]->par; ad->next_ad = 0; if (hw->desc[1] == machine_data->dummy_ad->paddr) out_be32(&hw->desc[1], ad->paddr); else /* AOI0 open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; case 4: /* plane 2 AOI 1 */ pmfbi = machine_data->fsl_diu_info[3]->par; ad->next_ad = 0; if (hw->desc[2] == machine_data->dummy_ad->paddr) out_be32(&hw->desc[2], ad->paddr); else /* AOI0 was open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; default: res = -EINVAL; break; } } else res = -EINVAL; return res;}static int fsl_diu_disable_panel(struct fb_info *info){ struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; struct diu *hw = dr.diu_reg; struct diu_ad *ad = mfbi->ad; struct fsl_diu_data *machine_data = mfbi->parent; int res = 0; switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != machine_data->dummy_ad->paddr) out_be32(&hw->desc[0], machine_data->dummy_ad->paddr); break; case 1: /* plane 1 AOI 0 */ cmfbi = machine_data->fsl_diu_info[2]->par; if (cmfbi->count > 0) /* AOI1 is open */ out_be32(&hw->desc[1], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ out_be32(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 3: /* plane 2 AOI 0 */ cmfbi = machine_data->fsl_diu_info[4]->par; if (cmfbi->count > 0) /* AOI1 is open */ out_be32(&hw->desc[2], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ out_be32(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 2: /* plane 1 AOI 1 */ pmfbi = machine_data->fsl_diu_info[1]->par; if (hw->desc[1] != ad->paddr) { /* AOI1 is not the first in the chain */ if (pmfbi->count > 0) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ out_be32(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; case 4: /* plane 2 AOI 1 */ pmfbi = machine_data->fsl_diu_info[3]->par; if (hw->desc[2] != ad->paddr) { /* AOI1 is not the first in the chain */ if (pmfbi->count > 0) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ out_be32(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; default: res = -EINVAL; break; } return res;}static void enable_lcdc(struct fb_info *info){ struct diu *hw = dr.diu_reg; struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; if (!machine_data->fb_enabled) { out_be32(&hw->diu_mode, dr.mode); machine_data->fb_enabled++; }}static void disable_lcdc(struct fb_info *info){ struct diu *hw = dr.diu_reg; struct mfb_info *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; if (machine_data->fb_enabled) { out_be32(&hw->diu_mode, 0); machine_data->fb_enabled = 0; }}static void adjust_aoi_size_position(struct fb_var_screeninfo *var, struct fb_info *info){ struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par; struct fsl_diu_data *machine_data = mfbi->parent; int available_height, upper_aoi_bottom, index = mfbi->index; int lower_aoi_is_open, upper_aoi_is_open; __u32 base_plane_width, base_plane_height, upper_aoi_height; base_plane_width = machine_data->fsl_diu_info[0]->var.xres; base_plane_height = machine_data->fsl_diu_info[0]->var.yres; if (mfbi->x_aoi_d < 0) mfbi->x_aoi_d = 0; if (mfbi->y_aoi_d < 0) mfbi->y_aoi_d = 0; switch (index) { case 0: if (mfbi->x_aoi_d != 0) mfbi->x_aoi_d = 0; if (mfbi->y_aoi_d != 0) mfbi->y_aoi_d = 0; break; case 1: /* AOI 0 */ case 3: lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par; lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0; if (var->xres > base_plane_width) var->xres = base_plane_width; if ((mfbi->x_aoi_d + var->xres) > base_plane_width) mfbi->x_aoi_d = base_plane_width - var->xres; if (lower_aoi_is_open) available_height = lower_aoi_mfbi->y_aoi_d; else available_height = base_plane_height; if (var->yres > available_height) var->yres = available_height; if ((mfbi->y_aoi_d + var->yres) > available_height) mfbi->y_aoi_d = available_height - var->yres; break; case 2: /* AOI 1 */ case 4: upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par; upper_aoi_height = machine_data->fsl_diu_info[index-1]->var.yres; upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height; upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0; if (var->xres > base_plane_width) var->xres = base_plane_width; if ((mfbi->x_aoi_d + var->xres) > base_plane_width) mfbi->x_aoi_d = base_plane_width - var->xres; if (mfbi->y_aoi_d < 0) mfbi->y_aoi_d = 0; if (upper_aoi_is_open) { if (mfbi->y_aoi_d < upper_aoi_bottom) mfbi->y_aoi_d = upper_aoi_bottom; available_height = base_plane_height - upper_aoi_bottom; } else available_height = base_plane_height; if (var->yres > available_height) var->yres = available_height; if ((mfbi->y_aoi_d + var->yres) > base_plane_height) mfbi->y_aoi_d = base_plane_height - var->yres; break; }}/* * Checks to see if the hardware supports the state requested by var passed * in. This function does not alter the hardware state! If the var passed in * is slightly off by what the hardware can support then we alter the var * PASSED in to what we can do. If the hardware doesn't support mode change * a -EINVAL will be returned by the upper layers. */static int fsl_diu_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ unsigned long htotal, vtotal; pr_debug("check_var xres: %d\n", var->xres); pr_debug("check_var yres: %d\n", var->yres); if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) var->yoffset = 0; if (var->xoffset + info->var.xres > info->var.xres_virtual) var->xoffset = info->var.xres_virtual - info->var.xres; if (var->yoffset + info->var.yres > info->var.yres_virtual) var->yoffset = info->var.yres_virtual - info->var.yres; if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && (var->bits_per_pixel != 16)) var->bits_per_pixel = default_bpp; switch (var->bits_per_pixel) { case 16: var->red.length = 5; var->red.offset = 11; var->red.msb_right = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -