162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Frame buffer driver for the Carmine GPU. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The driver configures the GPU as follows 662306a36Sopenharmony_ci * - FB0 is display 0 with unique memory area 762306a36Sopenharmony_ci * - FB1 is display 1 with unique memory area 862306a36Sopenharmony_ci * - both display use 32 bit colors 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/aperture.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/fb.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "carminefb.h" 2062306a36Sopenharmony_ci#include "carminefb_regs.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) 2362306a36Sopenharmony_ci#error "The endianness of the target host has not been defined." 2462306a36Sopenharmony_ci#endif 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * The initial video mode can be supplied via two different ways: 2862306a36Sopenharmony_ci * - as a string that is passed to fb_find_mode() (module option fb_mode_str) 2962306a36Sopenharmony_ci * - as an integer that picks the video mode from carmine_modedb[] (module 3062306a36Sopenharmony_ci * option fb_mode) 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * If nothing is used than the initial video mode will be the 3362306a36Sopenharmony_ci * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[]. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#define CARMINEFB_DEFAULT_VIDEO_MODE 1 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; 3862306a36Sopenharmony_cimodule_param(fb_mode, uint, 0444); 3962306a36Sopenharmony_ciMODULE_PARM_DESC(fb_mode, "Initial video mode as integer."); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic char *fb_mode_str; 4262306a36Sopenharmony_cimodule_param(fb_mode_str, charp, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Carminefb displays: 4762306a36Sopenharmony_ci * 0b000 None 4862306a36Sopenharmony_ci * 0b001 Display 0 4962306a36Sopenharmony_ci * 0b010 Display 1 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1; 5262306a36Sopenharmony_cimodule_param(fb_displays, int, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used"); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct carmine_hw { 5662306a36Sopenharmony_ci void __iomem *v_regs; 5762306a36Sopenharmony_ci void __iomem *screen_mem; 5862306a36Sopenharmony_ci struct fb_info *fb[MAX_DISPLAY]; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct carmine_resolution { 6262306a36Sopenharmony_ci u32 htp; 6362306a36Sopenharmony_ci u32 hsp; 6462306a36Sopenharmony_ci u32 hsw; 6562306a36Sopenharmony_ci u32 hdp; 6662306a36Sopenharmony_ci u32 vtr; 6762306a36Sopenharmony_ci u32 vsp; 6862306a36Sopenharmony_ci u32 vsw; 6962306a36Sopenharmony_ci u32 vdp; 7062306a36Sopenharmony_ci u32 disp_mode; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct carmine_fb { 7462306a36Sopenharmony_ci void __iomem *display_reg; 7562306a36Sopenharmony_ci void __iomem *screen_base; 7662306a36Sopenharmony_ci u32 smem_offset; 7762306a36Sopenharmony_ci u32 cur_mode; 7862306a36Sopenharmony_ci u32 new_mode; 7962306a36Sopenharmony_ci struct carmine_resolution *res; 8062306a36Sopenharmony_ci u32 pseudo_palette[16]; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct fb_fix_screeninfo carminefb_fix = { 8462306a36Sopenharmony_ci .id = "Carmine", 8562306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 8662306a36Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 8762306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const struct fb_videomode carmine_modedb[] = { 9162306a36Sopenharmony_ci { 9262306a36Sopenharmony_ci .name = "640x480", 9362306a36Sopenharmony_ci .xres = 640, 9462306a36Sopenharmony_ci .yres = 480, 9562306a36Sopenharmony_ci }, { 9662306a36Sopenharmony_ci .name = "800x600", 9762306a36Sopenharmony_ci .xres = 800, 9862306a36Sopenharmony_ci .yres = 600, 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct carmine_resolution car_modes[] = { 10362306a36Sopenharmony_ci { 10462306a36Sopenharmony_ci /* 640x480 */ 10562306a36Sopenharmony_ci .htp = 800, 10662306a36Sopenharmony_ci .hsp = 672, 10762306a36Sopenharmony_ci .hsw = 96, 10862306a36Sopenharmony_ci .hdp = 640, 10962306a36Sopenharmony_ci .vtr = 525, 11062306a36Sopenharmony_ci .vsp = 490, 11162306a36Sopenharmony_ci .vsw = 2, 11262306a36Sopenharmony_ci .vdp = 480, 11362306a36Sopenharmony_ci .disp_mode = 0x1400, 11462306a36Sopenharmony_ci }, 11562306a36Sopenharmony_ci { 11662306a36Sopenharmony_ci /* 800x600 */ 11762306a36Sopenharmony_ci .htp = 1060, 11862306a36Sopenharmony_ci .hsp = 864, 11962306a36Sopenharmony_ci .hsw = 72, 12062306a36Sopenharmony_ci .hdp = 800, 12162306a36Sopenharmony_ci .vtr = 628, 12262306a36Sopenharmony_ci .vsp = 601, 12362306a36Sopenharmony_ci .vsw = 2, 12462306a36Sopenharmony_ci .vdp = 600, 12562306a36Sopenharmony_ci .disp_mode = 0x0d00, 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int carmine_find_mode(const struct fb_var_screeninfo *var) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(car_modes); i++) 13462306a36Sopenharmony_ci if (car_modes[i].hdp == var->xres && 13562306a36Sopenharmony_ci car_modes[i].vdp == var->yres) 13662306a36Sopenharmony_ci return i; 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void c_set_disp_reg(const struct carmine_fb *par, 14162306a36Sopenharmony_ci u32 offset, u32 val) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci writel(val, par->display_reg + offset); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic u32 c_get_disp_reg(const struct carmine_fb *par, 14762306a36Sopenharmony_ci u32 offset) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return readl(par->display_reg + offset); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void c_set_hw_reg(const struct carmine_hw *hw, 15362306a36Sopenharmony_ci u32 offset, u32 val) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci writel(val, hw->v_regs + offset); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic u32 c_get_hw_reg(const struct carmine_hw *hw, 15962306a36Sopenharmony_ci u32 offset) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci return readl(hw->v_regs + offset); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int carmine_setcolreg(unsigned regno, unsigned red, unsigned green, 16562306a36Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (regno >= 16) 16862306a36Sopenharmony_ci return 1; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci red >>= 8; 17162306a36Sopenharmony_ci green >>= 8; 17262306a36Sopenharmony_ci blue >>= 8; 17362306a36Sopenharmony_ci transp >>= 8; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 | 17662306a36Sopenharmony_ci red << 0 | green << 8 | blue << 16); 17762306a36Sopenharmony_ci return 0; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int carmine_check_var(struct fb_var_screeninfo *var, 18162306a36Sopenharmony_ci struct fb_info *info) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = carmine_find_mode(var); 18662306a36Sopenharmony_ci if (ret < 0) 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (var->grayscale || var->rotate || var->nonstd) 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci var->xres_virtual = var->xres; 19362306a36Sopenharmony_ci var->yres_virtual = var->yres; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci var->bits_per_pixel = 32; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 19862306a36Sopenharmony_ci var->transp.offset = 24; 19962306a36Sopenharmony_ci var->red.offset = 0; 20062306a36Sopenharmony_ci var->green.offset = 8; 20162306a36Sopenharmony_ci var->blue.offset = 16; 20262306a36Sopenharmony_ci#else 20362306a36Sopenharmony_ci var->transp.offset = 24; 20462306a36Sopenharmony_ci var->red.offset = 16; 20562306a36Sopenharmony_ci var->green.offset = 8; 20662306a36Sopenharmony_ci var->blue.offset = 0; 20762306a36Sopenharmony_ci#endif 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci var->red.length = 8; 21062306a36Sopenharmony_ci var->green.length = 8; 21162306a36Sopenharmony_ci var->blue.length = 8; 21262306a36Sopenharmony_ci var->transp.length = 8; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci var->red.msb_right = 0; 21562306a36Sopenharmony_ci var->green.msb_right = 0; 21662306a36Sopenharmony_ci var->blue.msb_right = 0; 21762306a36Sopenharmony_ci var->transp.msb_right = 0; 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void carmine_init_display_param(struct carmine_fb *par) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci u32 width; 22462306a36Sopenharmony_ci u32 height; 22562306a36Sopenharmony_ci u32 param; 22662306a36Sopenharmony_ci u32 window_size; 22762306a36Sopenharmony_ci u32 soffset = par->smem_offset; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0); 23062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0); 23162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE, 23262306a36Sopenharmony_ci CARMINE_CURSOR0_PRIORITY_MASK | 23362306a36Sopenharmony_ci CARMINE_CURSOR1_PRIORITY_MASK | 23462306a36Sopenharmony_ci CARMINE_CURSOR_CUTZ_MASK); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Set default cursor position */ 23762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0); 23862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Set default display mode */ 24162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE | 24262306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 24362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE, 24462306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 24562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE | 24662306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 24762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE | 24862306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 24962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE | 25062306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 25162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE | 25262306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 25362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE | 25462306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 25562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE | 25662306a36Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Set default frame size to layer mode register */ 25962306a36Sopenharmony_ci width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT; 26062306a36Sopenharmony_ci width = width << CARMINE_DISP_WIDTH_SHIFT; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci height = par->res->vdp - 1; 26362306a36Sopenharmony_ci param = width | height; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param); 26662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width); 26762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param); 26862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param); 26962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param); 27062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param); 27162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param); 27262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Set default pos and size */ 27562306a36Sopenharmony_ci window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT; 27662306a36Sopenharmony_ci window_size |= par->res->hdp; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0); 27962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size); 28062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0); 28162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size); 28262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0); 28362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size); 28462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0); 28562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size); 28662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0); 28762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size); 28862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0); 28962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size); 29062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0); 29162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size); 29262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0); 29362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Set default origin address */ 29662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset); 29762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset); 29862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset); 29962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset); 30062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset); 30162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset); 30262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset); 30362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Set default display address */ 30662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset); 30762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset); 30862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset); 30962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset); 31062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset); 31162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset); 31262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Set default display position */ 31562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0); 31662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0); 31762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0); 31862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0); 31962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0); 32062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0); 32162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Set default blend mode */ 32462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0); 32562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0); 32662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0); 32762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0); 32862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0); 32962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0); 33062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0); 33162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* default transparency mode */ 33462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0); 33562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0); 33662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0); 33762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0); 33862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0); 33962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0); 34062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0); 34162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Set default read skip parameter */ 34462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0); 34562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0); 34662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0); 34762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0); 34862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0); 34962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0); 35062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0); 35362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0); 35462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0); 35562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0); 35662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0); 35762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0); 35862306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0); 36162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0); 36262306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0); 36362306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0); 36462306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0); 36562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0); 36662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void set_display_parameters(struct carmine_fb *par) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci u32 mode; 37262306a36Sopenharmony_ci u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * display timing. Parameters are decreased by one because hardware 37662306a36Sopenharmony_ci * spec is 0 to (n - 1) 37762306a36Sopenharmony_ci * */ 37862306a36Sopenharmony_ci hdp = par->res->hdp - 1; 37962306a36Sopenharmony_ci vdp = par->res->vdp - 1; 38062306a36Sopenharmony_ci htp = par->res->htp - 1; 38162306a36Sopenharmony_ci hsp = par->res->hsp - 1; 38262306a36Sopenharmony_ci hsw = par->res->hsw - 1; 38362306a36Sopenharmony_ci vtr = par->res->vtr - 1; 38462306a36Sopenharmony_ci vsp = par->res->vsp - 1; 38562306a36Sopenharmony_ci vsw = par->res->vsw - 1; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL, 38862306a36Sopenharmony_ci htp << CARMINE_DISP_HTP_SHIFT); 38962306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD, 39062306a36Sopenharmony_ci (hdp << CARMINE_DISP_HDB_SHIFT) | hdp); 39162306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS, 39262306a36Sopenharmony_ci (vsw << CARMINE_DISP_VSW_SHIFT) | 39362306a36Sopenharmony_ci (hsw << CARMINE_DISP_HSW_SHIFT) | 39462306a36Sopenharmony_ci (hsp)); 39562306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL, 39662306a36Sopenharmony_ci vtr << CARMINE_DISP_VTR_SHIFT); 39762306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS, 39862306a36Sopenharmony_ci (vdp << CARMINE_DISP_VDP_SHIFT) | vsp); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* clock */ 40162306a36Sopenharmony_ci mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1); 40262306a36Sopenharmony_ci mode = (mode & ~CARMINE_DISP_DCM_MASK) | 40362306a36Sopenharmony_ci (par->res->disp_mode & CARMINE_DISP_DCM_MASK); 40462306a36Sopenharmony_ci /* enable video output and layer 0 */ 40562306a36Sopenharmony_ci mode |= CARMINE_DEN | CARMINE_L0E; 40662306a36Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int carmine_set_par(struct fb_info *info) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct carmine_fb *par = info->par; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = carmine_find_mode(&info->var); 41562306a36Sopenharmony_ci if (ret < 0) 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci par->new_mode = ret; 41962306a36Sopenharmony_ci if (par->cur_mode != par->new_mode) { 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci par->cur_mode = par->new_mode; 42262306a36Sopenharmony_ci par->res = &car_modes[par->new_mode]; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci carmine_init_display_param(par); 42562306a36Sopenharmony_ci set_display_parameters(par); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int init_hardware(struct carmine_hw *hw) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci u32 flags; 43562306a36Sopenharmony_ci u32 loops; 43662306a36Sopenharmony_ci u32 ret; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Initialize Carmine */ 43962306a36Sopenharmony_ci /* Sets internal clock */ 44062306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 44162306a36Sopenharmony_ci CARMINE_DFLT_IP_CLOCK_ENABLE); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Video signal output is turned off */ 44462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); 44562306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Software reset */ 44862306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1); 44962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* I/O mode settings */ 45262306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 | 45362306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_IO_CONT0; 45462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0, 45562306a36Sopenharmony_ci flags); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* DRAM initial sequence */ 45862306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD; 45962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, 46062306a36Sopenharmony_ci flags); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 | 46362306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_EMODE; 46462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE, 46562306a36Sopenharmony_ci flags); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 | 46862306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_SET_TIME2; 46962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2, 47062306a36Sopenharmony_ci flags); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 | 47362306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_FIFO_DEPTH; 47462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1; 47762306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1, 47862306a36Sopenharmony_ci flags); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | 48162306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_STATES; 48262306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, 48362306a36Sopenharmony_ci flags); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Executes DLL reset */ 48662306a36Sopenharmony_ci if (CARMINE_DCTL_DLL_RESET) { 48762306a36Sopenharmony_ci for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) { 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ret = c_get_hw_reg(hw, CARMINE_DCTL_REG + 49062306a36Sopenharmony_ci CARMINE_DCTL_REG_RSV0_STATES); 49162306a36Sopenharmony_ci ret &= CARMINE_DCTL_REG_STATES_MASK; 49262306a36Sopenharmony_ci if (!ret) 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) { 49962306a36Sopenharmony_ci printk(KERN_ERR "DRAM init failed\n"); 50062306a36Sopenharmony_ci return -EIO; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 | 50562306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_ADD; 50662306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | 50962306a36Sopenharmony_ci CARMINE_DFLT_IP_DCTL_STATES_AFT_RST; 51062306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, 51162306a36Sopenharmony_ci flags); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Initialize the write back register */ 51462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM, 51562306a36Sopenharmony_ci CARMINE_WB_REG_WBM_DEFAULT); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* Initialize the Kottos registers */ 51862306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0); 51962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* Set DC offsets */ 52262306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0); 52362306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0); 52462306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0); 52562306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0); 52662306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0); 52762306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0); 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct fb_ops carminefb_ops = { 53262306a36Sopenharmony_ci .owner = THIS_MODULE, 53362306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 53462306a36Sopenharmony_ci .fb_check_var = carmine_check_var, 53562306a36Sopenharmony_ci .fb_set_par = carmine_set_par, 53662306a36Sopenharmony_ci .fb_setcolreg = carmine_setcolreg, 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, 54062306a36Sopenharmony_ci int smem_offset, struct device *device, 54162306a36Sopenharmony_ci struct fb_info **rinfo) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci int ret; 54462306a36Sopenharmony_ci struct fb_info *info; 54562306a36Sopenharmony_ci struct carmine_fb *par; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci info = framebuffer_alloc(sizeof *par, device); 54862306a36Sopenharmony_ci if (!info) 54962306a36Sopenharmony_ci return -ENOMEM; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci par = info->par; 55262306a36Sopenharmony_ci par->display_reg = regs; 55362306a36Sopenharmony_ci par->smem_offset = smem_offset; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci info->screen_base = smem_base + smem_offset; 55662306a36Sopenharmony_ci info->screen_size = CARMINE_DISPLAY_MEM; 55762306a36Sopenharmony_ci info->fbops = &carminefb_ops; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci info->fix = carminefb_fix; 56062306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 1); 56362306a36Sopenharmony_ci if (ret < 0) 56462306a36Sopenharmony_ci goto err_free_fb; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (fb_mode >= ARRAY_SIZE(carmine_modedb)) 56762306a36Sopenharmony_ci fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci par->cur_mode = par->new_mode = ~0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb, 57262306a36Sopenharmony_ci ARRAY_SIZE(carmine_modedb), 57362306a36Sopenharmony_ci &carmine_modedb[fb_mode], 32); 57462306a36Sopenharmony_ci if (!ret || ret == 4) { 57562306a36Sopenharmony_ci ret = -EINVAL; 57662306a36Sopenharmony_ci goto err_dealloc_cmap; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb), 58062306a36Sopenharmony_ci &info->modelist); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = register_framebuffer(info); 58362306a36Sopenharmony_ci if (ret < 0) 58462306a36Sopenharmony_ci goto err_dealloc_cmap; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci *rinfo = info; 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cierr_dealloc_cmap: 59262306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 59362306a36Sopenharmony_cierr_free_fb: 59462306a36Sopenharmony_ci framebuffer_release(info); 59562306a36Sopenharmony_ci return ret; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void cleanup_fb_device(struct fb_info *info) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci if (info) { 60162306a36Sopenharmony_ci unregister_framebuffer(info); 60262306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 60362306a36Sopenharmony_ci framebuffer_release(info); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct carmine_hw *hw; 61062306a36Sopenharmony_ci struct device *device = &dev->dev; 61162306a36Sopenharmony_ci struct fb_info *info; 61262306a36Sopenharmony_ci int ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci ret = aperture_remove_conflicting_pci_devices(dev, "carminefb"); 61562306a36Sopenharmony_ci if (ret) 61662306a36Sopenharmony_ci return ret; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ret = pci_enable_device(dev); 61962306a36Sopenharmony_ci if (ret) 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci ret = -ENOMEM; 62362306a36Sopenharmony_ci hw = kzalloc(sizeof *hw, GFP_KERNEL); 62462306a36Sopenharmony_ci if (!hw) 62562306a36Sopenharmony_ci goto err_enable_pci; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR); 62862306a36Sopenharmony_ci carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!request_mem_region(carminefb_fix.mmio_start, 63162306a36Sopenharmony_ci carminefb_fix.mmio_len, 63262306a36Sopenharmony_ci "carminefb regbase")) { 63362306a36Sopenharmony_ci printk(KERN_ERR "carminefb: Can't reserve regbase.\n"); 63462306a36Sopenharmony_ci ret = -EBUSY; 63562306a36Sopenharmony_ci goto err_free_hw; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci hw->v_regs = ioremap(carminefb_fix.mmio_start, 63862306a36Sopenharmony_ci carminefb_fix.mmio_len); 63962306a36Sopenharmony_ci if (!hw->v_regs) { 64062306a36Sopenharmony_ci printk(KERN_ERR "carminefb: Can't remap %s register.\n", 64162306a36Sopenharmony_ci carminefb_fix.id); 64262306a36Sopenharmony_ci goto err_free_reg_mmio; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR); 64662306a36Sopenharmony_ci carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* The memory area tends to be very large (256 MiB). Remap only what 64962306a36Sopenharmony_ci * is required for that largest resolution to avoid remaps at run 65062306a36Sopenharmony_ci * time 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM) 65362306a36Sopenharmony_ci carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) { 65662306a36Sopenharmony_ci printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " 65762306a36Sopenharmony_ci "are required.", carminefb_fix.smem_len, 65862306a36Sopenharmony_ci CARMINE_TOTAL_DIPLAY_MEM); 65962306a36Sopenharmony_ci goto err_unmap_vregs; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!request_mem_region(carminefb_fix.smem_start, 66362306a36Sopenharmony_ci carminefb_fix.smem_len, "carminefb smem")) { 66462306a36Sopenharmony_ci printk(KERN_ERR "carminefb: Can't reserve smem.\n"); 66562306a36Sopenharmony_ci goto err_unmap_vregs; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci hw->screen_mem = ioremap(carminefb_fix.smem_start, 66962306a36Sopenharmony_ci carminefb_fix.smem_len); 67062306a36Sopenharmony_ci if (!hw->screen_mem) { 67162306a36Sopenharmony_ci printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); 67262306a36Sopenharmony_ci goto err_reg_smem; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ret = init_hardware(hw); 67662306a36Sopenharmony_ci if (ret) 67762306a36Sopenharmony_ci goto err_unmap_screen; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci info = NULL; 68062306a36Sopenharmony_ci if (fb_displays & CARMINE_USE_DISPLAY0) { 68162306a36Sopenharmony_ci ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG, 68262306a36Sopenharmony_ci hw->screen_mem, CARMINE_DISPLAY_MEM * 0, 68362306a36Sopenharmony_ci device, &info); 68462306a36Sopenharmony_ci if (ret) 68562306a36Sopenharmony_ci goto err_deinit_hw; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci hw->fb[0] = info; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci info = NULL; 69162306a36Sopenharmony_ci if (fb_displays & CARMINE_USE_DISPLAY1) { 69262306a36Sopenharmony_ci ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG, 69362306a36Sopenharmony_ci hw->screen_mem, CARMINE_DISPLAY_MEM * 1, 69462306a36Sopenharmony_ci device, &info); 69562306a36Sopenharmony_ci if (ret) 69662306a36Sopenharmony_ci goto err_cleanup_fb0; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci hw->fb[1] = info; 70062306a36Sopenharmony_ci info = NULL; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci pci_set_drvdata(dev, hw); 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cierr_cleanup_fb0: 70662306a36Sopenharmony_ci cleanup_fb_device(hw->fb[0]); 70762306a36Sopenharmony_cierr_deinit_hw: 70862306a36Sopenharmony_ci /* disable clock, etc */ 70962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); 71062306a36Sopenharmony_cierr_unmap_screen: 71162306a36Sopenharmony_ci iounmap(hw->screen_mem); 71262306a36Sopenharmony_cierr_reg_smem: 71362306a36Sopenharmony_ci release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len); 71462306a36Sopenharmony_cierr_unmap_vregs: 71562306a36Sopenharmony_ci iounmap(hw->v_regs); 71662306a36Sopenharmony_cierr_free_reg_mmio: 71762306a36Sopenharmony_ci release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); 71862306a36Sopenharmony_cierr_free_hw: 71962306a36Sopenharmony_ci kfree(hw); 72062306a36Sopenharmony_cierr_enable_pci: 72162306a36Sopenharmony_ci pci_disable_device(dev); 72262306a36Sopenharmony_ci return ret; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void carminefb_remove(struct pci_dev *dev) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct carmine_hw *hw = pci_get_drvdata(dev); 72862306a36Sopenharmony_ci struct fb_fix_screeninfo fix; 72962306a36Sopenharmony_ci int i; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* in case we use only fb1 and not fb1 */ 73262306a36Sopenharmony_ci if (hw->fb[0]) 73362306a36Sopenharmony_ci fix = hw->fb[0]->fix; 73462306a36Sopenharmony_ci else 73562306a36Sopenharmony_ci fix = hw->fb[1]->fix; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* deactivate display(s) and switch clocks */ 73862306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); 73962306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); 74062306a36Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci for (i = 0; i < MAX_DISPLAY; i++) 74362306a36Sopenharmony_ci cleanup_fb_device(hw->fb[i]); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci iounmap(hw->screen_mem); 74662306a36Sopenharmony_ci release_mem_region(fix.smem_start, fix.smem_len); 74762306a36Sopenharmony_ci iounmap(hw->v_regs); 74862306a36Sopenharmony_ci release_mem_region(fix.mmio_start, fix.mmio_len); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci pci_disable_device(dev); 75162306a36Sopenharmony_ci kfree(hw); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf 75562306a36Sopenharmony_cistatic struct pci_device_id carmine_devices[] = { 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)}, 75862306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 75962306a36Sopenharmony_ci}; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, carmine_devices); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic struct pci_driver carmine_pci_driver = { 76462306a36Sopenharmony_ci .name = "carminefb", 76562306a36Sopenharmony_ci .id_table = carmine_devices, 76662306a36Sopenharmony_ci .probe = carminefb_probe, 76762306a36Sopenharmony_ci .remove = carminefb_remove, 76862306a36Sopenharmony_ci}; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int __init carminefb_init(void) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci if (fb_modesetting_disabled("carminefb")) 77362306a36Sopenharmony_ci return -ENODEV; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (!(fb_displays & 77662306a36Sopenharmony_ci (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) { 77762306a36Sopenharmony_ci printk(KERN_ERR "If you disable both displays than you don't " 77862306a36Sopenharmony_ci "need the driver at all\n"); 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci return pci_register_driver(&carmine_pci_driver); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_cimodule_init(carminefb_init); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void __exit carminefb_cleanup(void) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci pci_unregister_driver(&carmine_pci_driver); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_cimodule_exit(carminefb_cleanup); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciMODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>"); 79262306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices"); 79362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 794