Re: [PATCH 00/03][RFC] Reusable UIO Platform Driver

From: Magnus Damm
Date: Wed May 21 2008 - 04:23:26 EST


On Wed, May 21, 2008 at 5:05 PM, Uwe Kleine-König
<Uwe.Kleine-Koenig@xxxxxxxx> wrote:
> @Magnus: Maybe you can provide the userspace part of the driver?
> How is that mapping used there?

[Added Matsubara-san as CC]

Sure, here is a little test program. Have a look at "uio_mem". The
"address" member contains the physical address that can be used for
bus mastering DMA. Compare that to "iomem" which is the pointer to the
virtual memory area in user space.

Hope this helps!

/ magnus
/*
* sh7722 VEU scaling example using UIO - 20080521 Magnus Damm
*
* Derived from
* "Simple tick timer through Sky TMU UIO driver" by Katsuya Matsubara
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fb.h>

static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) {
FILE *fp;

if ((fp = fopen(fname, "r")) != NULL) {
fgets(buf, maxlen, fp);
fclose(fp);
return strlen(buf);
} else {
return -1;
}
}

struct uio_device {
char *name;
char *path;
int fd;
};

#define MAXUIOIDS 100
#define MAXNAMELEN 256

static int locate_uio_device(char *name, struct uio_device *udp)
{
char fname[MAXNAMELEN], buf[MAXNAMELEN];
int uio_id;

for (uio_id = 0; uio_id < MAXUIOIDS; uio_id++) {
sprintf(fname, "/sys/class/uio/uio%d/name", uio_id);
if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0)
continue;
if (strncmp(name, buf, strlen(name)) == 0)
break;
}

if (uio_id >= MAXUIOIDS)
return -1;

udp->name = strdup(buf);
udp->path = strdup(fname);
udp->path[strlen(udp->path) - 4] = '\0';

sprintf(buf, "/dev/uio%d", uio_id);
udp->fd = open(buf, O_RDWR|O_SYNC);

if (udp->fd < 0) {
perror("open");
return -1;
}

return 0;
}

struct uio_map {
unsigned long address;
unsigned long size;
void *iomem;
};

static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump)
{
char fname[MAXNAMELEN], buf[MAXNAMELEN];

sprintf(fname, "%s/maps/map%d/addr", udp->path, nr);
if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0)
return -1;

ump->address = strtoul(buf, NULL, 0);

sprintf(fname, "%s/maps/map%d/size", udp->path, nr);
if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0)
return -1;

ump->size = strtoul(buf, NULL, 0);

ump->iomem = mmap(0, ump->size,
PROT_READ|PROT_WRITE, MAP_SHARED,
udp->fd, nr * getpagesize());

if (ump->iomem == MAP_FAILED)
return -1;

return 0;
}

struct fb_info {
unsigned long width;
unsigned long height;
unsigned long bpp;
unsigned long line_length;

unsigned long address;
unsigned long size;
};

static int get_fb_info(char *device, struct fb_info *fip)
{
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
int fd;

fd = open(device, O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}

if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
perror("ioctl(FBIOGET_VSCREENINFO)");
return -1;
}

fip->width = vinfo.xres;
fip->height = vinfo.yres;
fip->bpp = vinfo.bits_per_pixel;

if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
perror("ioctl(FBIOGET_FSCREENINFO)");
return -1;
}

fip->address = finfo.smem_start;
fip->size = finfo.smem_len;
fip->line_length = finfo.line_length;

close(fd);
return 0;
}

#define VESTR 0x00 /* start register */
#define VESWR 0x10 /* src: line length */
#define VESSR 0x14 /* src: image size */
#define VSAYR 0x18 /* src: y/rgb plane address */
#define VSACR 0x1c /* src: c plane address */
#define VBSSR 0x20 /* bundle mode register */
#define VEDWR 0x30 /* dst: line length */
#define VDAYR 0x34 /* dst: y/rgb plane address */
#define VDACR 0x38 /* dst: c plane address */
#define VTRCR 0x50 /* transform control */
#define VRFCR 0x54 /* resize scale */
#define VRFSR 0x58 /* resize clip */
#define VENHR 0x5c /* enhance */
#define VFMCR 0x70 /* filter mode */
#define VVTCR 0x74 /* lowpass vertical */
#define VHTCR 0x78 /* lowpass horizontal */
#define VAPCR 0x80 /* color match */
#define VECCR 0x84 /* color replace */
#define VAFXR 0x90 /* fixed mode */
#define VSWPR 0x94 /* swap */
#define VEIER 0xa0 /* interrupt mask */
#define VEVTR 0xa4 /* interrupt event */
#define VSTAR 0xb0 /* status */
#define VBSRR 0xb4 /* reset */

static unsigned long read_reg(struct uio_map *ump, int reg_nr)
{
volatile unsigned long *reg = ump->iomem + reg_nr;

return *reg;
}

static void write_reg(struct uio_map *ump, unsigned long value, int reg_nr)
{
volatile unsigned long *reg = ump->iomem + reg_nr;

*reg = value;
}

static void set_scale(struct uio_map *ump, int vertical,
int size_in, int size_out)
{
unsigned long fixpoint, mant, frac, value;

/* calculate FRAC and MANT */

fixpoint = (4096 * (size_in - 1)) / (size_out + 1);
mant = fixpoint / 4096;
frac = fixpoint - (mant * 4096);

if (frac & 0x07) {
frac &= ~0x07;

if (size_out > size_in)
frac -= 8; /* round down if scaling up */
else
frac += 8; /* round up if scaling down */
}

/* set scale */

value = read_reg(ump, VRFCR);

if (vertical) {
value &= ~0xffff0000;
value |= ((mant << 12) | frac) << 16;
}
else {
value &= ~0xffff;
value |= (mant << 12) | frac;
}

write_reg(ump, value, VRFCR);

/* set clip */

value = read_reg(ump, VRFSR);

if (vertical) {
value &= ~0xffff0000;
value |= size_out << 16;
}
else {
value &= ~0xffff;
value |= size_out;
}

write_reg(ump, value, VRFSR);
}

static int foo(void)
{
struct fb_info fbi;
struct uio_device uio_dev;
struct uio_map uio_mmio, uio_mem;
unsigned long addr;
int src_w, src_h, src_bpp;
int dst_w, dst_h;
int ret;

ret = get_fb_info("/dev/fb0", &fbi);
if (ret < 0)
return ret;

printf("using %lux%lu %ld bpp framebuffer at 0x%08lx (0x%08lx).\n",
fbi.width, fbi.height, fbi.bpp, fbi.address, fbi.size);

ret = locate_uio_device("VEU", &uio_dev);
if (ret < 0)
return ret;

printf("found matching UIO device at %s\n", uio_dev.path);

ret = setup_uio_map(&uio_dev, 0, &uio_mmio);
if (ret < 0)
return ret;

ret = setup_uio_map(&uio_dev, 1, &uio_mem);
if (ret < 0)
return ret;

printf("VSTAR = 0x%lx\n", read_reg(&uio_mmio, VSTAR));
printf("VEVTR = 0x%lx\n", read_reg(&uio_mmio, VEVTR));

/* reset VEU */

printf("VBSRR = 0x%lx\n", read_reg(&uio_mmio, VBSRR));
write_reg(&uio_mmio, 0x100, VBSRR);
printf("VBSRR(2) = 0x%lx\n", read_reg(&uio_mmio, VBSRR));

/* destination: get from fb */

dst_w = fbi.width;
dst_h = fbi.height;

/* source bitmap */

src_w = 32;
src_h = 32;
src_bpp = 16;

new_color:

{
struct timeval tv;
int k;
unsigned char *buf = uio_mem.iomem;

gettimeofday(&tv, NULL);

for (k = 0; k < (src_w * src_h * (src_bpp / 8)); k++)
buf[k] = k ^ (tv.tv_sec + tv.tv_usec);
}

addr = fbi.address;
addr += (fbi.bpp / 8) * ((fbi.width - dst_w) / 2);
addr += fbi.line_length * ((fbi.height - dst_h) / 2);

again:
write_reg(&uio_mmio, src_w * (src_bpp / 8), VESWR);
write_reg(&uio_mmio, src_w | (src_h << 16), VESSR);
write_reg(&uio_mmio, uio_mem.address, VSAYR);
write_reg(&uio_mmio, 0, VSACR); /* unused in case of RGB */
write_reg(&uio_mmio, 0, VBSSR); /* not using bundle mode */

write_reg(&uio_mmio, fbi.line_length, VEDWR);
write_reg(&uio_mmio, addr, VDAYR);
write_reg(&uio_mmio, 0, VDACR); /* unused in case of RGB */

write_reg(&uio_mmio, (6 << 16) | (3 << 8) | 1, VTRCR);

set_scale(&uio_mmio, 0, src_w, dst_w);
set_scale(&uio_mmio, 1, src_h, dst_h);

write_reg(&uio_mmio, 1, VEIER); /* enable interrupt */
write_reg(&uio_mmio, 1, VESTR);

/* Wait for an interrupt */
{
unsigned long n_pending;
read(uio_dev.fd, &n_pending, sizeof(u_long));
}

write_reg(&uio_mmio, 0x100, VEVTR); /* ack int, write 0 to bit 0 */

goto again;
goto new_color;
}

int main(int argc, char ** argv)
{
foo();

return 0;
}