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