I'm aware of the drm-fbdev resolution, but CONFIG_DRM adds 100kb, which is a complete blocker on a system with 8mb RAM. Signed-off-by: Lauri Kasanen <cand@xxxxxxx> --- arch/mips/n64/init.c | 10 +++ drivers/video/fbdev/Kconfig | 9 ++ drivers/video/fbdev/Makefile | 1 + drivers/video/fbdev/n64rdp.c | 190 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 drivers/video/fbdev/n64rdp.c diff --git a/arch/mips/n64/init.c b/arch/mips/n64/init.c index 6fb622d..635e9ef 100644 --- a/arch/mips/n64/init.c +++ b/arch/mips/n64/init.c @@ -8,6 +8,7 @@ #include <linux/ioport.h> #include <linux/irq.h> #include <linux/memblock.h> +#include <linux/platform_device.h> #include <linux/string.h> #include <asm/bootinfo.h> @@ -46,6 +47,15 @@ void __init prom_free_prom_memory(void) { } +static int __init n64_platform_init(void) +{ + platform_device_register_simple("n64rdp", -1, NULL, 0); + + return 0; +} + +arch_initcall(n64_platform_init); + void __init plat_mem_setup(void) { iomem_resource_init(); diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index cfb7f56..4dde2c7 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2206,6 +2206,15 @@ config FB_SIMPLE Configuration re: surface address, size, and format must be provided through device tree, or plain old platform data. +config FB_N64RDP + bool "Nintendo 64 RDP support" + depends on (FB = y) && MIPS_N64 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + Driver for the N64's display. + config FB_SSD1307 tristate "Solomon SSD1307 framebuffer support" depends on FB && I2C diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 477b962..86f1e22 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -129,6 +129,7 @@ obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o obj-$(CONFIG_FB_SIMPLE) += simplefb.o +obj-$(CONFIG_FB_N64RDP) += n64rdp.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/fbdev/n64rdp.c b/drivers/video/fbdev/n64rdp.c new file mode 100644 index 0000000..e5456b6 --- /dev/null +++ b/drivers/video/fbdev/n64rdp.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for the N64's RDP + * + * Copyright (c) 2020 Lauri Kasanen + * + * Based on simplefb.c, which was: + * Copyright (c) 2013, Stephen Warren + * + * Based on q40fb.c, which was: + * Copyright (C) 2001 Richard Zidlicky <rz@xxxxxxxxxxxxxx> + * + * Also based on offb.c, which was: + * Copyright (C) 1997 Geert Uytterhoeven + * Copyright (C) 1996 Paul Mackerras + */ + +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/parser.h> + +#include <asm/addrspace.h> + +static const struct fb_fix_screeninfo n64rdp_fix = { + .id = "default", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .accel = FB_ACCEL_NONE, +}; + +static const struct fb_var_screeninfo n64rdp_var = { + .height = -1, + .width = -1, + .activate = FB_ACTIVATE_NOW, + .vmode = FB_VMODE_NONINTERLACED, +}; + +#define PSEUDO_PALETTE_SIZE 16 + +static int n64rdp_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + u32 *pal = info->pseudo_palette; + u32 cr = red >> (16 - info->var.red.length); + u32 cg = green >> (16 - info->var.green.length); + u32 cb = blue >> (16 - info->var.blue.length); + u32 value; + + if (regno >= PSEUDO_PALETTE_SIZE) + return -EINVAL; + + value = (cr << info->var.red.offset) | + (cg << info->var.green.offset) | + (cb << info->var.blue.offset); + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; + value |= mask; + } + pal[regno] = value; + + return 0; +} + +static const struct fb_ops n64rdp_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = n64rdp_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +struct n64rdp_par { + u32 palette[PSEUDO_PALETTE_SIZE]; + dma_addr_t physaddr; +}; + +#define REG_BASE ((u32 *) CKSEG1ADDR(0xA4400000)) + +static void n64rdp_write_reg(const u8 reg, const u32 value) +{ + __raw_writel(value, REG_BASE + reg); +} + +#define W 320 +#define H 240 + +static const u32 ntsc_320[] __initconst = { + 0x00013212, 0x00000000, 0x00000140, 0x00000200, + 0x00000000, 0x03e52239, 0x0000020d, 0x00000c15, + 0x0c150c15, 0x006c02ec, 0x002501ff, 0x000e0204, + 0x00000200, 0x00000400 +}; + +static int __init n64rdp_probe(struct platform_device *pdev) +{ + int ret; + u32 i; + struct fb_info *info; + struct n64rdp_par *par; + dma_addr_t addr; + + info = framebuffer_alloc(sizeof(struct n64rdp_par), &pdev->dev); + if (!info) + return -ENOMEM; + platform_set_drvdata(pdev, info); + + par = info->par; + + info->fix = n64rdp_fix; + info->screen_base = dma_alloc_coherent(&pdev->dev, W * H * 2, &addr, + GFP_DMA | GFP_KERNEL); + if (!info->screen_base) + return -ENOMEM; + + info->fix.smem_start = par->physaddr = addr; + info->fix.smem_len = W * H * 2; + info->fix.line_length = W * 2; + + info->var = n64rdp_var; + info->var.xres = W; + info->var.yres = H; + info->var.xres_virtual = W; + info->var.yres_virtual = H; + info->var.bits_per_pixel = 16; + info->var.red = (struct fb_bitfield) {11, 5}; + info->var.green = (struct fb_bitfield) {6, 5}; + info->var.blue = (struct fb_bitfield) {1, 5}; + info->var.transp = (struct fb_bitfield) {0, 1}; + + info->apertures = alloc_apertures(1); + if (!info->apertures) { + ret = -ENOMEM; + goto error_fb_release; + } + info->apertures->ranges[0].base = info->fix.smem_start; + info->apertures->ranges[0].size = info->fix.smem_len; + + info->fbops = &n64rdp_ops; + info->flags = FBINFO_DEFAULT; + info->pseudo_palette = par->palette; + + dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", + info->fix.smem_start, info->fix.smem_len, + info->screen_base); + + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to register n64rdp: %d\n", ret); + goto error_fb_release; + } + + for (i = 0; i < ARRAY_SIZE(ntsc_320); i++) { + if (i == 1) + n64rdp_write_reg(i, par->physaddr); + else + n64rdp_write_reg(i, ntsc_320[i]); + } + + return 0; + +error_fb_release: + framebuffer_release(info); + return ret; +} + +static struct platform_driver n64rdp_driver = { + .driver = { + .name = "n64rdp", + }, +}; + +static int __init n64rdp_init(void) +{ + int ret; + + ret = platform_driver_probe(&n64rdp_driver, n64rdp_probe); + + return ret; +} + +fs_initcall(n64rdp_init); + +MODULE_AUTHOR("Lauri Kasanen <cand@xxxxxxx>"); +MODULE_DESCRIPTION("Driver for the N64's display"); +MODULE_LICENSE("GPL v2"); -- 2.6.2