1/* 2 * framebuffer driver for Intel Based Mac's 3 * 4 * (c) 2006 Edgar Hucek <gimli@dark-green.com> 5 * Original imac driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de> 6 * 7 */ 8 9#include <linux/delay.h> 10#include <linux/errno.h> 11#include <linux/fb.h> 12#include <linux/kernel.h> 13#include <linux/init.h> 14#include <linux/ioport.h> 15#include <linux/mm.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/screen_info.h> 19#include <linux/slab.h> 20#include <linux/string.h> 21#include <linux/dmi.h> 22#include <linux/efi.h> 23 24#include <asm/io.h> 25 26#include <video/vga.h> 27 28typedef enum _MAC_TYPE { 29 M_I17, 30 M_I20, 31 M_MINI, 32 M_MACBOOK, 33 M_UNKNOWN 34} MAC_TYPE; 35 36/* --------------------------------------------------------------------- */ 37 38static struct fb_var_screeninfo imacfb_defined __initdata = { 39 .activate = FB_ACTIVATE_NOW, 40 .height = -1, 41 .width = -1, 42 .right_margin = 32, 43 .upper_margin = 16, 44 .lower_margin = 4, 45 .vsync_len = 4, 46 .vmode = FB_VMODE_NONINTERLACED, 47}; 48 49static struct fb_fix_screeninfo imacfb_fix __initdata = { 50 .id = "IMAC VGA", 51 .type = FB_TYPE_PACKED_PIXELS, 52 .accel = FB_ACCEL_NONE, 53 .visual = FB_VISUAL_TRUECOLOR, 54}; 55 56static int inverse; 57static int model = M_UNKNOWN; 58static int manual_height; 59static int manual_width; 60 61static int set_system(struct dmi_system_id *id) 62{ 63 printk(KERN_INFO "imacfb: %s detected - set system to %ld\n", 64 id->ident, (long)id->driver_data); 65 66 model = (long)id->driver_data; 67 68 return 0; 69} 70 71static struct dmi_system_id __initdata dmi_system_table[] = { 72 { set_system, "iMac4,1", { 73 DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), 74 DMI_MATCH(DMI_PRODUCT_NAME,"iMac4,1") }, (void*)M_I17}, 75 { set_system, "MacBookPro1,1", { 76 DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), 77 DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro1,1") }, (void*)M_I17}, 78 { set_system, "MacBook1,1", { 79 DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), 80 DMI_MATCH(DMI_PRODUCT_NAME,"MacBook1,1")}, (void *)M_MACBOOK}, 81 { set_system, "Macmini1,1", { 82 DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), 83 DMI_MATCH(DMI_PRODUCT_NAME,"Macmini1,1")}, (void *)M_MINI}, 84 {}, 85}; 86 87#define DEFAULT_FB_MEM 1024*1024*16 88 89/* --------------------------------------------------------------------- */ 90 91static int imacfb_setcolreg(unsigned regno, unsigned red, unsigned green, 92 unsigned blue, unsigned transp, 93 struct fb_info *info) 94{ 95 /* 96 * Set a single color register. The values supplied are 97 * already rounded down to the hardware's capabilities 98 * (according to the entries in the `var' structure). Return 99 * != 0 for invalid regno. 100 */ 101 102 if (regno >= info->cmap.len) 103 return 1; 104 105 if (regno < 16) { 106 red >>= 8; 107 green >>= 8; 108 blue >>= 8; 109 ((u32 *)(info->pseudo_palette))[regno] = 110 (red << info->var.red.offset) | 111 (green << info->var.green.offset) | 112 (blue << info->var.blue.offset); 113 } 114 return 0; 115} 116 117static struct fb_ops imacfb_ops = { 118 .owner = THIS_MODULE, 119 .fb_setcolreg = imacfb_setcolreg, 120 .fb_fillrect = cfb_fillrect, 121 .fb_copyarea = cfb_copyarea, 122 .fb_imageblit = cfb_imageblit, 123}; 124 125static int __init imacfb_setup(char *options) 126{ 127 char *this_opt; 128 129 if (!options || !*options) 130 return 0; 131 132 while ((this_opt = strsep(&options, ",")) != NULL) { 133 if (!*this_opt) continue; 134 135 if (!strcmp(this_opt, "inverse")) 136 inverse = 1; 137 else if (!strcmp(this_opt, "i17")) 138 model = M_I17; 139 else if (!strcmp(this_opt, "i20")) 140 model = M_I20; 141 else if (!strcmp(this_opt, "mini")) 142 model = M_MINI; 143 else if (!strcmp(this_opt, "macbook")) 144 model = M_MACBOOK; 145 else if (!strncmp(this_opt, "height:", 7)) 146 manual_height = simple_strtoul(this_opt+7, NULL, 0); 147 else if (!strncmp(this_opt, "width:", 6)) 148 manual_width = simple_strtoul(this_opt+6, NULL, 0); 149 } 150 return 0; 151} 152 153static int __init imacfb_probe(struct platform_device *dev) 154{ 155 struct fb_info *info; 156 int err; 157 unsigned int size_vmode; 158 unsigned int size_remap; 159 unsigned int size_total; 160 161 screen_info.lfb_depth = 32; 162 screen_info.lfb_size = DEFAULT_FB_MEM / 0x10000; 163 screen_info.pages=1; 164 screen_info.blue_size = 8; 165 screen_info.blue_pos = 0; 166 screen_info.green_size = 8; 167 screen_info.green_pos = 8; 168 screen_info.red_size = 8; 169 screen_info.red_pos = 16; 170 screen_info.rsvd_size = 8; 171 screen_info.rsvd_pos = 24; 172 173 switch (model) { 174 case M_I17: 175 screen_info.lfb_width = 1440; 176 screen_info.lfb_height = 900; 177 screen_info.lfb_linelength = 1472 * 4; 178 screen_info.lfb_base = 0x80010000; 179 break; 180 case M_I20: 181 screen_info.lfb_width = 1680; 182 screen_info.lfb_height = 1050; 183 screen_info.lfb_linelength = 1728 * 4; 184 screen_info.lfb_base = 0x80010000; 185 break; 186 case M_MINI: 187 screen_info.lfb_width = 1024; 188 screen_info.lfb_height = 768; 189 screen_info.lfb_linelength = 2048 * 4; 190 screen_info.lfb_base = 0x80000000; 191 break; 192 case M_MACBOOK: 193 screen_info.lfb_width = 1280; 194 screen_info.lfb_height = 800; 195 screen_info.lfb_linelength = 2048 * 4; 196 screen_info.lfb_base = 0x80000000; 197 break; 198 } 199 200 /* if the user wants to manually specify height/width, 201 we will override the defaults */ 202 /* TODO: eventually get auto-detection working */ 203 if (manual_height > 0) 204 screen_info.lfb_height = manual_height; 205 if (manual_width > 0) 206 screen_info.lfb_width = manual_width; 207 208 imacfb_fix.smem_start = screen_info.lfb_base; 209 imacfb_defined.bits_per_pixel = screen_info.lfb_depth; 210 imacfb_defined.xres = screen_info.lfb_width; 211 imacfb_defined.yres = screen_info.lfb_height; 212 imacfb_fix.line_length = screen_info.lfb_linelength; 213 214 /* size_vmode -- that is the amount of memory needed for the 215 * used video mode, i.e. the minimum amount of 216 * memory we need. */ 217 size_vmode = imacfb_defined.yres * imacfb_fix.line_length; 218 219 /* size_total -- all video memory we have. Used for 220 * entries, ressource allocation and bounds 221 * checking. */ 222 size_total = screen_info.lfb_size * 65536; 223 if (size_total < size_vmode) 224 size_total = size_vmode; 225 226 /* size_remap -- the amount of video memory we are going to 227 * use for imacfb. With modern cards it is no 228 * option to simply use size_total as that 229 * wastes plenty of kernel address space. */ 230 size_remap = size_vmode * 2; 231 if (size_remap < size_vmode) 232 size_remap = size_vmode; 233 if (size_remap > size_total) 234 size_remap = size_total; 235 imacfb_fix.smem_len = size_remap; 236 237#ifndef __i386__ 238 screen_info.imacpm_seg = 0; 239#endif 240 241 if (!request_mem_region(imacfb_fix.smem_start, size_total, "imacfb")) { 242 printk(KERN_WARNING 243 "imacfb: cannot reserve video memory at 0x%lx\n", 244 imacfb_fix.smem_start); 245 /* We cannot make this fatal. Sometimes this comes from magic 246 spaces our resource handlers simply don't know about */ 247 } 248 249 info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); 250 if (!info) { 251 err = -ENOMEM; 252 goto err_release_mem; 253 } 254 info->pseudo_palette = info->par; 255 info->par = NULL; 256 257 info->screen_base = ioremap(imacfb_fix.smem_start, imacfb_fix.smem_len); 258 if (!info->screen_base) { 259 printk(KERN_ERR "imacfb: abort, cannot ioremap video memory " 260 "0x%x @ 0x%lx\n", 261 imacfb_fix.smem_len, imacfb_fix.smem_start); 262 err = -EIO; 263 goto err_unmap; 264 } 265 266 printk(KERN_INFO "imacfb: framebuffer at 0x%lx, mapped to 0x%p, " 267 "using %dk, total %dk\n", 268 imacfb_fix.smem_start, info->screen_base, 269 size_remap/1024, size_total/1024); 270 printk(KERN_INFO "imacfb: mode is %dx%dx%d, linelength=%d, pages=%d\n", 271 imacfb_defined.xres, imacfb_defined.yres, 272 imacfb_defined.bits_per_pixel, imacfb_fix.line_length, 273 screen_info.pages); 274 275 imacfb_defined.xres_virtual = imacfb_defined.xres; 276 imacfb_defined.yres_virtual = imacfb_fix.smem_len / 277 imacfb_fix.line_length; 278 printk(KERN_INFO "imacfb: scrolling: redraw\n"); 279 imacfb_defined.yres_virtual = imacfb_defined.yres; 280 281 /* some dummy values for timing to make fbset happy */ 282 imacfb_defined.pixclock = 10000000 / imacfb_defined.xres * 283 1000 / imacfb_defined.yres; 284 imacfb_defined.left_margin = (imacfb_defined.xres / 8) & 0xf8; 285 imacfb_defined.hsync_len = (imacfb_defined.xres / 8) & 0xf8; 286 287 imacfb_defined.red.offset = screen_info.red_pos; 288 imacfb_defined.red.length = screen_info.red_size; 289 imacfb_defined.green.offset = screen_info.green_pos; 290 imacfb_defined.green.length = screen_info.green_size; 291 imacfb_defined.blue.offset = screen_info.blue_pos; 292 imacfb_defined.blue.length = screen_info.blue_size; 293 imacfb_defined.transp.offset = screen_info.rsvd_pos; 294 imacfb_defined.transp.length = screen_info.rsvd_size; 295 296 printk(KERN_INFO "imacfb: %s: " 297 "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", 298 "Truecolor", 299 screen_info.rsvd_size, 300 screen_info.red_size, 301 screen_info.green_size, 302 screen_info.blue_size, 303 screen_info.rsvd_pos, 304 screen_info.red_pos, 305 screen_info.green_pos, 306 screen_info.blue_pos); 307 308 imacfb_fix.ypanstep = 0; 309 imacfb_fix.ywrapstep = 0; 310 311 request_region(0x3c0, 32, "imacfb"); 312 313 info->fbops = &imacfb_ops; 314 info->var = imacfb_defined; 315 info->fix = imacfb_fix; 316 info->flags = FBINFO_FLAG_DEFAULT; 317 318 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { 319 err = -ENOMEM; 320 goto err_unmap; 321 } 322 if (register_framebuffer(info)<0) { 323 err = -EINVAL; 324 goto err_fb_dealoc; 325 } 326 printk(KERN_INFO "fb%d: %s frame buffer device\n", 327 info->node, info->fix.id); 328 return 0; 329 330err_fb_dealoc: 331 fb_dealloc_cmap(&info->cmap); 332err_unmap: 333 iounmap(info->screen_base); 334 framebuffer_release(info); 335err_release_mem: 336 release_mem_region(imacfb_fix.smem_start, size_total); 337 return err; 338} 339 340static struct platform_driver imacfb_driver = { 341 .probe = imacfb_probe, 342 .driver = { 343 .name = "imacfb", 344 }, 345}; 346 347static struct platform_device imacfb_device = { 348 .name = "imacfb", 349}; 350 351static int __init imacfb_init(void) 352{ 353 int ret; 354 char *option = NULL; 355 356 if (!efi_enabled) 357 return -ENODEV; 358 if (!dmi_check_system(dmi_system_table)) 359 return -ENODEV; 360 if (model == M_UNKNOWN) 361 return -ENODEV; 362 363 if (fb_get_options("imacfb", &option)) 364 return -ENODEV; 365 366 imacfb_setup(option); 367 ret = platform_driver_register(&imacfb_driver); 368 369 if (!ret) { 370 ret = platform_device_register(&imacfb_device); 371 if (ret) 372 platform_driver_unregister(&imacfb_driver); 373 } 374 return ret; 375} 376module_init(imacfb_init); 377 378MODULE_LICENSE("GPL"); 379