| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * drivers/video/pvr2fb.c |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 139 | 140 | unsigned char is_doublescan; /* Are scanlines output twice? (doublescan) */ |
|---|
| 140 | 141 | unsigned char is_lowres; /* Is horizontal pixel-doubling enabled? */ |
|---|
| 141 | 142 | |
|---|
| 142 | | - unsigned long mmio_base; /* MMIO base */ |
|---|
| 143 | + void __iomem *mmio_base; /* MMIO base */ |
|---|
| 143 | 144 | u32 palette[16]; |
|---|
| 144 | 145 | } *currentpar; |
|---|
| 145 | 146 | |
|---|
| .. | .. |
|---|
| 192 | 193 | static unsigned int shdma = PVR2_CASCADE_CHAN; |
|---|
| 193 | 194 | static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS; |
|---|
| 194 | 195 | #endif |
|---|
| 195 | | - |
|---|
| 196 | | -static int pvr2fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, |
|---|
| 197 | | - unsigned int transp, struct fb_info *info); |
|---|
| 198 | | -static int pvr2fb_blank(int blank, struct fb_info *info); |
|---|
| 199 | | -static unsigned long get_line_length(int xres_virtual, int bpp); |
|---|
| 200 | | -static void set_color_bitfields(struct fb_var_screeninfo *var); |
|---|
| 201 | | -static int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); |
|---|
| 202 | | -static int pvr2fb_set_par(struct fb_info *info); |
|---|
| 203 | | -static void pvr2_update_display(struct fb_info *info); |
|---|
| 204 | | -static void pvr2_init_display(struct fb_info *info); |
|---|
| 205 | | -static void pvr2_do_blank(void); |
|---|
| 206 | | -static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id); |
|---|
| 207 | | -static int pvr2_init_cable(void); |
|---|
| 208 | | -static int pvr2_get_param(const struct pvr2_params *p, const char *s, |
|---|
| 209 | | - int val, int size); |
|---|
| 210 | | -#ifdef CONFIG_PVR2_DMA |
|---|
| 211 | | -static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, |
|---|
| 212 | | - size_t count, loff_t *ppos); |
|---|
| 213 | | -#endif |
|---|
| 214 | | - |
|---|
| 215 | | -static struct fb_ops pvr2fb_ops = { |
|---|
| 216 | | - .owner = THIS_MODULE, |
|---|
| 217 | | - .fb_setcolreg = pvr2fb_setcolreg, |
|---|
| 218 | | - .fb_blank = pvr2fb_blank, |
|---|
| 219 | | - .fb_check_var = pvr2fb_check_var, |
|---|
| 220 | | - .fb_set_par = pvr2fb_set_par, |
|---|
| 221 | | -#ifdef CONFIG_PVR2_DMA |
|---|
| 222 | | - .fb_write = pvr2fb_write, |
|---|
| 223 | | -#endif |
|---|
| 224 | | - .fb_fillrect = cfb_fillrect, |
|---|
| 225 | | - .fb_copyarea = cfb_copyarea, |
|---|
| 226 | | - .fb_imageblit = cfb_imageblit, |
|---|
| 227 | | -}; |
|---|
| 228 | 196 | |
|---|
| 229 | 197 | static struct fb_videomode pvr2_modedb[] = { |
|---|
| 230 | 198 | /* |
|---|
| .. | .. |
|---|
| 353 | 321 | return 0; |
|---|
| 354 | 322 | } |
|---|
| 355 | 323 | |
|---|
| 324 | +/* |
|---|
| 325 | + * Determine the cable type and initialize the cable output format. Don't do |
|---|
| 326 | + * anything if the cable type has been overidden (via "cable:XX"). |
|---|
| 327 | + */ |
|---|
| 328 | + |
|---|
| 329 | +#define PCTRA ((void __iomem *)0xff80002c) |
|---|
| 330 | +#define PDTRA ((void __iomem *)0xff800030) |
|---|
| 331 | +#define VOUTC ((void __iomem *)0xa0702c00) |
|---|
| 332 | + |
|---|
| 333 | +static int pvr2_init_cable(void) |
|---|
| 334 | +{ |
|---|
| 335 | + if (cable_type < 0) { |
|---|
| 336 | + fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, |
|---|
| 337 | + PCTRA); |
|---|
| 338 | + cable_type = (fb_readw(PDTRA) >> 8) & 3; |
|---|
| 339 | + } |
|---|
| 340 | + |
|---|
| 341 | + /* Now select the output format (either composite or other) */ |
|---|
| 342 | + /* XXX: Save the previous val first, as this reg is also AICA |
|---|
| 343 | + related */ |
|---|
| 344 | + if (cable_type == CT_COMPOSITE) |
|---|
| 345 | + fb_writel(3 << 8, VOUTC); |
|---|
| 346 | + else if (cable_type == CT_RGB) |
|---|
| 347 | + fb_writel(1 << 9, VOUTC); |
|---|
| 348 | + else |
|---|
| 349 | + fb_writel(0, VOUTC); |
|---|
| 350 | + |
|---|
| 351 | + return cable_type; |
|---|
| 352 | +} |
|---|
| 353 | + |
|---|
| 356 | 354 | static int pvr2fb_set_par(struct fb_info *info) |
|---|
| 357 | 355 | { |
|---|
| 358 | 356 | struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; |
|---|
| .. | .. |
|---|
| 460 | 458 | set_color_bitfields(var); |
|---|
| 461 | 459 | |
|---|
| 462 | 460 | if (var->vmode & FB_VMODE_YWRAP) { |
|---|
| 463 | | - if (var->xoffset || var->yoffset < 0 || |
|---|
| 464 | | - var->yoffset >= var->yres_virtual) { |
|---|
| 461 | + if (var->xoffset || var->yoffset >= var->yres_virtual) { |
|---|
| 465 | 462 | var->xoffset = var->yoffset = 0; |
|---|
| 466 | 463 | } else { |
|---|
| 467 | 464 | if (var->xoffset > var->xres_virtual - var->xres || |
|---|
| 468 | | - var->yoffset > var->yres_virtual - var->yres || |
|---|
| 469 | | - var->xoffset < 0 || var->yoffset < 0) |
|---|
| 465 | + var->yoffset > var->yres_virtual - var->yres) |
|---|
| 470 | 466 | var->xoffset = var->yoffset = 0; |
|---|
| 471 | 467 | } |
|---|
| 472 | 468 | } else { |
|---|
| .. | .. |
|---|
| 622 | 618 | is_blanked = do_blank > 0 ? do_blank : 0; |
|---|
| 623 | 619 | } |
|---|
| 624 | 620 | |
|---|
| 625 | | -static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id) |
|---|
| 621 | +static irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id) |
|---|
| 626 | 622 | { |
|---|
| 627 | 623 | struct fb_info *info = dev_id; |
|---|
| 628 | 624 | |
|---|
| .. | .. |
|---|
| 641 | 637 | return IRQ_HANDLED; |
|---|
| 642 | 638 | } |
|---|
| 643 | 639 | |
|---|
| 644 | | -/* |
|---|
| 645 | | - * Determine the cable type and initialize the cable output format. Don't do |
|---|
| 646 | | - * anything if the cable type has been overidden (via "cable:XX"). |
|---|
| 647 | | - */ |
|---|
| 648 | | - |
|---|
| 649 | | -#define PCTRA 0xff80002c |
|---|
| 650 | | -#define PDTRA 0xff800030 |
|---|
| 651 | | -#define VOUTC 0xa0702c00 |
|---|
| 652 | | - |
|---|
| 653 | | -static int pvr2_init_cable(void) |
|---|
| 654 | | -{ |
|---|
| 655 | | - if (cable_type < 0) { |
|---|
| 656 | | - fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, |
|---|
| 657 | | - PCTRA); |
|---|
| 658 | | - cable_type = (fb_readw(PDTRA) >> 8) & 3; |
|---|
| 659 | | - } |
|---|
| 660 | | - |
|---|
| 661 | | - /* Now select the output format (either composite or other) */ |
|---|
| 662 | | - /* XXX: Save the previous val first, as this reg is also AICA |
|---|
| 663 | | - related */ |
|---|
| 664 | | - if (cable_type == CT_COMPOSITE) |
|---|
| 665 | | - fb_writel(3 << 8, VOUTC); |
|---|
| 666 | | - else if (cable_type == CT_RGB) |
|---|
| 667 | | - fb_writel(1 << 9, VOUTC); |
|---|
| 668 | | - else |
|---|
| 669 | | - fb_writel(0, VOUTC); |
|---|
| 670 | | - |
|---|
| 671 | | - return cable_type; |
|---|
| 672 | | -} |
|---|
| 673 | | - |
|---|
| 674 | 640 | #ifdef CONFIG_PVR2_DMA |
|---|
| 675 | 641 | static ssize_t pvr2fb_write(struct fb_info *info, const char *buf, |
|---|
| 676 | 642 | size_t count, loff_t *ppos) |
|---|
| .. | .. |
|---|
| 686 | 652 | if (!pages) |
|---|
| 687 | 653 | return -ENOMEM; |
|---|
| 688 | 654 | |
|---|
| 689 | | - ret = get_user_pages_fast((unsigned long)buf, nr_pages, true, pages); |
|---|
| 655 | + ret = pin_user_pages_fast((unsigned long)buf, nr_pages, FOLL_WRITE, pages); |
|---|
| 690 | 656 | if (ret < nr_pages) { |
|---|
| 691 | | - nr_pages = ret; |
|---|
| 692 | | - ret = -EINVAL; |
|---|
| 657 | + if (ret < 0) { |
|---|
| 658 | + /* |
|---|
| 659 | + * Clamp the unsigned nr_pages to zero so that the |
|---|
| 660 | + * error handling works. And leave ret at whatever |
|---|
| 661 | + * -errno value was returned from GUP. |
|---|
| 662 | + */ |
|---|
| 663 | + nr_pages = 0; |
|---|
| 664 | + } else { |
|---|
| 665 | + nr_pages = ret; |
|---|
| 666 | + /* |
|---|
| 667 | + * Use -EINVAL to represent a mildly desperate guess at |
|---|
| 668 | + * why we got fewer pages (maybe even zero pages) than |
|---|
| 669 | + * requested. |
|---|
| 670 | + */ |
|---|
| 671 | + ret = -EINVAL; |
|---|
| 672 | + } |
|---|
| 693 | 673 | goto out_unmap; |
|---|
| 694 | 674 | } |
|---|
| 695 | 675 | |
|---|
| .. | .. |
|---|
| 732 | 712 | ret = count; |
|---|
| 733 | 713 | |
|---|
| 734 | 714 | out_unmap: |
|---|
| 735 | | - for (i = 0; i < nr_pages; i++) |
|---|
| 736 | | - put_page(pages[i]); |
|---|
| 737 | | - |
|---|
| 715 | + unpin_user_pages(pages, nr_pages); |
|---|
| 738 | 716 | kfree(pages); |
|---|
| 739 | 717 | |
|---|
| 740 | 718 | return ret; |
|---|
| 741 | 719 | } |
|---|
| 742 | 720 | #endif /* CONFIG_PVR2_DMA */ |
|---|
| 721 | + |
|---|
| 722 | +static const struct fb_ops pvr2fb_ops = { |
|---|
| 723 | + .owner = THIS_MODULE, |
|---|
| 724 | + .fb_setcolreg = pvr2fb_setcolreg, |
|---|
| 725 | + .fb_blank = pvr2fb_blank, |
|---|
| 726 | + .fb_check_var = pvr2fb_check_var, |
|---|
| 727 | + .fb_set_par = pvr2fb_set_par, |
|---|
| 728 | +#ifdef CONFIG_PVR2_DMA |
|---|
| 729 | + .fb_write = pvr2fb_write, |
|---|
| 730 | +#endif |
|---|
| 731 | + .fb_fillrect = cfb_fillrect, |
|---|
| 732 | + .fb_copyarea = cfb_copyarea, |
|---|
| 733 | + .fb_imageblit = cfb_imageblit, |
|---|
| 734 | +}; |
|---|
| 735 | + |
|---|
| 736 | +#ifndef MODULE |
|---|
| 737 | +static int pvr2_get_param_val(const struct pvr2_params *p, const char *s, |
|---|
| 738 | + int size) |
|---|
| 739 | +{ |
|---|
| 740 | + int i; |
|---|
| 741 | + |
|---|
| 742 | + for (i = 0; i < size; i++) { |
|---|
| 743 | + if (!strncasecmp(p[i].name, s, strlen(s))) |
|---|
| 744 | + return p[i].val; |
|---|
| 745 | + } |
|---|
| 746 | + return -1; |
|---|
| 747 | +} |
|---|
| 748 | +#endif |
|---|
| 749 | + |
|---|
| 750 | +static char *pvr2_get_param_name(const struct pvr2_params *p, int val, |
|---|
| 751 | + int size) |
|---|
| 752 | +{ |
|---|
| 753 | + int i; |
|---|
| 754 | + |
|---|
| 755 | + for (i = 0; i < size; i++) { |
|---|
| 756 | + if (p[i].val == val) |
|---|
| 757 | + return p[i].name; |
|---|
| 758 | + } |
|---|
| 759 | + return NULL; |
|---|
| 760 | +} |
|---|
| 743 | 761 | |
|---|
| 744 | 762 | /** |
|---|
| 745 | 763 | * pvr2fb_common_init |
|---|
| .. | .. |
|---|
| 759 | 777 | * in for flexibility anyways. Who knows, maybe someone has tv-out on a |
|---|
| 760 | 778 | * PCI-based version of these things ;-) |
|---|
| 761 | 779 | */ |
|---|
| 762 | | -static int pvr2fb_common_init(void) |
|---|
| 780 | +static int __maybe_unused pvr2fb_common_init(void) |
|---|
| 763 | 781 | { |
|---|
| 764 | 782 | struct pvr2fb_par *par = currentpar; |
|---|
| 765 | 783 | unsigned long modememused, rev; |
|---|
| 766 | 784 | |
|---|
| 767 | | - fb_info->screen_base = ioremap_nocache(pvr2_fix.smem_start, |
|---|
| 785 | + fb_info->screen_base = ioremap(pvr2_fix.smem_start, |
|---|
| 768 | 786 | pvr2_fix.smem_len); |
|---|
| 769 | 787 | |
|---|
| 770 | 788 | if (!fb_info->screen_base) { |
|---|
| .. | .. |
|---|
| 772 | 790 | goto out_err; |
|---|
| 773 | 791 | } |
|---|
| 774 | 792 | |
|---|
| 775 | | - par->mmio_base = (unsigned long)ioremap_nocache(pvr2_fix.mmio_start, |
|---|
| 776 | | - pvr2_fix.mmio_len); |
|---|
| 793 | + par->mmio_base = ioremap(pvr2_fix.mmio_start, |
|---|
| 794 | + pvr2_fix.mmio_len); |
|---|
| 777 | 795 | if (!par->mmio_base) { |
|---|
| 778 | 796 | printk(KERN_ERR "pvr2fb: Failed to remap mmio space\n"); |
|---|
| 779 | 797 | goto out_err; |
|---|
| .. | .. |
|---|
| 821 | 839 | fb_info->var.xres, fb_info->var.yres, |
|---|
| 822 | 840 | fb_info->var.bits_per_pixel, |
|---|
| 823 | 841 | get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel), |
|---|
| 824 | | - (char *)pvr2_get_param(cables, NULL, cable_type, 3), |
|---|
| 825 | | - (char *)pvr2_get_param(outputs, NULL, video_output, 3)); |
|---|
| 842 | + pvr2_get_param_name(cables, cable_type, 3), |
|---|
| 843 | + pvr2_get_param_name(outputs, video_output, 3)); |
|---|
| 826 | 844 | |
|---|
| 827 | 845 | #ifdef CONFIG_SH_STORE_QUEUES |
|---|
| 828 | 846 | fb_notice(fb_info, "registering with SQ API\n"); |
|---|
| .. | .. |
|---|
| 840 | 858 | if (fb_info->screen_base) |
|---|
| 841 | 859 | iounmap(fb_info->screen_base); |
|---|
| 842 | 860 | if (par->mmio_base) |
|---|
| 843 | | - iounmap((void *)par->mmio_base); |
|---|
| 861 | + iounmap(par->mmio_base); |
|---|
| 844 | 862 | |
|---|
| 845 | 863 | return -ENXIO; |
|---|
| 846 | 864 | } |
|---|
| .. | .. |
|---|
| 900 | 918 | return pvr2fb_common_init(); |
|---|
| 901 | 919 | } |
|---|
| 902 | 920 | |
|---|
| 903 | | -static void __exit pvr2fb_dc_exit(void) |
|---|
| 921 | +static void pvr2fb_dc_exit(void) |
|---|
| 904 | 922 | { |
|---|
| 905 | 923 | if (fb_info->screen_base) { |
|---|
| 906 | 924 | iounmap(fb_info->screen_base); |
|---|
| 907 | 925 | fb_info->screen_base = NULL; |
|---|
| 908 | 926 | } |
|---|
| 909 | 927 | if (currentpar->mmio_base) { |
|---|
| 910 | | - iounmap((void *)currentpar->mmio_base); |
|---|
| 911 | | - currentpar->mmio_base = 0; |
|---|
| 928 | + iounmap(currentpar->mmio_base); |
|---|
| 929 | + currentpar->mmio_base = NULL; |
|---|
| 912 | 930 | } |
|---|
| 913 | 931 | |
|---|
| 914 | 932 | free_irq(HW_EVENT_VSYNC, fb_info); |
|---|
| .. | .. |
|---|
| 957 | 975 | fb_info->screen_base = NULL; |
|---|
| 958 | 976 | } |
|---|
| 959 | 977 | if (currentpar->mmio_base) { |
|---|
| 960 | | - iounmap((void *)currentpar->mmio_base); |
|---|
| 961 | | - currentpar->mmio_base = 0; |
|---|
| 978 | + iounmap(currentpar->mmio_base); |
|---|
| 979 | + currentpar->mmio_base = NULL; |
|---|
| 962 | 980 | } |
|---|
| 963 | 981 | |
|---|
| 964 | 982 | pci_release_regions(pdev); |
|---|
| .. | .. |
|---|
| 984 | 1002 | return pci_register_driver(&pvr2fb_pci_driver); |
|---|
| 985 | 1003 | } |
|---|
| 986 | 1004 | |
|---|
| 987 | | -static void __exit pvr2fb_pci_exit(void) |
|---|
| 1005 | +static void pvr2fb_pci_exit(void) |
|---|
| 988 | 1006 | { |
|---|
| 989 | 1007 | pci_unregister_driver(&pvr2fb_pci_driver); |
|---|
| 990 | 1008 | } |
|---|
| 991 | 1009 | #endif /* CONFIG_PCI */ |
|---|
| 992 | | - |
|---|
| 993 | | -static int pvr2_get_param(const struct pvr2_params *p, const char *s, int val, |
|---|
| 994 | | - int size) |
|---|
| 995 | | -{ |
|---|
| 996 | | - int i; |
|---|
| 997 | | - |
|---|
| 998 | | - for (i = 0 ; i < size ; i++ ) { |
|---|
| 999 | | - if (s != NULL) { |
|---|
| 1000 | | - if (!strncasecmp(p[i].name, s, strlen(s))) |
|---|
| 1001 | | - return p[i].val; |
|---|
| 1002 | | - } else { |
|---|
| 1003 | | - if (p[i].val == val) |
|---|
| 1004 | | - return (int)p[i].name; |
|---|
| 1005 | | - } |
|---|
| 1006 | | - } |
|---|
| 1007 | | - return -1; |
|---|
| 1008 | | -} |
|---|
| 1009 | 1010 | |
|---|
| 1010 | 1011 | /* |
|---|
| 1011 | 1012 | * Parse command arguments. Supported arguments are: |
|---|
| .. | .. |
|---|
| 1048 | 1049 | } |
|---|
| 1049 | 1050 | |
|---|
| 1050 | 1051 | if (*cable_arg) |
|---|
| 1051 | | - cable_type = pvr2_get_param(cables, cable_arg, 0, 3); |
|---|
| 1052 | + cable_type = pvr2_get_param_val(cables, cable_arg, 3); |
|---|
| 1052 | 1053 | if (*output_arg) |
|---|
| 1053 | | - video_output = pvr2_get_param(outputs, output_arg, 0, 3); |
|---|
| 1054 | + video_output = pvr2_get_param_val(outputs, output_arg, 3); |
|---|
| 1054 | 1055 | |
|---|
| 1055 | 1056 | return 0; |
|---|
| 1056 | 1057 | } |
|---|
| .. | .. |
|---|
| 1073 | 1074 | static int __init pvr2fb_init(void) |
|---|
| 1074 | 1075 | { |
|---|
| 1075 | 1076 | int i, ret = -ENODEV; |
|---|
| 1076 | | - int size; |
|---|
| 1077 | 1077 | |
|---|
| 1078 | 1078 | #ifndef MODULE |
|---|
| 1079 | 1079 | char *option = NULL; |
|---|
| .. | .. |
|---|
| 1082 | 1082 | return -ENODEV; |
|---|
| 1083 | 1083 | pvr2fb_setup(option); |
|---|
| 1084 | 1084 | #endif |
|---|
| 1085 | | - size = sizeof(struct fb_info) + sizeof(struct pvr2fb_par) + 16 * sizeof(u32); |
|---|
| 1086 | 1085 | |
|---|
| 1087 | 1086 | fb_info = framebuffer_alloc(sizeof(struct pvr2fb_par), NULL); |
|---|
| 1088 | | - |
|---|
| 1089 | | - if (!fb_info) { |
|---|
| 1090 | | - printk(KERN_ERR "Failed to allocate memory for fb_info\n"); |
|---|
| 1087 | + if (!fb_info) |
|---|
| 1091 | 1088 | return -ENOMEM; |
|---|
| 1092 | | - } |
|---|
| 1093 | | - |
|---|
| 1094 | 1089 | |
|---|
| 1095 | 1090 | currentpar = fb_info->par; |
|---|
| 1096 | 1091 | |
|---|