794 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Frame buffer driver for the Carmine GPU.4 *5 * The driver configures the GPU as follows6 * - FB0 is display 0 with unique memory area7 * - FB1 is display 1 with unique memory area8 * - both display use 32 bit colors9 */10#include <linux/aperture.h>11#include <linux/delay.h>12#include <linux/errno.h>13#include <linux/fb.h>14#include <linux/interrupt.h>15#include <linux/pci.h>16#include <linux/slab.h>17#include <linux/module.h>18 19#include "carminefb.h"20#include "carminefb_regs.h"21 22#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)23#error "The endianness of the target host has not been defined."24#endif25 26/*27 * The initial video mode can be supplied via two different ways:28 * - as a string that is passed to fb_find_mode() (module option fb_mode_str)29 * - as an integer that picks the video mode from carmine_modedb[] (module30 * option fb_mode)31 *32 * If nothing is used than the initial video mode will be the33 * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[].34 */35#define CARMINEFB_DEFAULT_VIDEO_MODE 136 37static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;38module_param(fb_mode, uint, 0444);39MODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");40 41static char *fb_mode_str;42module_param(fb_mode_str, charp, 0444);43MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");44 45/*46 * Carminefb displays:47 * 0b000 None48 * 0b001 Display 049 * 0b010 Display 150 */51static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;52module_param(fb_displays, int, 0444);53MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");54 55struct carmine_hw {56 void __iomem *v_regs;57 void __iomem *screen_mem;58 struct fb_info *fb[MAX_DISPLAY];59};60 61struct carmine_resolution {62 u32 htp;63 u32 hsp;64 u32 hsw;65 u32 hdp;66 u32 vtr;67 u32 vsp;68 u32 vsw;69 u32 vdp;70 u32 disp_mode;71};72 73struct carmine_fb {74 void __iomem *display_reg;75 void __iomem *screen_base;76 u32 smem_offset;77 u32 cur_mode;78 u32 new_mode;79 struct carmine_resolution *res;80 u32 pseudo_palette[16];81};82 83static struct fb_fix_screeninfo carminefb_fix = {84 .id = "Carmine",85 .type = FB_TYPE_PACKED_PIXELS,86 .visual = FB_VISUAL_TRUECOLOR,87 .accel = FB_ACCEL_NONE,88};89 90static const struct fb_videomode carmine_modedb[] = {91 {92 .name = "640x480",93 .xres = 640,94 .yres = 480,95 }, {96 .name = "800x600",97 .xres = 800,98 .yres = 600,99 },100};101 102static struct carmine_resolution car_modes[] = {103 {104 /* 640x480 */105 .htp = 800,106 .hsp = 672,107 .hsw = 96,108 .hdp = 640,109 .vtr = 525,110 .vsp = 490,111 .vsw = 2,112 .vdp = 480,113 .disp_mode = 0x1400,114 },115 {116 /* 800x600 */117 .htp = 1060,118 .hsp = 864,119 .hsw = 72,120 .hdp = 800,121 .vtr = 628,122 .vsp = 601,123 .vsw = 2,124 .vdp = 600,125 .disp_mode = 0x0d00,126 }127};128 129static int carmine_find_mode(const struct fb_var_screeninfo *var)130{131 int i;132 133 for (i = 0; i < ARRAY_SIZE(car_modes); i++)134 if (car_modes[i].hdp == var->xres &&135 car_modes[i].vdp == var->yres)136 return i;137 return -EINVAL;138}139 140static void c_set_disp_reg(const struct carmine_fb *par,141 u32 offset, u32 val)142{143 writel(val, par->display_reg + offset);144}145 146static u32 c_get_disp_reg(const struct carmine_fb *par,147 u32 offset)148{149 return readl(par->display_reg + offset);150}151 152static void c_set_hw_reg(const struct carmine_hw *hw,153 u32 offset, u32 val)154{155 writel(val, hw->v_regs + offset);156}157 158static u32 c_get_hw_reg(const struct carmine_hw *hw,159 u32 offset)160{161 return readl(hw->v_regs + offset);162}163 164static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green,165 unsigned blue, unsigned transp, struct fb_info *info)166{167 if (regno >= 16)168 return 1;169 170 red >>= 8;171 green >>= 8;172 blue >>= 8;173 transp >>= 8;174 175 ((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 |176 red << 0 | green << 8 | blue << 16);177 return 0;178}179 180static int carmine_check_var(struct fb_var_screeninfo *var,181 struct fb_info *info)182{183 int ret;184 185 ret = carmine_find_mode(var);186 if (ret < 0)187 return ret;188 189 if (var->grayscale || var->rotate || var->nonstd)190 return -EINVAL;191 192 var->xres_virtual = var->xres;193 var->yres_virtual = var->yres;194 195 var->bits_per_pixel = 32;196 197#ifdef __BIG_ENDIAN198 var->transp.offset = 24;199 var->red.offset = 0;200 var->green.offset = 8;201 var->blue.offset = 16;202#else203 var->transp.offset = 24;204 var->red.offset = 16;205 var->green.offset = 8;206 var->blue.offset = 0;207#endif208 209 var->red.length = 8;210 var->green.length = 8;211 var->blue.length = 8;212 var->transp.length = 8;213 214 var->red.msb_right = 0;215 var->green.msb_right = 0;216 var->blue.msb_right = 0;217 var->transp.msb_right = 0;218 return 0;219}220 221static void carmine_init_display_param(struct carmine_fb *par)222{223 u32 width;224 u32 height;225 u32 param;226 u32 window_size;227 u32 soffset = par->smem_offset;228 229 c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0);230 c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0);231 c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE,232 CARMINE_CURSOR0_PRIORITY_MASK |233 CARMINE_CURSOR1_PRIORITY_MASK |234 CARMINE_CURSOR_CUTZ_MASK);235 236 /* Set default cursor position */237 c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0);238 c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0);239 240 /* Set default display mode */241 c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE |242 CARMINE_EXT_CMODE_DIRECT24_RGBA);243 c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE,244 CARMINE_EXT_CMODE_DIRECT24_RGBA);245 c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE |246 CARMINE_EXT_CMODE_DIRECT24_RGBA);247 c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE |248 CARMINE_EXT_CMODE_DIRECT24_RGBA);249 c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE |250 CARMINE_EXT_CMODE_DIRECT24_RGBA);251 c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE |252 CARMINE_EXT_CMODE_DIRECT24_RGBA);253 c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE |254 CARMINE_EXT_CMODE_DIRECT24_RGBA);255 c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE |256 CARMINE_EXT_CMODE_DIRECT24_RGBA);257 258 /* Set default frame size to layer mode register */259 width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT;260 width = width << CARMINE_DISP_WIDTH_SHIFT;261 262 height = par->res->vdp - 1;263 param = width | height;264 265 c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param);266 c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width);267 c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param);268 c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param);269 c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param);270 c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param);271 c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param);272 c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param);273 274 /* Set default pos and size */275 window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT;276 window_size |= par->res->hdp;277 278 c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0);279 c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size);280 c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0);281 c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size);282 c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0);283 c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size);284 c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0);285 c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size);286 c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0);287 c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size);288 c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0);289 c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size);290 c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0);291 c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size);292 c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0);293 c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size);294 295 /* Set default origin address */296 c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset);297 c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset);298 c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset);299 c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset);300 c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset);301 c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset);302 c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset);303 c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset);304 305 /* Set default display address */306 c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset);307 c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset);308 c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset);309 c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset);310 c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset);311 c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset);312 c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset);313 314 /* Set default display position */315 c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0);316 c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0);317 c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0);318 c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0);319 c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0);320 c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0);321 c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0);322 323 /* Set default blend mode */324 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0);325 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0);326 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0);327 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0);328 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0);329 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0);330 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0);331 c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0);332 333 /* default transparency mode */334 c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0);335 c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0);336 c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0);337 c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0);338 c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0);339 c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0);340 c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0);341 c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0);342 343 /* Set default read skip parameter */344 c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0);345 c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0);346 c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0);347 c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0);348 c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0);349 c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0);350 c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0);351 352 c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0);353 c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0);354 c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0);355 c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0);356 c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0);357 c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0);358 c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0);359 360 c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0);361 c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0);362 c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0);363 c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0);364 c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0);365 c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0);366 c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0);367}368 369static void set_display_parameters(struct carmine_fb *par)370{371 u32 mode;372 u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw;373 374 /*375 * display timing. Parameters are decreased by one because hardware376 * spec is 0 to (n - 1)377 * */378 hdp = par->res->hdp - 1;379 vdp = par->res->vdp - 1;380 htp = par->res->htp - 1;381 hsp = par->res->hsp - 1;382 hsw = par->res->hsw - 1;383 vtr = par->res->vtr - 1;384 vsp = par->res->vsp - 1;385 vsw = par->res->vsw - 1;386 387 c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL,388 htp << CARMINE_DISP_HTP_SHIFT);389 c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD,390 (hdp << CARMINE_DISP_HDB_SHIFT) | hdp);391 c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS,392 (vsw << CARMINE_DISP_VSW_SHIFT) |393 (hsw << CARMINE_DISP_HSW_SHIFT) |394 (hsp));395 c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL,396 vtr << CARMINE_DISP_VTR_SHIFT);397 c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS,398 (vdp << CARMINE_DISP_VDP_SHIFT) | vsp);399 400 /* clock */401 mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1);402 mode = (mode & ~CARMINE_DISP_DCM_MASK) |403 (par->res->disp_mode & CARMINE_DISP_DCM_MASK);404 /* enable video output and layer 0 */405 mode |= CARMINE_DEN | CARMINE_L0E;406 c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode);407}408 409static int carmine_set_par(struct fb_info *info)410{411 struct carmine_fb *par = info->par;412 int ret;413 414 ret = carmine_find_mode(&info->var);415 if (ret < 0)416 return ret;417 418 par->new_mode = ret;419 if (par->cur_mode != par->new_mode) {420 421 par->cur_mode = par->new_mode;422 par->res = &car_modes[par->new_mode];423 424 carmine_init_display_param(par);425 set_display_parameters(par);426 }427 428 info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;429 return 0;430}431 432static int init_hardware(struct carmine_hw *hw)433{434 u32 flags;435 u32 loops;436 u32 ret;437 438 /* Initialize Carmine */439 /* Sets internal clock */440 c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE,441 CARMINE_DFLT_IP_CLOCK_ENABLE);442 443 /* Video signal output is turned off */444 c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);445 c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);446 447 /* Software reset */448 c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1);449 c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0);450 451 /* I/O mode settings */452 flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 |453 CARMINE_DFLT_IP_DCTL_IO_CONT0;454 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0,455 flags);456 457 /* DRAM initial sequence */458 flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD;459 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD,460 flags);461 462 flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 |463 CARMINE_DFLT_IP_DCTL_EMODE;464 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE,465 flags);466 467 flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 |468 CARMINE_DFLT_IP_DCTL_SET_TIME2;469 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2,470 flags);471 472 flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 |473 CARMINE_DFLT_IP_DCTL_FIFO_DEPTH;474 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags);475 476 flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1;477 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1,478 flags);479 480 flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |481 CARMINE_DFLT_IP_DCTL_STATES;482 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,483 flags);484 485 /* Executes DLL reset */486 if (CARMINE_DCTL_DLL_RESET) {487 for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) {488 489 ret = c_get_hw_reg(hw, CARMINE_DCTL_REG +490 CARMINE_DCTL_REG_RSV0_STATES);491 ret &= CARMINE_DCTL_REG_STATES_MASK;492 if (!ret)493 break;494 495 mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL);496 }497 498 if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) {499 printk(KERN_ERR "DRAM init failed\n");500 return -EIO;501 }502 }503 504 flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 |505 CARMINE_DFLT_IP_DCTL_ADD;506 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags);507 508 flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |509 CARMINE_DFLT_IP_DCTL_STATES_AFT_RST;510 c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,511 flags);512 513 /* Initialize the write back register */514 c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM,515 CARMINE_WB_REG_WBM_DEFAULT);516 517 /* Initialize the Kottos registers */518 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0);519 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0);520 521 /* Set DC offsets */522 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0);523 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0);524 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0);525 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0);526 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0);527 c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0);528 return 0;529}530 531static const struct fb_ops carminefb_ops = {532 .owner = THIS_MODULE,533 FB_DEFAULT_IOMEM_OPS,534 .fb_check_var = carmine_check_var,535 .fb_set_par = carmine_set_par,536 .fb_setcolreg = carmine_setcolreg,537};538 539static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,540 int smem_offset, struct device *device,541 struct fb_info **rinfo)542{543 int ret;544 struct fb_info *info;545 struct carmine_fb *par;546 547 info = framebuffer_alloc(sizeof *par, device);548 if (!info)549 return -ENOMEM;550 551 par = info->par;552 par->display_reg = regs;553 par->smem_offset = smem_offset;554 555 info->screen_base = smem_base + smem_offset;556 info->screen_size = CARMINE_DISPLAY_MEM;557 info->fbops = &carminefb_ops;558 559 info->fix = carminefb_fix;560 info->pseudo_palette = par->pseudo_palette;561 562 ret = fb_alloc_cmap(&info->cmap, 256, 1);563 if (ret < 0)564 goto err_free_fb;565 566 if (fb_mode >= ARRAY_SIZE(carmine_modedb))567 fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;568 569 par->cur_mode = par->new_mode = ~0;570 571 ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb,572 ARRAY_SIZE(carmine_modedb),573 &carmine_modedb[fb_mode], 32);574 if (!ret || ret == 4) {575 ret = -EINVAL;576 goto err_dealloc_cmap;577 }578 579 fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb),580 &info->modelist);581 582 ret = register_framebuffer(info);583 if (ret < 0)584 goto err_dealloc_cmap;585 586 fb_info(info, "%s frame buffer device\n", info->fix.id);587 588 *rinfo = info;589 return 0;590 591err_dealloc_cmap:592 fb_dealloc_cmap(&info->cmap);593err_free_fb:594 framebuffer_release(info);595 return ret;596}597 598static void cleanup_fb_device(struct fb_info *info)599{600 if (info) {601 unregister_framebuffer(info);602 fb_dealloc_cmap(&info->cmap);603 framebuffer_release(info);604 }605}606 607static int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent)608{609 struct carmine_hw *hw;610 struct device *device = &dev->dev;611 struct fb_info *info;612 int ret;613 614 ret = aperture_remove_conflicting_pci_devices(dev, "carminefb");615 if (ret)616 return ret;617 618 ret = pci_enable_device(dev);619 if (ret)620 return ret;621 622 ret = -ENOMEM;623 hw = kzalloc(sizeof *hw, GFP_KERNEL);624 if (!hw)625 goto err_enable_pci;626 627 carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR);628 carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR);629 630 if (!request_mem_region(carminefb_fix.mmio_start,631 carminefb_fix.mmio_len,632 "carminefb regbase")) {633 printk(KERN_ERR "carminefb: Can't reserve regbase.\n");634 ret = -EBUSY;635 goto err_free_hw;636 }637 hw->v_regs = ioremap(carminefb_fix.mmio_start,638 carminefb_fix.mmio_len);639 if (!hw->v_regs) {640 printk(KERN_ERR "carminefb: Can't remap %s register.\n",641 carminefb_fix.id);642 goto err_free_reg_mmio;643 }644 645 carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR);646 carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR);647 648 /* The memory area tends to be very large (256 MiB). Remap only what649 * is required for that largest resolution to avoid remaps at run650 * time651 */652 if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM)653 carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM;654 655 else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) {656 printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d "657 "are required.", carminefb_fix.smem_len,658 CARMINE_TOTAL_DIPLAY_MEM);659 goto err_unmap_vregs;660 }661 662 if (!request_mem_region(carminefb_fix.smem_start,663 carminefb_fix.smem_len, "carminefb smem")) {664 printk(KERN_ERR "carminefb: Can't reserve smem.\n");665 goto err_unmap_vregs;666 }667 668 hw->screen_mem = ioremap(carminefb_fix.smem_start,669 carminefb_fix.smem_len);670 if (!hw->screen_mem) {671 printk(KERN_ERR "carmine: Can't ioremap smem area.\n");672 goto err_reg_smem;673 }674 675 ret = init_hardware(hw);676 if (ret)677 goto err_unmap_screen;678 679 info = NULL;680 if (fb_displays & CARMINE_USE_DISPLAY0) {681 ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG,682 hw->screen_mem, CARMINE_DISPLAY_MEM * 0,683 device, &info);684 if (ret)685 goto err_deinit_hw;686 }687 688 hw->fb[0] = info;689 690 info = NULL;691 if (fb_displays & CARMINE_USE_DISPLAY1) {692 ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG,693 hw->screen_mem, CARMINE_DISPLAY_MEM * 1,694 device, &info);695 if (ret)696 goto err_cleanup_fb0;697 }698 699 hw->fb[1] = info;700 info = NULL;701 702 pci_set_drvdata(dev, hw);703 return 0;704 705err_cleanup_fb0:706 cleanup_fb_device(hw->fb[0]);707err_deinit_hw:708 /* disable clock, etc */709 c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);710err_unmap_screen:711 iounmap(hw->screen_mem);712err_reg_smem:713 release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len);714err_unmap_vregs:715 iounmap(hw->v_regs);716err_free_reg_mmio:717 release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);718err_free_hw:719 kfree(hw);720err_enable_pci:721 pci_disable_device(dev);722 return ret;723}724 725static void carminefb_remove(struct pci_dev *dev)726{727 struct carmine_hw *hw = pci_get_drvdata(dev);728 struct fb_fix_screeninfo fix;729 int i;730 731 /* in case we use only fb1 and not fb1 */732 if (hw->fb[0])733 fix = hw->fb[0]->fix;734 else735 fix = hw->fb[1]->fix;736 737 /* deactivate display(s) and switch clocks */738 c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);739 c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);740 c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);741 742 for (i = 0; i < MAX_DISPLAY; i++)743 cleanup_fb_device(hw->fb[i]);744 745 iounmap(hw->screen_mem);746 release_mem_region(fix.smem_start, fix.smem_len);747 iounmap(hw->v_regs);748 release_mem_region(fix.mmio_start, fix.mmio_len);749 750 pci_disable_device(dev);751 kfree(hw);752}753 754#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf755static struct pci_device_id carmine_devices[] = {756{757 PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)},758 {0, 0, 0, 0, 0, 0, 0}759};760 761MODULE_DEVICE_TABLE(pci, carmine_devices);762 763static struct pci_driver carmine_pci_driver = {764 .name = "carminefb",765 .id_table = carmine_devices,766 .probe = carminefb_probe,767 .remove = carminefb_remove,768};769 770static int __init carminefb_init(void)771{772 if (fb_modesetting_disabled("carminefb"))773 return -ENODEV;774 775 if (!(fb_displays &776 (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) {777 printk(KERN_ERR "If you disable both displays than you don't "778 "need the driver at all\n");779 return -EINVAL;780 }781 return pci_register_driver(&carmine_pci_driver);782}783module_init(carminefb_init);784 785static void __exit carminefb_cleanup(void)786{787 pci_unregister_driver(&carmine_pci_driver);788}789module_exit(carminefb_cleanup);790 791MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>");792MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices");793MODULE_LICENSE("GPL v2");794