diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-28 01:31:46 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-30 03:27:33 -0400 |
commit | c7f439b99efbea74c70a5531f92566db5a6731f2 (patch) | |
tree | 7053ceffa23d54670862e14d0bc2eec9d5d42427 /drivers/video/tcx.c | |
parent | a0afaa6ab12cf696d170c22a8fdfd88c3e33555c (diff) |
[VIDEO]: Fix OOPS in all SBUS framebuffer drivers.
All of these drivers use a silly:
struct all_info {
struct fb_info info;
struct foo_par par;
};
struct all_info *all = kzalloc(sizeof(*all), GFP_KERNEL);
all->info.par = &all->par;
etc. etc. code sequence, basically replicating the provided
framebuffer_alloc()/framebuffer_release(), and doing it badly.
Not only is this massive code duplication, it also caused a
bug in that we weren't setting the fb_info->device pointer
which results in an OOPS when fb_is_primary_device() runs.
Fix all of this by using framebuffer_{alloc,release}() and
passing in "&of_device->dev" as the device pointer.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/video/tcx.c')
-rw-r--r-- | drivers/video/tcx.c | 184 |
1 files changed, 90 insertions, 94 deletions
diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index 5a99669232ce..e5a9ddb3c8be 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c | |||
@@ -345,88 +345,82 @@ tcx_init_fix(struct fb_info *info, int linebytes) | |||
345 | info->fix.accel = FB_ACCEL_SUN_TCX; | 345 | info->fix.accel = FB_ACCEL_SUN_TCX; |
346 | } | 346 | } |
347 | 347 | ||
348 | struct all_info { | 348 | static void tcx_unmap_regs(struct of_device *op, struct fb_info *info, |
349 | struct fb_info info; | 349 | struct tcx_par *par) |
350 | struct tcx_par par; | ||
351 | }; | ||
352 | |||
353 | static void tcx_unmap_regs(struct of_device *op, struct all_info *all) | ||
354 | { | 350 | { |
355 | if (all->par.tec) | 351 | if (par->tec) |
356 | of_iounmap(&op->resource[7], | 352 | of_iounmap(&op->resource[7], |
357 | all->par.tec, sizeof(struct tcx_tec)); | 353 | par->tec, sizeof(struct tcx_tec)); |
358 | if (all->par.thc) | 354 | if (par->thc) |
359 | of_iounmap(&op->resource[9], | 355 | of_iounmap(&op->resource[9], |
360 | all->par.thc, sizeof(struct tcx_thc)); | 356 | par->thc, sizeof(struct tcx_thc)); |
361 | if (all->par.bt) | 357 | if (par->bt) |
362 | of_iounmap(&op->resource[8], | 358 | of_iounmap(&op->resource[8], |
363 | all->par.bt, sizeof(struct bt_regs)); | 359 | par->bt, sizeof(struct bt_regs)); |
364 | if (all->par.cplane) | 360 | if (par->cplane) |
365 | of_iounmap(&op->resource[4], | 361 | of_iounmap(&op->resource[4], |
366 | all->par.cplane, all->par.fbsize * sizeof(u32)); | 362 | par->cplane, par->fbsize * sizeof(u32)); |
367 | if (all->info.screen_base) | 363 | if (info->screen_base) |
368 | of_iounmap(&op->resource[0], | 364 | of_iounmap(&op->resource[0], |
369 | all->info.screen_base, all->par.fbsize); | 365 | info->screen_base, par->fbsize); |
370 | } | 366 | } |
371 | 367 | ||
372 | static int __devinit tcx_init_one(struct of_device *op) | 368 | static int __devinit tcx_init_one(struct of_device *op) |
373 | { | 369 | { |
374 | struct device_node *dp = op->node; | 370 | struct device_node *dp = op->node; |
375 | struct all_info *all; | 371 | struct fb_info *info; |
372 | struct tcx_par *par; | ||
376 | int linebytes, i, err; | 373 | int linebytes, i, err; |
377 | 374 | ||
378 | all = kzalloc(sizeof(*all), GFP_KERNEL); | 375 | info = framebuffer_alloc(sizeof(struct tcx_par), &op->dev); |
379 | if (!all) | ||
380 | return -ENOMEM; | ||
381 | 376 | ||
382 | spin_lock_init(&all->par.lock); | 377 | err = -ENOMEM; |
378 | if (!info) | ||
379 | goto out_err; | ||
380 | par = info->par; | ||
383 | 381 | ||
384 | all->par.lowdepth = | 382 | spin_lock_init(&par->lock); |
383 | |||
384 | par->lowdepth = | ||
385 | (of_find_property(dp, "tcx-8-bit", NULL) != NULL); | 385 | (of_find_property(dp, "tcx-8-bit", NULL) != NULL); |
386 | 386 | ||
387 | sbusfb_fill_var(&all->info.var, dp->node, 8); | 387 | sbusfb_fill_var(&info->var, dp->node, 8); |
388 | all->info.var.red.length = 8; | 388 | info->var.red.length = 8; |
389 | all->info.var.green.length = 8; | 389 | info->var.green.length = 8; |
390 | all->info.var.blue.length = 8; | 390 | info->var.blue.length = 8; |
391 | 391 | ||
392 | linebytes = of_getintprop_default(dp, "linebytes", | 392 | linebytes = of_getintprop_default(dp, "linebytes", |
393 | all->info.var.xres); | 393 | info->var.xres); |
394 | all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres); | 394 | par->fbsize = PAGE_ALIGN(linebytes * info->var.yres); |
395 | 395 | ||
396 | all->par.tec = of_ioremap(&op->resource[7], 0, | 396 | par->tec = of_ioremap(&op->resource[7], 0, |
397 | sizeof(struct tcx_tec), "tcx tec"); | 397 | sizeof(struct tcx_tec), "tcx tec"); |
398 | all->par.thc = of_ioremap(&op->resource[9], 0, | 398 | par->thc = of_ioremap(&op->resource[9], 0, |
399 | sizeof(struct tcx_thc), "tcx thc"); | 399 | sizeof(struct tcx_thc), "tcx thc"); |
400 | all->par.bt = of_ioremap(&op->resource[8], 0, | 400 | par->bt = of_ioremap(&op->resource[8], 0, |
401 | sizeof(struct bt_regs), "tcx dac"); | 401 | sizeof(struct bt_regs), "tcx dac"); |
402 | all->info.screen_base = of_ioremap(&op->resource[0], 0, | 402 | info->screen_base = of_ioremap(&op->resource[0], 0, |
403 | all->par.fbsize, "tcx ram"); | 403 | par->fbsize, "tcx ram"); |
404 | if (!all->par.tec || !all->par.thc || | 404 | if (!par->tec || !par->thc || |
405 | !all->par.bt || !all->info.screen_base) { | 405 | !par->bt || !info->screen_base) |
406 | tcx_unmap_regs(op, all); | 406 | goto out_unmap_regs; |
407 | kfree(all); | 407 | |
408 | return -ENOMEM; | 408 | memcpy(&par->mmap_map, &__tcx_mmap_map, sizeof(par->mmap_map)); |
409 | } | 409 | if (!par->lowdepth) { |
410 | 410 | par->cplane = of_ioremap(&op->resource[4], 0, | |
411 | memcpy(&all->par.mmap_map, &__tcx_mmap_map, sizeof(all->par.mmap_map)); | 411 | par->fbsize * sizeof(u32), |
412 | if (!all->par.lowdepth) { | ||
413 | all->par.cplane = of_ioremap(&op->resource[4], 0, | ||
414 | all->par.fbsize * sizeof(u32), | ||
415 | "tcx cplane"); | 412 | "tcx cplane"); |
416 | if (!all->par.cplane) { | 413 | if (!par->cplane) |
417 | tcx_unmap_regs(op, all); | 414 | goto out_unmap_regs; |
418 | kfree(all); | ||
419 | return -ENOMEM; | ||
420 | } | ||
421 | } else { | 415 | } else { |
422 | all->par.mmap_map[1].size = SBUS_MMAP_EMPTY; | 416 | par->mmap_map[1].size = SBUS_MMAP_EMPTY; |
423 | all->par.mmap_map[4].size = SBUS_MMAP_EMPTY; | 417 | par->mmap_map[4].size = SBUS_MMAP_EMPTY; |
424 | all->par.mmap_map[5].size = SBUS_MMAP_EMPTY; | 418 | par->mmap_map[5].size = SBUS_MMAP_EMPTY; |
425 | all->par.mmap_map[6].size = SBUS_MMAP_EMPTY; | 419 | par->mmap_map[6].size = SBUS_MMAP_EMPTY; |
426 | } | 420 | } |
427 | 421 | ||
428 | all->par.physbase = 0; | 422 | par->physbase = 0; |
429 | all->par.which_io = op->resource[0].flags & IORESOURCE_BITS; | 423 | par->which_io = op->resource[0].flags & IORESOURCE_BITS; |
430 | 424 | ||
431 | for (i = 0; i < TCX_MMAP_ENTRIES; i++) { | 425 | for (i = 0; i < TCX_MMAP_ENTRIES; i++) { |
432 | int j; | 426 | int j; |
@@ -444,53 +438,54 @@ static int __devinit tcx_init_one(struct of_device *op) | |||
444 | j = i; | 438 | j = i; |
445 | break; | 439 | break; |
446 | }; | 440 | }; |
447 | all->par.mmap_map[i].poff = op->resource[j].start; | 441 | par->mmap_map[i].poff = op->resource[j].start; |
448 | } | 442 | } |
449 | 443 | ||
450 | all->info.flags = FBINFO_DEFAULT; | 444 | info->flags = FBINFO_DEFAULT; |
451 | all->info.fbops = &tcx_ops; | 445 | info->fbops = &tcx_ops; |
452 | all->info.par = &all->par; | ||
453 | 446 | ||
454 | /* Initialize brooktree DAC. */ | 447 | /* Initialize brooktree DAC. */ |
455 | sbus_writel(0x04 << 24, &all->par.bt->addr); /* color planes */ | 448 | sbus_writel(0x04 << 24, &par->bt->addr); /* color planes */ |
456 | sbus_writel(0xff << 24, &all->par.bt->control); | 449 | sbus_writel(0xff << 24, &par->bt->control); |
457 | sbus_writel(0x05 << 24, &all->par.bt->addr); | 450 | sbus_writel(0x05 << 24, &par->bt->addr); |
458 | sbus_writel(0x00 << 24, &all->par.bt->control); | 451 | sbus_writel(0x00 << 24, &par->bt->control); |
459 | sbus_writel(0x06 << 24, &all->par.bt->addr); /* overlay plane */ | 452 | sbus_writel(0x06 << 24, &par->bt->addr); /* overlay plane */ |
460 | sbus_writel(0x73 << 24, &all->par.bt->control); | 453 | sbus_writel(0x73 << 24, &par->bt->control); |
461 | sbus_writel(0x07 << 24, &all->par.bt->addr); | 454 | sbus_writel(0x07 << 24, &par->bt->addr); |
462 | sbus_writel(0x00 << 24, &all->par.bt->control); | 455 | sbus_writel(0x00 << 24, &par->bt->control); |
463 | 456 | ||
464 | tcx_reset(&all->info); | 457 | tcx_reset(info); |
465 | |||
466 | tcx_blank(FB_BLANK_UNBLANK, &all->info); | ||
467 | |||
468 | if (fb_alloc_cmap(&all->info.cmap, 256, 0)) { | ||
469 | tcx_unmap_regs(op, all); | ||
470 | kfree(all); | ||
471 | return -ENOMEM; | ||
472 | } | ||
473 | 458 | ||
474 | fb_set_cmap(&all->info.cmap, &all->info); | 459 | tcx_blank(FB_BLANK_UNBLANK, info); |
475 | tcx_init_fix(&all->info, linebytes); | ||
476 | 460 | ||
477 | err = register_framebuffer(&all->info); | 461 | if (fb_alloc_cmap(&info->cmap, 256, 0)) |
478 | if (err < 0) { | 462 | goto out_unmap_regs; |
479 | fb_dealloc_cmap(&all->info.cmap); | 463 | |
480 | tcx_unmap_regs(op, all); | 464 | fb_set_cmap(&info->cmap, info); |
481 | kfree(all); | 465 | tcx_init_fix(info, linebytes); |
482 | return err; | 466 | |
483 | } | 467 | err = register_framebuffer(info); |
468 | if (err < 0) | ||
469 | goto out_dealloc_cmap; | ||
484 | 470 | ||
485 | dev_set_drvdata(&op->dev, all); | 471 | dev_set_drvdata(&op->dev, info); |
486 | 472 | ||
487 | printk("%s: TCX at %lx:%lx, %s\n", | 473 | printk("%s: TCX at %lx:%lx, %s\n", |
488 | dp->full_name, | 474 | dp->full_name, |
489 | all->par.which_io, | 475 | par->which_io, |
490 | op->resource[0].start, | 476 | op->resource[0].start, |
491 | all->par.lowdepth ? "8-bit only" : "24-bit depth"); | 477 | par->lowdepth ? "8-bit only" : "24-bit depth"); |
492 | 478 | ||
493 | return 0; | 479 | return 0; |
480 | |||
481 | out_dealloc_cmap: | ||
482 | fb_dealloc_cmap(&info->cmap); | ||
483 | |||
484 | out_unmap_regs: | ||
485 | tcx_unmap_regs(op, info, par); | ||
486 | |||
487 | out_err: | ||
488 | return err; | ||
494 | } | 489 | } |
495 | 490 | ||
496 | static int __devinit tcx_probe(struct of_device *dev, const struct of_device_id *match) | 491 | static int __devinit tcx_probe(struct of_device *dev, const struct of_device_id *match) |
@@ -502,14 +497,15 @@ static int __devinit tcx_probe(struct of_device *dev, const struct of_device_id | |||
502 | 497 | ||
503 | static int __devexit tcx_remove(struct of_device *op) | 498 | static int __devexit tcx_remove(struct of_device *op) |
504 | { | 499 | { |
505 | struct all_info *all = dev_get_drvdata(&op->dev); | 500 | struct fb_info *info = dev_get_drvdata(&op->dev); |
501 | struct tcx_par *par = info->par; | ||
506 | 502 | ||
507 | unregister_framebuffer(&all->info); | 503 | unregister_framebuffer(info); |
508 | fb_dealloc_cmap(&all->info.cmap); | 504 | fb_dealloc_cmap(&info->cmap); |
509 | 505 | ||
510 | tcx_unmap_regs(op, all); | 506 | tcx_unmap_regs(op, info, par); |
511 | 507 | ||
512 | kfree(all); | 508 | framebuffer_release(info); |
513 | 509 | ||
514 | dev_set_drvdata(&op->dev, NULL); | 510 | dev_set_drvdata(&op->dev, NULL); |
515 | 511 | ||