diff options
author | Jaya Kumar <jayakumar.lkml@gmail.com> | 2008-08-19 06:17:55 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-08-26 12:01:12 -0400 |
commit | e935508515cc5592d7c80d7f51f21103f73efb2d (patch) | |
tree | eba61a65739b10329150099b49bb313b772c5e8a /drivers | |
parent | 28501336f8b9fb5ec6c2d7bb07b4dfa88ceed272 (diff) |
[ARM] 5209/1: metronomefb: changes to use platform framebuffer
These changes are used in order to support the use of the framebuffer
provided by the platform device driver rather than to directly allocate one.
Other changes are cleanup to error handling and order of release.
Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Acked-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Acked-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/Kconfig | 18 | ||||
-rw-r--r-- | drivers/video/metronomefb.c | 241 |
2 files changed, 147 insertions, 112 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 70d135e0cc47..6fd4a2f63cf8 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -172,11 +172,6 @@ config FB_DEFERRED_IO | |||
172 | bool | 172 | bool |
173 | depends on FB | 173 | depends on FB |
174 | 174 | ||
175 | config FB_METRONOME | ||
176 | tristate | ||
177 | depends on FB | ||
178 | depends on FB_DEFERRED_IO | ||
179 | |||
180 | config FB_HECUBA | 175 | config FB_HECUBA |
181 | tristate | 176 | tristate |
182 | depends on FB | 177 | depends on FB |
@@ -2041,6 +2036,19 @@ config XEN_FBDEV_FRONTEND | |||
2041 | frame buffer driver. It communicates with a back-end | 2036 | frame buffer driver. It communicates with a back-end |
2042 | in another domain. | 2037 | in another domain. |
2043 | 2038 | ||
2039 | config FB_METRONOME | ||
2040 | tristate "E-Ink Metronome/8track controller support" | ||
2041 | depends on FB | ||
2042 | select FB_SYS_FILLRECT | ||
2043 | select FB_SYS_COPYAREA | ||
2044 | select FB_SYS_IMAGEBLIT | ||
2045 | select FB_SYS_FOPS | ||
2046 | select FB_DEFERRED_IO | ||
2047 | help | ||
2048 | This driver implements support for the E-Ink Metronome | ||
2049 | controller. The pre-release name for this device was 8track | ||
2050 | and could also have been called by some vendors as PVI-nnnn. | ||
2051 | |||
2044 | source "drivers/video/omap/Kconfig" | 2052 | source "drivers/video/omap/Kconfig" |
2045 | 2053 | ||
2046 | source "drivers/video/backlight/Kconfig" | 2054 | source "drivers/video/backlight/Kconfig" |
diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index 7cda4f80af52..afeed0611e3e 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c | |||
@@ -44,16 +44,59 @@ | |||
44 | #define DPY_W 832 | 44 | #define DPY_W 832 |
45 | #define DPY_H 622 | 45 | #define DPY_H 622 |
46 | 46 | ||
47 | static int user_wfm_size; | ||
48 | |||
47 | /* frame differs from image. frame includes non-visible pixels */ | 49 | /* frame differs from image. frame includes non-visible pixels */ |
48 | struct epd_frame { | 50 | struct epd_frame { |
49 | int fw; /* frame width */ | 51 | int fw; /* frame width */ |
50 | int fh; /* frame height */ | 52 | int fh; /* frame height */ |
53 | u16 config[4]; | ||
54 | int wfm_size; | ||
51 | }; | 55 | }; |
52 | 56 | ||
53 | static struct epd_frame epd_frame_table[] = { | 57 | static struct epd_frame epd_frame_table[] = { |
54 | { | 58 | { |
55 | .fw = 832, | 59 | .fw = 832, |
56 | .fh = 622 | 60 | .fh = 622, |
61 | .config = { | ||
62 | 15 /* sdlew */ | ||
63 | | 2 << 8 /* sdosz */ | ||
64 | | 0 << 11 /* sdor */ | ||
65 | | 0 << 12 /* sdces */ | ||
66 | | 0 << 15, /* sdcer */ | ||
67 | 42 /* gdspl */ | ||
68 | | 1 << 8 /* gdr1 */ | ||
69 | | 1 << 9 /* sdshr */ | ||
70 | | 0 << 15, /* gdspp */ | ||
71 | 18 /* gdspw */ | ||
72 | | 0 << 15, /* dispc */ | ||
73 | 599 /* vdlc */ | ||
74 | | 0 << 11 /* dsi */ | ||
75 | | 0 << 12, /* dsic */ | ||
76 | }, | ||
77 | .wfm_size = 47001, | ||
78 | }, | ||
79 | { | ||
80 | .fw = 1088, | ||
81 | .fh = 791, | ||
82 | .config = { | ||
83 | 0x0104, | ||
84 | 0x031f, | ||
85 | 0x0088, | ||
86 | 0x02ff, | ||
87 | }, | ||
88 | .wfm_size = 46770, | ||
89 | }, | ||
90 | { | ||
91 | .fw = 1200, | ||
92 | .fh = 842, | ||
93 | .config = { | ||
94 | 0x0101, | ||
95 | 0x030e, | ||
96 | 0x0012, | ||
97 | 0x0280, | ||
98 | }, | ||
99 | .wfm_size = 46770, | ||
57 | }, | 100 | }, |
58 | }; | 101 | }; |
59 | 102 | ||
@@ -125,7 +168,6 @@ static u16 calc_img_cksum(u16 *start, int length) | |||
125 | } | 168 | } |
126 | 169 | ||
127 | /* here we decode the incoming waveform file and populate metromem */ | 170 | /* here we decode the incoming waveform file and populate metromem */ |
128 | #define EXP_WFORM_SIZE 47001 | ||
129 | static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, | 171 | static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, |
130 | struct metronomefb_par *par) | 172 | struct metronomefb_par *par) |
131 | { | 173 | { |
@@ -142,9 +184,12 @@ static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, | |||
142 | u8 *metromem = par->metromem_wfm; | 184 | u8 *metromem = par->metromem_wfm; |
143 | struct device *dev = par->info->dev; | 185 | struct device *dev = par->info->dev; |
144 | 186 | ||
145 | if (size != EXP_WFORM_SIZE) { | 187 | if (user_wfm_size) |
188 | epd_frame_table[par->dt].wfm_size = user_wfm_size; | ||
189 | |||
190 | if (size != epd_frame_table[par->dt].wfm_size) { | ||
146 | dev_err(dev, "Error: unexpected size %d != %d\n", size, | 191 | dev_err(dev, "Error: unexpected size %d != %d\n", size, |
147 | EXP_WFORM_SIZE); | 192 | epd_frame_table[par->dt].wfm_size); |
148 | return -EINVAL; | 193 | return -EINVAL; |
149 | } | 194 | } |
150 | 195 | ||
@@ -267,15 +312,12 @@ static int metronome_display_cmd(struct metronomefb_par *par) | |||
267 | u16 cs; | 312 | u16 cs; |
268 | u16 opcode; | 313 | u16 opcode; |
269 | static u8 borderval; | 314 | static u8 borderval; |
270 | u8 *ptr; | ||
271 | 315 | ||
272 | /* setup display command | 316 | /* setup display command |
273 | we can't immediately set the opcode since the controller | 317 | we can't immediately set the opcode since the controller |
274 | will try parse the command before we've set it all up | 318 | will try parse the command before we've set it all up |
275 | so we just set cs here and set the opcode at the end */ | 319 | so we just set cs here and set the opcode at the end */ |
276 | 320 | ||
277 | ptr = par->metromem; | ||
278 | |||
279 | if (par->metromem_cmd->opcode == 0xCC40) | 321 | if (par->metromem_cmd->opcode == 0xCC40) |
280 | opcode = cs = 0xCC41; | 322 | opcode = cs = 0xCC41; |
281 | else | 323 | else |
@@ -328,44 +370,17 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) | |||
328 | 370 | ||
329 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) | 371 | static int __devinit metronome_config_cmd(struct metronomefb_par *par) |
330 | { | 372 | { |
331 | int i; | ||
332 | u16 cs; | ||
333 | |||
334 | /* setup config command | 373 | /* setup config command |
335 | we can't immediately set the opcode since the controller | 374 | we can't immediately set the opcode since the controller |
336 | will try parse the command before we've set it all up | 375 | will try parse the command before we've set it all up */ |
337 | so we just set cs here and set the opcode at the end */ | ||
338 | |||
339 | cs = 0xCC10; | ||
340 | |||
341 | /* set the 12 args ( 8 bytes ) for config. see spec for meanings */ | ||
342 | i = 0; | ||
343 | par->metromem_cmd->args[i] = 15 /* sdlew */ | ||
344 | | 2 << 8 /* sdosz */ | ||
345 | | 0 << 11 /* sdor */ | ||
346 | | 0 << 12 /* sdces */ | ||
347 | | 0 << 15; /* sdcer */ | ||
348 | cs += par->metromem_cmd->args[i++]; | ||
349 | |||
350 | par->metromem_cmd->args[i] = 42 /* gdspl */ | ||
351 | | 1 << 8 /* gdr1 */ | ||
352 | | 1 << 9 /* sdshr */ | ||
353 | | 0 << 15; /* gdspp */ | ||
354 | cs += par->metromem_cmd->args[i++]; | ||
355 | |||
356 | par->metromem_cmd->args[i] = 18 /* gdspw */ | ||
357 | | 0 << 15; /* dispc */ | ||
358 | cs += par->metromem_cmd->args[i++]; | ||
359 | |||
360 | par->metromem_cmd->args[i] = 599 /* vdlc */ | ||
361 | | 0 << 11 /* dsi */ | ||
362 | | 0 << 12; /* dsic */ | ||
363 | cs += par->metromem_cmd->args[i++]; | ||
364 | 376 | ||
377 | memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config, | ||
378 | sizeof(epd_frame_table[par->dt].config)); | ||
365 | /* the rest are 0 */ | 379 | /* the rest are 0 */ |
366 | memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); | 380 | memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2); |
367 | 381 | ||
368 | par->metromem_cmd->csum = cs; | 382 | par->metromem_cmd->csum = 0xCC10; |
383 | par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4); | ||
369 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ | 384 | par->metromem_cmd->opcode = 0xCC10; /* config cmd */ |
370 | 385 | ||
371 | return par->board->met_wait_event(par); | 386 | return par->board->met_wait_event(par); |
@@ -401,12 +416,9 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) | |||
401 | { | 416 | { |
402 | int res; | 417 | int res; |
403 | 418 | ||
404 | par->board->init_gpio_regs(par); | 419 | res = par->board->setup_io(par); |
405 | 420 | if (res) | |
406 | par->board->init_lcdc_regs(par); | 421 | return res; |
407 | |||
408 | /* now that lcd is setup, setup dma descriptor */ | ||
409 | par->board->post_dma_setup(par); | ||
410 | 422 | ||
411 | res = metronome_powerup_cmd(par); | 423 | res = metronome_powerup_cmd(par); |
412 | if (res) | 424 | if (res) |
@@ -423,16 +435,16 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) | |||
423 | 435 | ||
424 | static void metronomefb_dpy_update(struct metronomefb_par *par) | 436 | static void metronomefb_dpy_update(struct metronomefb_par *par) |
425 | { | 437 | { |
438 | int fbsize; | ||
426 | u16 cksum; | 439 | u16 cksum; |
427 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; | 440 | unsigned char *buf = (unsigned char __force *)par->info->screen_base; |
428 | 441 | ||
442 | fbsize = par->info->fix.smem_len; | ||
429 | /* copy from vm to metromem */ | 443 | /* copy from vm to metromem */ |
430 | memcpy(par->metromem_img, buf, DPY_W*DPY_H); | 444 | memcpy(par->metromem_img, buf, fbsize); |
431 | 445 | ||
432 | cksum = calc_img_cksum((u16 *) par->metromem_img, | 446 | cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2); |
433 | (epd_frame_table[0].fw * DPY_H)/2); | 447 | *((u16 *)(par->metromem_img) + fbsize/2) = cksum; |
434 | *((u16 *)(par->metromem_img) + | ||
435 | (epd_frame_table[0].fw * DPY_H)/2) = cksum; | ||
436 | metronome_display_cmd(par); | 448 | metronome_display_cmd(par); |
437 | } | 449 | } |
438 | 450 | ||
@@ -567,8 +579,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
567 | unsigned char *videomemory; | 579 | unsigned char *videomemory; |
568 | struct metronomefb_par *par; | 580 | struct metronomefb_par *par; |
569 | const struct firmware *fw_entry; | 581 | const struct firmware *fw_entry; |
570 | int cmd_size, wfm_size, img_size, padding_size, totalsize; | ||
571 | int i; | 582 | int i; |
583 | int panel_type; | ||
584 | int fw, fh; | ||
585 | int epd_dt_index; | ||
572 | 586 | ||
573 | /* pick up board specific routines */ | 587 | /* pick up board specific routines */ |
574 | board = dev->dev.platform_data; | 588 | board = dev->dev.platform_data; |
@@ -579,76 +593,88 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
579 | if (!try_module_get(board->owner)) | 593 | if (!try_module_get(board->owner)) |
580 | return -ENODEV; | 594 | return -ENODEV; |
581 | 595 | ||
596 | info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); | ||
597 | if (!info) | ||
598 | goto err; | ||
599 | |||
582 | /* we have two blocks of memory. | 600 | /* we have two blocks of memory. |
583 | info->screen_base which is vm, and is the fb used by apps. | 601 | info->screen_base which is vm, and is the fb used by apps. |
584 | par->metromem which is physically contiguous memory and | 602 | par->metromem which is physically contiguous memory and |
585 | contains the display controller commands, waveform, | 603 | contains the display controller commands, waveform, |
586 | processed image data and padding. this is the data pulled | 604 | processed image data and padding. this is the data pulled |
587 | by the device's LCD controller and pushed to Metronome */ | 605 | by the device's LCD controller and pushed to Metronome. |
606 | the metromem memory is allocated by the board driver and | ||
607 | is provided to us */ | ||
608 | |||
609 | panel_type = board->get_panel_type(); | ||
610 | switch (panel_type) { | ||
611 | case 6: | ||
612 | epd_dt_index = 0; | ||
613 | break; | ||
614 | case 8: | ||
615 | epd_dt_index = 1; | ||
616 | break; | ||
617 | case 97: | ||
618 | epd_dt_index = 2; | ||
619 | break; | ||
620 | default: | ||
621 | dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n"); | ||
622 | epd_dt_index = 0; | ||
623 | break; | ||
624 | } | ||
588 | 625 | ||
589 | videomemorysize = (DPY_W*DPY_H); | 626 | fw = epd_frame_table[epd_dt_index].fw; |
627 | fh = epd_frame_table[epd_dt_index].fh; | ||
628 | |||
629 | /* we need to add a spare page because our csum caching scheme walks | ||
630 | * to the end of the page */ | ||
631 | videomemorysize = PAGE_SIZE + (fw * fh); | ||
590 | videomemory = vmalloc(videomemorysize); | 632 | videomemory = vmalloc(videomemorysize); |
591 | if (!videomemory) | 633 | if (!videomemory) |
592 | return -ENOMEM; | 634 | goto err_fb_rel; |
593 | 635 | ||
594 | memset(videomemory, 0, videomemorysize); | 636 | memset(videomemory, 0, videomemorysize); |
595 | 637 | ||
596 | info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); | ||
597 | if (!info) | ||
598 | goto err_vfree; | ||
599 | |||
600 | info->screen_base = (char __force __iomem *)videomemory; | 638 | info->screen_base = (char __force __iomem *)videomemory; |
601 | info->fbops = &metronomefb_ops; | 639 | info->fbops = &metronomefb_ops; |
602 | 640 | ||
641 | metronomefb_fix.line_length = fw; | ||
642 | metronomefb_var.xres = fw; | ||
643 | metronomefb_var.yres = fh; | ||
644 | metronomefb_var.xres_virtual = fw; | ||
645 | metronomefb_var.yres_virtual = fh; | ||
603 | info->var = metronomefb_var; | 646 | info->var = metronomefb_var; |
604 | info->fix = metronomefb_fix; | 647 | info->fix = metronomefb_fix; |
605 | info->fix.smem_len = videomemorysize; | 648 | info->fix.smem_len = videomemorysize; |
606 | par = info->par; | 649 | par = info->par; |
607 | par->info = info; | 650 | par->info = info; |
608 | par->board = board; | 651 | par->board = board; |
652 | par->dt = epd_dt_index; | ||
609 | init_waitqueue_head(&par->waitq); | 653 | init_waitqueue_head(&par->waitq); |
610 | 654 | ||
611 | /* this table caches per page csum values. */ | 655 | /* this table caches per page csum values. */ |
612 | par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); | 656 | par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); |
613 | if (!par->csum_table) | 657 | if (!par->csum_table) |
658 | goto err_vfree; | ||
659 | |||
660 | /* the physical framebuffer that we use is setup by | ||
661 | * the platform device driver. It will provide us | ||
662 | * with cmd, wfm and image memory in a contiguous area. */ | ||
663 | retval = board->setup_fb(par); | ||
664 | if (retval) { | ||
665 | dev_err(&dev->dev, "Failed to setup fb\n"); | ||
614 | goto err_csum_table; | 666 | goto err_csum_table; |
667 | } | ||
615 | 668 | ||
616 | /* the metromem buffer is divided as follows: | 669 | /* after this point we should have a framebuffer */ |
617 | command | CRC | padding | 670 | if ((!par->metromem_wfm) || (!par->metromem_img) || |
618 | 16kb waveform data | CRC | padding | 671 | (!par->metromem_dma)) { |
619 | image data | CRC | 672 | dev_err(&dev->dev, "fb access failure\n"); |
620 | and an extra 256 bytes for dma descriptors | 673 | retval = -EINVAL; |
621 | eg: IW=832 IH=622 WS=128 | 674 | goto err_csum_table; |
622 | */ | ||
623 | |||
624 | cmd_size = 1 * epd_frame_table[0].fw; | ||
625 | wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1) | ||
626 | / epd_frame_table[0].fw) * epd_frame_table[0].fw; | ||
627 | img_size = epd_frame_table[0].fh * epd_frame_table[0].fw; | ||
628 | padding_size = 4 * epd_frame_table[0].fw; | ||
629 | totalsize = cmd_size + wfm_size + img_size + padding_size; | ||
630 | par->metromemsize = PAGE_ALIGN(totalsize + 256); | ||
631 | DPRINTK("desired memory size = %d\n", par->metromemsize); | ||
632 | dev->dev.coherent_dma_mask = 0xffffffffull; | ||
633 | par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize, | ||
634 | &par->metromem_dma, GFP_KERNEL); | ||
635 | if (!par->metromem) { | ||
636 | printk(KERN_ERR | ||
637 | "metronomefb: unable to allocate dma buffer\n"); | ||
638 | goto err_vfree; | ||
639 | } | 675 | } |
640 | 676 | ||
641 | info->fix.smem_start = par->metromem_dma; | 677 | info->fix.smem_start = par->metromem_dma; |
642 | par->metromem_cmd = (struct metromem_cmd *) par->metromem; | ||
643 | par->metromem_wfm = par->metromem + cmd_size; | ||
644 | par->metromem_img = par->metromem + cmd_size + wfm_size; | ||
645 | par->metromem_img_csum = (u16 *) (par->metromem_img + | ||
646 | (epd_frame_table[0].fw * DPY_H)); | ||
647 | DPRINTK("img offset=0x%x\n", cmd_size + wfm_size); | ||
648 | par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size | ||
649 | + wfm_size + img_size + padding_size); | ||
650 | par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size | ||
651 | + img_size + padding_size; | ||
652 | 678 | ||
653 | /* load the waveform in. assume mode 3, temp 31 for now | 679 | /* load the waveform in. assume mode 3, temp 31 for now |
654 | a) request the waveform file from userspace | 680 | a) request the waveform file from userspace |
@@ -656,7 +682,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
656 | retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); | 682 | retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); |
657 | if (retval < 0) { | 683 | if (retval < 0) { |
658 | dev_err(&dev->dev, "Failed to get waveform\n"); | 684 | dev_err(&dev->dev, "Failed to get waveform\n"); |
659 | goto err_dma_free; | 685 | goto err_csum_table; |
660 | } | 686 | } |
661 | 687 | ||
662 | retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31, | 688 | retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31, |
@@ -664,11 +690,11 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
664 | release_firmware(fw_entry); | 690 | release_firmware(fw_entry); |
665 | if (retval < 0) { | 691 | if (retval < 0) { |
666 | dev_err(&dev->dev, "Failed processing waveform\n"); | 692 | dev_err(&dev->dev, "Failed processing waveform\n"); |
667 | goto err_dma_free; | 693 | goto err_csum_table; |
668 | } | 694 | } |
669 | 695 | ||
670 | if (board->setup_irq(info)) | 696 | if (board->setup_irq(info)) |
671 | goto err_dma_free; | 697 | goto err_csum_table; |
672 | 698 | ||
673 | retval = metronome_init_regs(par); | 699 | retval = metronome_init_regs(par); |
674 | if (retval < 0) | 700 | if (retval < 0) |
@@ -682,7 +708,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
682 | retval = fb_alloc_cmap(&info->cmap, 8, 0); | 708 | retval = fb_alloc_cmap(&info->cmap, 8, 0); |
683 | if (retval < 0) { | 709 | if (retval < 0) { |
684 | dev_err(&dev->dev, "Failed to allocate colormap\n"); | 710 | dev_err(&dev->dev, "Failed to allocate colormap\n"); |
685 | goto err_fb_rel; | 711 | goto err_free_irq; |
686 | } | 712 | } |
687 | 713 | ||
688 | /* set cmap */ | 714 | /* set cmap */ |
@@ -705,17 +731,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev) | |||
705 | 731 | ||
706 | err_cmap: | 732 | err_cmap: |
707 | fb_dealloc_cmap(&info->cmap); | 733 | fb_dealloc_cmap(&info->cmap); |
708 | err_fb_rel: | ||
709 | framebuffer_release(info); | ||
710 | err_free_irq: | 734 | err_free_irq: |
711 | board->free_irq(info); | 735 | board->cleanup(par); |
712 | err_dma_free: | ||
713 | dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem, | ||
714 | par->metromem_dma); | ||
715 | err_csum_table: | 736 | err_csum_table: |
716 | vfree(par->csum_table); | 737 | vfree(par->csum_table); |
717 | err_vfree: | 738 | err_vfree: |
718 | vfree(videomemory); | 739 | vfree(videomemory); |
740 | err_fb_rel: | ||
741 | framebuffer_release(info); | ||
742 | err: | ||
719 | module_put(board->owner); | 743 | module_put(board->owner); |
720 | return retval; | 744 | return retval; |
721 | } | 745 | } |
@@ -726,15 +750,15 @@ static int __devexit metronomefb_remove(struct platform_device *dev) | |||
726 | 750 | ||
727 | if (info) { | 751 | if (info) { |
728 | struct metronomefb_par *par = info->par; | 752 | struct metronomefb_par *par = info->par; |
753 | |||
754 | unregister_framebuffer(info); | ||
729 | fb_deferred_io_cleanup(info); | 755 | fb_deferred_io_cleanup(info); |
730 | dma_free_writecombine(&dev->dev, par->metromemsize, | ||
731 | par->metromem, par->metromem_dma); | ||
732 | fb_dealloc_cmap(&info->cmap); | 756 | fb_dealloc_cmap(&info->cmap); |
757 | par->board->cleanup(par); | ||
733 | vfree(par->csum_table); | 758 | vfree(par->csum_table); |
734 | unregister_framebuffer(info); | ||
735 | vfree((void __force *)info->screen_base); | 759 | vfree((void __force *)info->screen_base); |
736 | par->board->free_irq(info); | ||
737 | module_put(par->board->owner); | 760 | module_put(par->board->owner); |
761 | dev_dbg(&dev->dev, "calling release\n"); | ||
738 | framebuffer_release(info); | 762 | framebuffer_release(info); |
739 | } | 763 | } |
740 | return 0; | 764 | return 0; |
@@ -759,6 +783,9 @@ static void __exit metronomefb_exit(void) | |||
759 | platform_driver_unregister(&metronomefb_driver); | 783 | platform_driver_unregister(&metronomefb_driver); |
760 | } | 784 | } |
761 | 785 | ||
786 | module_param(user_wfm_size, uint, 0); | ||
787 | MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size"); | ||
788 | |||
762 | module_init(metronomefb_init); | 789 | module_init(metronomefb_init); |
763 | module_exit(metronomefb_exit); | 790 | module_exit(metronomefb_exit); |
764 | 791 | ||