linux-stable/drivers/video/fbdev/grvga.c
Uwe Kleine-König 01ecc142ef fbdev: Switch back to struct platform_driver::remove()
After commit 0edb555a65 ("platform: Make platform_driver::remove()
return void") .remove() is (again) the right callback to implement for
platform drivers.

Convert all platform drivers below drivers/video/fbdev to use .remove(),
with the eventual goal to drop struct platform_driver::remove_new(). As
.remove() and .remove_new() have the same prototypes, conversion is done
by just changing the structure member name in the driver initializer.

While touching these files, make indention of the struct initializer
consistent in several files.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Helge Deller <deller@gmx.de>
2024-10-08 21:47:18 +02:00

551 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Aeroflex Gaisler SVGACTRL framebuffer device.
*
* 2011 (c) Aeroflex Gaisler AB
*
* Full documentation of the core can be found here:
* https://www.gaisler.com/products/grlib/grip.pdf
*
* Contributors: Kristoffer Glembo <kristoffer@gaisler.com>
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/io.h>
struct grvga_regs {
u32 status; /* 0x00 */
u32 video_length; /* 0x04 */
u32 front_porch; /* 0x08 */
u32 sync_length; /* 0x0C */
u32 line_length; /* 0x10 */
u32 fb_pos; /* 0x14 */
u32 clk_vector[4]; /* 0x18 */
u32 clut; /* 0x20 */
};
struct grvga_par {
struct grvga_regs *regs;
u32 color_palette[16]; /* 16 entry pseudo palette used by fbcon in true color mode */
int clk_sel;
int fb_alloced; /* = 1 if framebuffer is allocated in main memory */
};
static const struct fb_videomode grvga_modedb[] = {
{
/* 640x480 @ 60 Hz */
NULL, 60, 640, 480, 40000, 48, 16, 39, 11, 96, 2,
0, FB_VMODE_NONINTERLACED
}, {
/* 800x600 @ 60 Hz */
NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
0, FB_VMODE_NONINTERLACED
}, {
/* 800x600 @ 72 Hz */
NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 60 Hz */
NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6,
0, FB_VMODE_NONINTERLACED
}
};
static const struct fb_fix_screeninfo grvga_fix = {
.id = "AG SVGACTRL",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 0,
.ypanstep = 1,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
static int grvga_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct grvga_par *par = info->par;
int i;
if (!var->xres)
var->xres = 1;
if (!var->yres)
var->yres = 1;
if (var->bits_per_pixel <= 8)
var->bits_per_pixel = 8;
else if (var->bits_per_pixel <= 16)
var->bits_per_pixel = 16;
else if (var->bits_per_pixel <= 24)
var->bits_per_pixel = 24;
else if (var->bits_per_pixel <= 32)
var->bits_per_pixel = 32;
else
return -EINVAL;
var->xres_virtual = var->xres;
var->yres_virtual = 2*var->yres;
if (info->fix.smem_len) {
if ((var->yres_virtual*var->xres_virtual*var->bits_per_pixel/8) > info->fix.smem_len)
return -ENOMEM;
}
/* Which clocks that are available can be read out in these registers */
for (i = 0; i <= 3 ; i++) {
if (var->pixclock == par->regs->clk_vector[i])
break;
}
if (i <= 3)
par->clk_sel = i;
else
return -EINVAL;
switch (info->var.bits_per_pixel) {
case 8:
var->red = (struct fb_bitfield) {0, 8, 0}; /* offset, length, msb-right */
var->green = (struct fb_bitfield) {0, 8, 0};
var->blue = (struct fb_bitfield) {0, 8, 0};
var->transp = (struct fb_bitfield) {0, 0, 0};
break;
case 16:
var->red = (struct fb_bitfield) {11, 5, 0};
var->green = (struct fb_bitfield) {5, 6, 0};
var->blue = (struct fb_bitfield) {0, 5, 0};
var->transp = (struct fb_bitfield) {0, 0, 0};
break;
case 24:
case 32:
var->red = (struct fb_bitfield) {16, 8, 0};
var->green = (struct fb_bitfield) {8, 8, 0};
var->blue = (struct fb_bitfield) {0, 8, 0};
var->transp = (struct fb_bitfield) {24, 8, 0};
break;
default:
return -EINVAL;
}
return 0;
}
static int grvga_set_par(struct fb_info *info)
{
u32 func = 0;
struct grvga_par *par = info->par;
__raw_writel(((info->var.yres - 1) << 16) | (info->var.xres - 1),
&par->regs->video_length);
__raw_writel((info->var.lower_margin << 16) | (info->var.right_margin),
&par->regs->front_porch);
__raw_writel((info->var.vsync_len << 16) | (info->var.hsync_len),
&par->regs->sync_length);
__raw_writel(((info->var.yres + info->var.lower_margin + info->var.upper_margin + info->var.vsync_len - 1) << 16) |
(info->var.xres + info->var.right_margin + info->var.left_margin + info->var.hsync_len - 1),
&par->regs->line_length);
switch (info->var.bits_per_pixel) {
case 8:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
func = 1;
break;
case 16:
info->fix.visual = FB_VISUAL_TRUECOLOR;
func = 2;
break;
case 24:
case 32:
info->fix.visual = FB_VISUAL_TRUECOLOR;
func = 3;
break;
default:
return -EINVAL;
}
__raw_writel((par->clk_sel << 6) | (func << 4) | 1,
&par->regs->status);
info->fix.line_length = (info->var.xres_virtual*info->var.bits_per_pixel)/8;
return 0;
}
static int grvga_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info)
{
struct grvga_par *par;
par = info->par;
if (regno >= 256) /* Size of CLUT */
return -EINVAL;
if (info->var.grayscale) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
}
#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
red = CNVT_TOHW(red, info->var.red.length);
green = CNVT_TOHW(green, info->var.green.length);
blue = CNVT_TOHW(blue, info->var.blue.length);
transp = CNVT_TOHW(transp, info->var.transp.length);
#undef CNVT_TOHW
/* In PSEUDOCOLOR we use the hardware CLUT */
if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
__raw_writel((regno << 24) | (red << 16) | (green << 8) | blue,
&par->regs->clut);
/* Truecolor uses the pseudo palette */
else if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v;
if (regno >= 16)
return -EINVAL;
v = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset) |
(transp << info->var.transp.offset);
((u32 *) (info->pseudo_palette))[regno] = v;
}
return 0;
}
static int grvga_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct grvga_par *par = info->par;
struct fb_fix_screeninfo *fix = &info->fix;
u32 base_addr;
if (var->xoffset != 0)
return -EINVAL;
base_addr = fix->smem_start + (var->yoffset * fix->line_length);
base_addr &= ~3UL;
/* Set framebuffer base address */
__raw_writel(base_addr,
&par->regs->fb_pos);
return 0;
}
static const struct fb_ops grvga_ops = {
.owner = THIS_MODULE,
FB_DEFAULT_IOMEM_OPS,
.fb_check_var = grvga_check_var,
.fb_set_par = grvga_set_par,
.fb_setcolreg = grvga_setcolreg,
.fb_pan_display = grvga_pan_display,
};
static int grvga_parse_custom(char *options,
struct fb_var_screeninfo *screendata)
{
char *this_opt;
int count = 0;
if (!options || !*options)
return -1;
while ((this_opt = strsep(&options, " ")) != NULL) {
if (!*this_opt)
continue;
switch (count) {
case 0:
screendata->pixclock = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 1:
screendata->xres = screendata->xres_virtual = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 2:
screendata->right_margin = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 3:
screendata->hsync_len = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 4:
screendata->left_margin = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 5:
screendata->yres = screendata->yres_virtual = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 6:
screendata->lower_margin = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 7:
screendata->vsync_len = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 8:
screendata->upper_margin = simple_strtoul(this_opt, NULL, 0);
count++;
break;
case 9:
screendata->bits_per_pixel = simple_strtoul(this_opt, NULL, 0);
count++;
break;
default:
return -1;
}
}
screendata->activate = FB_ACTIVATE_NOW;
screendata->vmode = FB_VMODE_NONINTERLACED;
return 0;
}
static int grvga_probe(struct platform_device *dev)
{
struct fb_info *info;
int retval = -ENOMEM;
unsigned long virtual_start;
unsigned long grvga_fix_addr = 0;
unsigned long physical_start = 0;
unsigned long grvga_mem_size = 0;
struct grvga_par *par = NULL;
char *options = NULL, *mode_opt = NULL;
info = framebuffer_alloc(sizeof(struct grvga_par), &dev->dev);
if (!info)
return -ENOMEM;
/* Expecting: "grvga: modestring, [addr:<framebuffer physical address>], [size:<framebuffer size>]
*
* If modestring is custom:<custom mode string> we parse the string which then contains all videoparameters
* If address is left out, we allocate memory,
* if size is left out we only allocate enough to support the given mode.
*/
if (fb_get_options("grvga", &options)) {
retval = -ENODEV;
goto free_fb;
}
if (!options || !*options)
options = "640x480-8@60";
while (1) {
char *this_opt = strsep(&options, ",");
if (!this_opt)
break;
if (!strncmp(this_opt, "custom", 6)) {
if (grvga_parse_custom(this_opt, &info->var) < 0) {
dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt);
retval = -EINVAL;
goto free_fb;
}
} else if (!strncmp(this_opt, "addr", 4))
grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
else if (!strncmp(this_opt, "size", 4))
grvga_mem_size = simple_strtoul(this_opt + 5, NULL, 0);
else
mode_opt = this_opt;
}
par = info->par;
info->fbops = &grvga_ops;
info->fix = grvga_fix;
info->pseudo_palette = par->color_palette;
info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
info->fix.smem_len = grvga_mem_size;
if (!devm_request_mem_region(&dev->dev, dev->resource[0].start,
resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
dev_err(&dev->dev, "registers already mapped\n");
retval = -EBUSY;
goto free_fb;
}
par->regs = of_ioremap(&dev->resource[0], 0,
resource_size(&dev->resource[0]),
"grlib-svgactrl regs");
if (!par->regs) {
dev_err(&dev->dev, "failed to map registers\n");
retval = -ENOMEM;
goto free_fb;
}
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0) {
dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");
retval = -ENOMEM;
goto unmap_regs;
}
if (mode_opt) {
retval = fb_find_mode(&info->var, info, mode_opt,
grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
if (!retval || retval == 4) {
retval = -EINVAL;
goto dealloc_cmap;
}
}
if (!grvga_mem_size)
grvga_mem_size = info->var.xres_virtual * info->var.yres_virtual * info->var.bits_per_pixel/8;
if (grvga_fix_addr) {
/* Got framebuffer base address from argument list */
physical_start = grvga_fix_addr;
if (!devm_request_mem_region(&dev->dev, physical_start,
grvga_mem_size, dev->name)) {
dev_err(&dev->dev, "failed to request memory region\n");
retval = -ENOMEM;
goto dealloc_cmap;
}
virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
if (!virtual_start) {
dev_err(&dev->dev, "error mapping framebuffer memory\n");
retval = -ENOMEM;
goto dealloc_cmap;
}
} else { /* Allocate frambuffer memory */
unsigned long page;
virtual_start = (unsigned long) __get_free_pages(GFP_DMA,
get_order(grvga_mem_size));
if (!virtual_start) {
dev_err(&dev->dev,
"unable to allocate framebuffer memory (%lu bytes)\n",
grvga_mem_size);
retval = -ENOMEM;
goto dealloc_cmap;
}
physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
/* Set page reserved so that mmap will work. This is necessary
* since we'll be remapping normal memory.
*/
for (page = virtual_start;
page < PAGE_ALIGN(virtual_start + grvga_mem_size);
page += PAGE_SIZE) {
SetPageReserved(virt_to_page(page));
}
par->fb_alloced = 1;
}
memset((unsigned long *) virtual_start, 0, grvga_mem_size);
info->screen_base = (char __iomem *) virtual_start;
info->fix.smem_start = physical_start;
info->fix.smem_len = grvga_mem_size;
dev_set_drvdata(&dev->dev, info);
dev_info(&dev->dev,
"Aeroflex Gaisler framebuffer device (fb%d), %dx%d-%d, using %luK of video memory @ %p\n",
info->node, info->var.xres, info->var.yres, info->var.bits_per_pixel,
grvga_mem_size >> 10, info->screen_base);
retval = register_framebuffer(info);
if (retval < 0) {
dev_err(&dev->dev, "failed to register framebuffer\n");
goto free_mem;
}
__raw_writel(physical_start, &par->regs->fb_pos);
__raw_writel(__raw_readl(&par->regs->status) | 1, /* Enable framebuffer */
&par->regs->status);
return 0;
free_mem:
if (grvga_fix_addr)
iounmap((void *)virtual_start);
else
kfree((void *)virtual_start);
dealloc_cmap:
fb_dealloc_cmap(&info->cmap);
unmap_regs:
of_iounmap(&dev->resource[0], par->regs,
resource_size(&dev->resource[0]));
free_fb:
framebuffer_release(info);
return retval;
}
static void grvga_remove(struct platform_device *device)
{
struct fb_info *info = dev_get_drvdata(&device->dev);
struct grvga_par *par;
if (info) {
par = info->par;
unregister_framebuffer(info);
fb_dealloc_cmap(&info->cmap);
of_iounmap(&device->resource[0], par->regs,
resource_size(&device->resource[0]));
if (!par->fb_alloced)
iounmap(info->screen_base);
else
kfree((void *)info->screen_base);
framebuffer_release(info);
}
}
static struct of_device_id svgactrl_of_match[] = {
{
.name = "GAISLER_SVGACTRL",
},
{
.name = "01_063",
},
{},
};
MODULE_DEVICE_TABLE(of, svgactrl_of_match);
static struct platform_driver grvga_driver = {
.driver = {
.name = "grlib-svgactrl",
.of_match_table = svgactrl_of_match,
},
.probe = grvga_probe,
.remove = grvga_remove,
};
module_platform_driver(grvga_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aeroflex Gaisler");
MODULE_DESCRIPTION("Aeroflex Gaisler framebuffer device driver");