320 lines
9.6 KiB
C
320 lines
9.6 KiB
C
/****************************************************************************
|
|
* drivers/video/goldfish_fb.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <debug.h>
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/video/fb.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor definitions
|
|
****************************************************************************/
|
|
|
|
#ifndef putreg32
|
|
#define putreg32(v, x) (*(volatile uint32_t *)(x) = (v))
|
|
#endif
|
|
|
|
#ifndef getreg32
|
|
#define getreg32(x) (*(uint32_t *)(x))
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
enum
|
|
{
|
|
GOLDFISH_FB_GET_WIDTH = 0x00,
|
|
GOLDFISH_FB_GET_HEIGHT = 0x04,
|
|
GOLDFISH_FB_INT_STATUS = 0x08,
|
|
GOLDFISH_FB_INT_ENABLE = 0x0c,
|
|
GOLDFISH_FB_SET_BASE = 0x10,
|
|
GOLDFISH_FB_SET_ROTATION = 0x14,
|
|
GOLDFISH_FB_SET_BLANK = 0x18,
|
|
GOLDFISH_FB_GET_PHYS_WIDTH = 0x1c,
|
|
GOLDFISH_FB_GET_PHYS_HEIGHT = 0x20,
|
|
GOLDFISH_FB_GET_FORMAT = 0x24,
|
|
GOLDFISH_FB_INT_VSYNC = 1U << 0,
|
|
GOLDFISH_FB_INT_UPDATE_DONE = 1U << 1,
|
|
GOLDFISH_FB_FORMAT_BRGA_8888 = 1,
|
|
GOLDFISH_FB_FORMAT_RGBX_8888 = 2,
|
|
GOLDFISH_FB_FORMAT_RGB_888 = 3,
|
|
GOLDFISH_FB_FORMAT_RGB_565 = 4,
|
|
GOLDFISH_FB_FORMAT_BGRA_8888 = 5,
|
|
GOLDFISH_FB_FORMAT_RGBA_5551 = 6,
|
|
GOLDFISH_FB_FORMAT_RGBA_4444 = 8
|
|
};
|
|
|
|
struct goldfish_fb_format_s
|
|
{
|
|
uint8_t fmt;
|
|
uint8_t bpp;
|
|
};
|
|
|
|
struct goldfish_fb_s
|
|
{
|
|
struct fb_vtable_s vtable;
|
|
struct fb_planeinfo_s planeinfo;
|
|
struct fb_videoinfo_s videoinfo;
|
|
FAR void *base;
|
|
int irq;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static FAR struct goldfish_fb_s *g_goldfish_fb;
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int goldfish_getvideoinfo(FAR struct fb_vtable_s *vtable,
|
|
FAR struct fb_videoinfo_s *vinfo);
|
|
static int goldfish_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
|
|
FAR struct fb_planeinfo_s *pinfo);
|
|
static int goldfish_fb_interrupt(int irq, FAR void *dev_id, FAR void *arg);
|
|
static void goldfish_fb_vsync_irq(FAR struct goldfish_fb_s *fb);
|
|
static void goldfish_fb_framedone_irq(FAR struct goldfish_fb_s *fb);
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_fb_vsync_irq
|
|
****************************************************************************/
|
|
|
|
static void goldfish_fb_vsync_irq(FAR struct goldfish_fb_s *fb)
|
|
{
|
|
union fb_paninfo_u info;
|
|
#ifdef CONFIG_GOLDFISH_FB_VIDEO_MODE
|
|
int count;
|
|
|
|
count = fb_paninfo_count(&fb->vtable, FB_NO_OVERLAY);
|
|
if (count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (count > 1)
|
|
{
|
|
fb_remove_paninfo(&fb->vtable, FB_NO_OVERLAY);
|
|
}
|
|
#endif
|
|
|
|
if (fb_peek_paninfo(&fb->vtable, &info, FB_NO_OVERLAY) == OK)
|
|
{
|
|
uintptr_t buf = (uintptr_t)(fb->planeinfo.fbmem +
|
|
fb->planeinfo.stride *
|
|
info.planeinfo.yoffset);
|
|
|
|
/* Send buffer addr to GOLDFISH */
|
|
|
|
putreg32(buf, fb->base + GOLDFISH_FB_SET_BASE);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_fb_framedone_irq
|
|
****************************************************************************/
|
|
|
|
static void goldfish_fb_framedone_irq(FAR struct goldfish_fb_s *fb)
|
|
{
|
|
#ifndef CONFIG_GOLDFISH_FB_VIDEO_MODE
|
|
/* After the sending is completed, remove it from the panbuf queue.
|
|
*/
|
|
|
|
fb_remove_paninfo(&fb->vtable, FB_NO_OVERLAY);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_fb_interrupt
|
|
****************************************************************************/
|
|
|
|
static int goldfish_fb_interrupt(int irq, FAR void *dev_id, FAR void *arg)
|
|
{
|
|
FAR struct goldfish_fb_s *fb = arg;
|
|
irqstate_t flags;
|
|
uint32_t status;
|
|
|
|
flags = enter_critical_section();
|
|
status = getreg32(fb->base + GOLDFISH_FB_INT_STATUS);
|
|
if (status & GOLDFISH_FB_INT_VSYNC)
|
|
{
|
|
goldfish_fb_vsync_irq(fb);
|
|
}
|
|
|
|
else if (status & GOLDFISH_FB_INT_UPDATE_DONE)
|
|
{
|
|
goldfish_fb_framedone_irq(fb);
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_getvideoinfo
|
|
****************************************************************************/
|
|
|
|
static int goldfish_getvideoinfo(FAR struct fb_vtable_s *vtable,
|
|
FAR struct fb_videoinfo_s *vinfo)
|
|
{
|
|
FAR struct goldfish_fb_s *fb = (FAR struct goldfish_fb_s *)vtable;
|
|
|
|
ginfo("vtable=%p vinfo=%p\n", vtable, vinfo);
|
|
if (fb && vinfo)
|
|
{
|
|
memcpy(vinfo, &fb->videoinfo, sizeof(struct fb_videoinfo_s));
|
|
return OK;
|
|
}
|
|
|
|
gerr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_getplaneinfo
|
|
****************************************************************************/
|
|
|
|
static int goldfish_getplaneinfo(FAR struct fb_vtable_s *vtable, int planeno,
|
|
FAR struct fb_planeinfo_s *pinfo)
|
|
{
|
|
FAR struct goldfish_fb_s *fb = (FAR struct goldfish_fb_s *)vtable;
|
|
|
|
ginfo("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
|
|
if (fb && planeno == 0 && pinfo)
|
|
{
|
|
memcpy(pinfo, &fb->planeinfo, sizeof(struct fb_planeinfo_s));
|
|
return OK;
|
|
}
|
|
|
|
gerr("ERROR: Returning EINVAL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: goldfish_fb_register
|
|
****************************************************************************/
|
|
|
|
int goldfish_fb_register(int display, FAR void *regs, int irq)
|
|
{
|
|
FAR struct goldfish_fb_s *fb;
|
|
uint32_t fmt;
|
|
int ret = OK;
|
|
|
|
const struct goldfish_fb_format_s format_map[] =
|
|
{
|
|
[GOLDFISH_FB_FORMAT_BRGA_8888] =
|
|
{FB_FMT_RGBA32, 32},
|
|
[GOLDFISH_FB_FORMAT_RGBX_8888] =
|
|
{FB_FMT_RGB32, 32},
|
|
[GOLDFISH_FB_FORMAT_RGB_888] =
|
|
{FB_FMT_RGB24, 24},
|
|
[GOLDFISH_FB_FORMAT_RGB_565] =
|
|
{FB_FMT_RGB16_565, 16},
|
|
[GOLDFISH_FB_FORMAT_BGRA_8888] =
|
|
{FB_FMT_RGBA32, 32},
|
|
[GOLDFISH_FB_FORMAT_RGBA_5551] =
|
|
{FB_FMT_RGB16_555, 16},
|
|
[GOLDFISH_FB_FORMAT_RGBA_4444] =
|
|
{FB_FMT_RGBA16, 16},
|
|
};
|
|
|
|
fb = kmm_zalloc(sizeof(*fb));
|
|
if (fb == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
fb->base = regs;
|
|
fb->irq = irq;
|
|
|
|
fmt = getreg32(fb->base + GOLDFISH_FB_GET_FORMAT);
|
|
|
|
fb->videoinfo.xres = getreg32(fb->base + GOLDFISH_FB_GET_WIDTH);
|
|
fb->videoinfo.yres = getreg32(fb->base + GOLDFISH_FB_GET_HEIGHT);
|
|
fb->videoinfo.nplanes = 1;
|
|
fb->videoinfo.fmt = format_map[fmt].fmt;
|
|
|
|
fb->planeinfo.bpp = format_map[fmt].bpp;
|
|
fb->planeinfo.stride = fb->videoinfo.xres * (fb->planeinfo.bpp >> 3);
|
|
fb->planeinfo.yres_virtual = fb->videoinfo.yres *
|
|
CONFIG_GOLDFISH_FB_FRAME_NBUFFER;
|
|
fb->planeinfo.xres_virtual = fb->videoinfo.xres;
|
|
|
|
fb->planeinfo.fblen = fb->planeinfo.stride * fb->planeinfo.yres_virtual;
|
|
fb->planeinfo.fbmem = kmm_zalloc(fb->planeinfo.fblen);
|
|
if (fb->planeinfo.fbmem == NULL)
|
|
{
|
|
gerr("ERROR: Failed to allocate framebuffer memory: %zu KB\n",
|
|
fb->planeinfo.fblen / 1024);
|
|
ret = -ENOMEM;
|
|
goto err_fbmem_alloc_failed;
|
|
}
|
|
|
|
fb->vtable.getplaneinfo = goldfish_getplaneinfo;
|
|
fb->vtable.getvideoinfo = goldfish_getvideoinfo;
|
|
|
|
ret = irq_attach(fb->irq, goldfish_fb_interrupt, fb);
|
|
if (ret < 0)
|
|
{
|
|
goto err_irq_attach_failed;
|
|
}
|
|
|
|
up_enable_irq(fb->irq);
|
|
putreg32(GOLDFISH_FB_INT_VSYNC | GOLDFISH_FB_INT_UPDATE_DONE,
|
|
fb->base + GOLDFISH_FB_INT_ENABLE);
|
|
|
|
/* Updates base */
|
|
|
|
putreg32((uintptr_t)fb->planeinfo.fbmem,
|
|
fb->base + GOLDFISH_FB_SET_BASE);
|
|
|
|
ret = fb_register_device(display, 0, (FAR struct fb_vtable_s *)fb);
|
|
if (ret < 0)
|
|
{
|
|
goto err_fb_register_failed;
|
|
}
|
|
|
|
g_goldfish_fb = fb;
|
|
return OK;
|
|
|
|
err_fb_register_failed:
|
|
irq_detach(fb->irq);
|
|
err_irq_attach_failed:
|
|
kmm_free(fb->planeinfo.fbmem);
|
|
err_fbmem_alloc_failed:
|
|
kmm_free(fb);
|
|
return ret;
|
|
}
|