console / fbdev: fbcon support for bits_per_pixel = 1?

From: Stefan Biereigel
Date: Sat Jun 14 2014 - 05:55:57 EST


CC: linux-console, linux-fbdev

Hello Kernel Developers,
for a university assignment we are developing a frame buffer driver for a monochrome
display. We succeeded so far in making a simple "RAM frame buffer" module, which is needed
as the basis (we don't have read access to the RAM in the LCD).
We are using the system-default fb_ops (fb_sys_{read,write},
sys_{fillrect,copyarea,imageblit), and all is well when we use bits_per_pixel = 8. When we
map a console to the RAM frame buffer (con2fbmap 1 1), do strg-alt-F1, type some
characters there, we can then 'cat /dev/fb1' and plot the ram contents in Matlab
(reshaping the data to be our display geometry first), where our typed characters and the
console appear.

If we however change bits_per_pixel to 1, and divide line_length by 8 (as it should
represent the line length in bytes), this suddenly stops working: echo and cat to fb1 work
as intended: We change our Matlab-Script to interpret every byte as 8 pixels, bytes
written into /dev/fb1 can be read out correctly. If we however map the console to fb1, no
console output can be seen in RAM - it is (seemingly) filled with garbage.

We're using fbcon as the console driver, and the first frame buffer for me is intelfb. I
see, that the bitblitting-calls for every typed character are depth 1 and have correct
geometry, but the whole bitblitting-builtin seems not to work for bits_per_pixel = 1, i
tried it with a simple test image after the initialisation...

Is this behavior intended or are we triggering a bug here (that no one noticed, because
who in the world uses monochrome framebuffers these days..). I can't see any comments that
monochrome consoles should not work with 1 bit per pixel. See the code below if you spot
some errors (ignore some missing error handling and mediocre style for now, please).

Thank you for any help and input.
Best regards,
Stefan Biereigel

8<--- testfb.c

#include <linux/init.h>
#include <linux/module.h>

#include <linux/fb.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/platform_device.h>

#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 64
#define FB_MEM_SIZE (DISPLAY_WIDTH * DISPLAY_HEIGHT)/8

static struct platform_device *testfb_device;

static struct fb_ops fbops = {
.owner = THIS_MODULE,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_sync = NULL,
};

static int __init testfb_probe (struct platform_device *pdev) {
void *fb_mem = NULL;
struct fb_info *fbinfo = NULL;

fb_mem = kzalloc(FB_MEM_SIZE, GFP_KERNEL);

if (!fb_mem) {
pr_err("testfb: memory allocation for framebuffer failed\n");
return(-ENOMEM);
}
else
pr_debug("testfb: allocated framebuffer memory successfully\n");

fbinfo = framebuffer_alloc(0, &pdev->dev);

if (!fbinfo) {
pr_err("testfb: framebuffer_alloc() failed\n");
kfree(fb_mem);
return(-1);
}
else
pr_debug("testfb: framebuffer_alloc was successful\n");

fbinfo->fix.smem_start = (unsigned long) fb_mem;
fbinfo->screen_base = (char __iomem *) fb_mem;
fbinfo->fix.smem_len = FB_MEM_SIZE;
fbinfo->fbops = &fbops;

fbinfo->node = 0; /* ?int */
fbinfo->device = &pdev->dev;
fbinfo->flags = FBINFO_DEFAULT; /* */
fbinfo->var.xres = DISPLAY_WIDTH; /* visible resolution */
fbinfo->var.yres = DISPLAY_HEIGHT; /* visible resolution */
fbinfo->var.xres_virtual = DISPLAY_WIDTH; /* virtual resolution */
fbinfo->var.yres_virtual = DISPLAY_HEIGHT; /* virtual resolution */
fbinfo->var.bits_per_pixel = 1; /* bits per pixel */
fbinfo->var.activate = FB_ACTIVATE_NOW; /* set values immediately (or vbl) */
fbinfo->var.sync = 0; /* ?see FB_SYNC_* */
fbinfo->var.vmode = FB_VMODE_NONINTERLACED; /* non interlaced, see FB_VMODE_* */
fbinfo->var.left_margin = 0;
fbinfo->var.right_margin = 0;
fbinfo->var.upper_margin = 0;
fbinfo->var.lower_margin = 0;
fbinfo->var.red.offset = 0;
fbinfo->var.red.length = fbinfo->var.bits_per_pixel;
fbinfo->var.green = fbinfo->var.red;
fbinfo->var.blue = fbinfo->var.red;
fbinfo->var.grayscale = 1;
strcpy(fbinfo->fix.id, "testfb"); /* identifier, 16 byte */
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; /* pack pixels to avoid overhead */
fbinfo->fix.visual = FB_VISUAL_MONO10; /* Monochr. 1=Black 0=White */
fbinfo->fix.line_length = DISPLAY_WIDTH/8; /* length of a line in bytes */
fbinfo->fix.accel = FB_ACCEL_NONE; /* no hardware accelerator */
fbinfo->fix.xpanstep = 0;
fbinfo->fix.ypanstep = 0;
fbinfo->fix.ywrapstep = 0;

if (register_framebuffer(fbinfo) < 0) {
pr_err("testfb: registering framebuffer failed\n");
kfree((void *) fbinfo->fix.smem_start);
framebuffer_release(fbinfo);
return(-1);
}
else
pr_debug("testfb: registered framebuffer\n");

platform_set_drvdata(pdev, fbinfo);

return 0;
}

static int testfb_remove(struct platform_device *pdev) {
struct fb_info *fbinfo = NULL;

fbinfo = platform_get_drvdata(pdev);

if (!fbinfo) {
pr_err("testfb: unable to get fbinfo from pdev\n");
return(-1);
}

if (unregister_framebuffer(fbinfo) < 0)
pr_err("testfb: unregistering framebuffer failed\n");
else
pr_debug("testfb: unregistered framebuffer\n");

kfree((void *) fbinfo->fix.smem_start);

framebuffer_release(fbinfo);

return 0;
}

static struct platform_driver testfb_driver = {
.probe = testfb_probe,
.remove = testfb_remove,
.driver = {
.name = "testfb",
},
};

static int __init testfb_init(void)
{
int ret;

ret = platform_driver_register(&testfb_driver);
if (!ret) {
testfb_device = platform_device_register_simple("testfb", 0,
NULL, 0);

if (IS_ERR(testfb_device)) {
platform_driver_unregister(&testfb_driver);
ret = PTR_ERR(testfb_device);
} else {
pr_info("testfb: platform_device registered\n");
}

}

pr_info("testfb: module loaded\n");

return 0;
}

static void __exit testfb_exit(void)
{
platform_device_unregister(testfb_device);
platform_driver_unregister(&testfb_driver);

pr_info("testfb: module unloaded\n");
}

module_init(testfb_init);
module_exit(testfb_exit);

MODULE_LICENSE("GPL v2");
MODULE_ALIAS("testfb");
MODULE_AUTHOR("Brian Fonfara");
MODULE_DESCRIPTION("Ein Modul zum Test der Linux Framebuffer-Programmierung");
MODULE_DESCRIPTION("version 0.1");
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/