arm64/pinephone: Add driver for Frame Buffer

This PR adds the Frame Buffer Driver for PINE64 PinePhone. With this driver, NuttX Apps will be able to use the standard Frame Buffer API to render graphics on PinePhone.

`boards/arm64/a64/pinephone/src/pinephone_bringup.c`: Render Test Pattern after calling `up_fbinitialize()` to start the Frame Buffer Driver

`boards/arm64/a64/pinephone/src/pinephone_display.c`: Add Frame Buffer Driver

`boards/arm64/a64/pinephone/src/pinephone_display.h`: Declare new function for rendering Test Pattern

`platforms/arm/a64/boards/pinephone/index.rst`: Add Frame Buffer as supported driver for PinePhone
This commit is contained in:
Lee Lup Yuen 2022-12-27 12:21:37 +08:00 committed by Xiang Xiao
parent f85d9a9332
commit f9bd04ff19
4 changed files with 487 additions and 131 deletions

View File

@ -157,6 +157,7 @@ Peripheral Support NOTES
======================= ======= =====
Backlight Yes
Display Engine Yes
Frame Buffer Yes
LCD Controller (ST7703) Yes
LCD Panel (XBD599) Yes
MIPI D-PHY Yes

View File

@ -36,6 +36,7 @@
#ifdef CONFIG_VIDEO_FB
# include <nuttx/video/fb.h>
# include "pinephone_display.h"
#endif
#include "pinephone.h"
@ -84,6 +85,10 @@ int pinephone_bringup(void)
{
syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret);
}
/* Render the Test Pattern */
pinephone_display_test_pattern();
#endif
UNUSED(ret);

View File

@ -41,6 +41,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <errno.h>
#include <nuttx/board.h>
#include <arch/board/board.h>
@ -53,6 +54,7 @@
#include "a64_tcon0.h"
#include "pinephone_lcd.h"
#include "pinephone_pmic.h"
#include "pinephone_display.h"
/****************************************************************************
* Pre-processor Definitions
@ -76,10 +78,53 @@
#define FB1_WIDTH 600
#define FB1_HEIGHT 600
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int pinephone_getvideoinfo(struct fb_vtable_s *vtable,
struct fb_videoinfo_s *vinfo);
static int pinephone_getplaneinfo(struct fb_vtable_s *vtable, int planeno,
struct fb_planeinfo_s *pinfo);
static int pinephone_getoverlayinfo(struct fb_vtable_s *vtable,
int overlayno,
struct fb_overlayinfo_s *oinfo);
static int pinephone_settransp(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo);
static int pinephone_setchromakey(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo);
static int pinephone_setcolor(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo);
static int pinephone_setblank(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo);
static int pinephone_setarea(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo);
/****************************************************************************
* Private Data
****************************************************************************/
/* Vtable for Frame Buffer Operations */
static struct fb_vtable_s g_pinephone_vtable =
{
.getvideoinfo = pinephone_getvideoinfo,
.getplaneinfo = pinephone_getplaneinfo,
.getoverlayinfo = pinephone_getoverlayinfo,
.settransp = pinephone_settransp,
.setchromakey = pinephone_setchromakey,
.setcolor = pinephone_setcolor,
.setblank = pinephone_setblank,
.setarea = pinephone_setarea
};
/* Frame Buffers for Display Engine *****************************************/
/* Frame Buffer 0: (Base UI Channel)
@ -195,130 +240,6 @@ static struct fb_overlayinfo_s g_pinephone_overlays[2] =
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: test_pattern
*
* Description:
* Fill the 3 Frame Buffers with a Test Pattern. Should be called after
* Display Engine is Enabled, or the rendered image will have missing
* rows.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static void test_pattern(void)
{
int i;
int x;
int y;
const int fb0_len = sizeof(g_pinephone_fb0) / sizeof(g_pinephone_fb0[0]);
const int fb1_len = sizeof(g_pinephone_fb1) / sizeof(g_pinephone_fb1[0]);
/* Zero the Framebuffers */
memset(g_pinephone_fb0, 0, sizeof(g_pinephone_fb0));
memset(g_pinephone_fb1, 0, sizeof(g_pinephone_fb1));
memset(g_pinephone_fb2, 0, sizeof(g_pinephone_fb2));
/* Init Framebuffer 0:
* Fill with Blue, Green and Red
*/
for (i = 0; i < fb0_len; i++)
{
/* Colours are in XRGB 8888 format */
if (i < fb0_len / 4)
{
/* Blue for top quarter */
g_pinephone_fb0[i] = 0x80000080;
}
else if (i < fb0_len / 2)
{
/* Green for next quarter */
g_pinephone_fb0[i] = 0x80008000;
}
else
{
/* Red for lower half */
g_pinephone_fb0[i] = 0x80800000;
}
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
/* Init Framebuffer 1:
* Fill with Semi-Transparent White
*/
for (i = 0; i < fb1_len; i++)
{
/* Semi-Transparent White in ARGB 8888 format */
g_pinephone_fb1[i] = 0x40ffffff;
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
/* Init Framebuffer 2:
* Fill with Semi-Transparent Green Circle
*/
for (y = 0; y < PANEL_HEIGHT; y++)
{
for (x = 0; x < PANEL_WIDTH; x++)
{
/* Get pixel index */
const int p = (y * PANEL_WIDTH) + x;
/* Shift coordinates so that centre of screen is (0,0) */
const int half_width = PANEL_WIDTH / 2;
const int half_height = PANEL_HEIGHT / 2;
const int x_shift = x - half_width;
const int y_shift = y - half_height;
/* If x^2 + y^2 < radius^2, set to Semi-Transparent Green */
if (x_shift*x_shift + y_shift*y_shift < half_width*half_width)
{
/* Semi-Transparent Green in ARGB 8888 Format */
g_pinephone_fb2[p] = 0x80008000;
}
else /* Otherwise set to Transparent Black */
{
/* Transparent Black in ARGB 8888 Format */
g_pinephone_fb2[p] = 0x00000000;
}
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
}
}
/****************************************************************************
* Name: render_framebuffers
*
@ -418,16 +339,266 @@ static int render_framebuffers(void)
return ret;
}
/* Fill Frame Buffers with Test Pattern. Should be called after
* Display Engine is Enabled, or the rendered image will have
* missing rows.
return OK;
}
/****************************************************************************
* Name: pinephone_getvideoinfo
*
* Description:
* Get the videoinfo for the framebuffer. (ioctl Entrypoint:
* FBIOGET_VIDEOINFO)
*
* Input Parameters:
* vtable - Framebuffer driver object
* vinfo - Returned videoinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_getvideoinfo(struct fb_vtable_s *vtable,
struct fb_videoinfo_s *vinfo)
{
static int stage = 0;
ginfo("vtable=%p vinfo=%p\n", vtable, vinfo);
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable &&
vinfo != NULL);
/* Copy and return the videoinfo object */
memcpy(vinfo, &g_pinephone_video, sizeof(struct fb_videoinfo_s));
/* Keep track of the stages during startup:
* Stage 0: Initialize driver at startup
* Stage 1: First call by apps
* Stage 2: Subsequent calls by apps
* We erase the framebuffers at stages 0 and 1. This allows the
* Test Pattern to be displayed for as long as possible before erasure.
*/
test_pattern();
if (stage < 2)
{
stage++;
memset(g_pinephone_fb0, 0, sizeof(g_pinephone_fb0));
memset(g_pinephone_fb1, 0, sizeof(g_pinephone_fb1));
memset(g_pinephone_fb2, 0, sizeof(g_pinephone_fb2));
}
return OK;
}
/****************************************************************************
* Name: pinephone_getplaneinfo
*
* Description:
* Get the planeinfo for the framebuffer. (ioctl Entrypoint:
* FBIOGET_PLANEINFO)
*
* Input Parameters:
* vtable - Framebuffer driver object
* pinfo - Returned planeinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_getplaneinfo(struct fb_vtable_s *vtable, int planeno,
struct fb_planeinfo_s *pinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable);
ginfo("vtable=%p planeno=%d pinfo=%p\n", vtable, planeno, pinfo);
/* Copy and return the planeinfo object */
if (planeno == 0)
{
memcpy(pinfo, &g_pinephone_plane, sizeof(struct fb_planeinfo_s));
return OK;
}
gerr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/****************************************************************************
* Name: pinephone_getoverlayinfo
*
* Description:
* Get the overlayinfo for the framebuffer. (ioctl Entrypoint:
* FBIOGET_OVERLAYINFO)
*
* Input Parameters:
* vtable - Framebuffer driver object
* overlayno - Overlay number
* oinfo - Returned overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_getoverlayinfo(struct fb_vtable_s *vtable,
int overlayno,
struct fb_overlayinfo_s *oinfo)
{
const int overlay_len = sizeof(g_pinephone_overlays) /
sizeof(g_pinephone_overlays[0]);
ginfo("vtable=%p overlay=%d oinfo=%p\n", vtable, overlayno, oinfo);
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable);
/* Copy and return the overlayinfo object */
if (overlayno >= 0 && overlayno < overlay_len)
{
struct fb_overlayinfo_s *overlay = &g_pinephone_overlays[overlayno];
memcpy(oinfo, overlay, sizeof(struct fb_overlayinfo_s));
return OK;
}
gerr("ERROR: Returning EINVAL\n");
return -EINVAL;
}
/****************************************************************************
* Name: pinephone_settransp
*
* Description:
* Set the overlay transparency for the framebuffer. (ioctl Entrypoint:
* FBIOSET_TRANSP)
*
* Input Parameters:
* vtable - Framebuffer driver object
* oinfo - overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_settransp(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable);
ginfo("vtable=%p, overlay=%d, transp=%02x, transp_mode=%02x\n", vtable,
oinfo->overlay, oinfo->transp.transp, oinfo->transp.transp_mode);
gerr("Not implemented");
return OK;
}
/****************************************************************************
* Name: pinephone_setchromakey
*
* Description:
* Set the overlay chroma key for the framebuffer. (ioctl Entrypoint:
* FBIOSET_CHROMAKEY)
*
* Input Parameters:
* vtable - Framebuffer driver object
* oinfo - overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_setchromakey(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable &&
oinfo != NULL);
ginfo("vtable=%p, overlay=%d, chromakey=%08x\n", vtable,
oinfo->overlay, oinfo->chromakey);
gerr("Not implemented");
return OK;
}
/****************************************************************************
* Name: pinephone_setcolor
*
* Description:
* Set the overlay color for the framebuffer. (ioctl Entrypoint:
* FBIOSET_COLOR)
*
* Input Parameters:
* vtable - Framebuffer driver object
* oinfo - overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_setcolor(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable &&
oinfo != NULL);
ginfo("vtable=%p, overlay=%d, color=%08x\n",
vtable, oinfo->overlay, oinfo->color);
gerr("Not implemented");
return OK;
}
/****************************************************************************
* Name: pinephone_setblank
*
* Description:
* Set the framebuffer overlay to blank. (ioctl Entrypoint: FBIOSET_BLANK)
*
* Input Parameters:
* vtable - Framebuffer driver object
* oinfo - overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_setblank(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable &&
oinfo != NULL);
ginfo("vtable=%p, overlay=%d, blank=%02x\n",
vtable, oinfo->overlay, oinfo->blank);
gerr("Not implemented");
return OK;
}
/****************************************************************************
* Name: pinephone_setarea
*
* Description:
* Set the overlay area for the framebuffer. (ioctl Entrypoint:
* FBIOSET_AREA)
*
* Input Parameters:
* vtable - Framebuffer driver object
* oinfo - overlayinfo object
*
* Returned Value:
* Zero (OK) on success; a negated errno value is returned on any failure.
*
****************************************************************************/
static int pinephone_setarea(struct fb_vtable_s *vtable,
const struct fb_overlayinfo_s *oinfo)
{
DEBUGASSERT(vtable != NULL && vtable == &g_pinephone_vtable &&
oinfo != NULL);
ginfo("vtable=%p, overlay=%d, x=%d, y=%d, w=%d, h=%d\n", vtable,
oinfo->overlay, oinfo->sarea.x, oinfo->sarea.y, oinfo->sarea.w,
oinfo->sarea.h);
gerr("Not implemented");
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -585,7 +756,7 @@ int up_fbinitialize(int display)
* Name: up_fbgetvplane
*
* Description:
* Return a a reference to the framebuffer object for the specified video
* Return a reference to the framebuffer object for the specified video
* plane of the specified plane. Many OSDs support multiple planes of
* video.
*
@ -602,10 +773,13 @@ int up_fbinitialize(int display)
struct fb_vtable_s *up_fbgetvplane(int display, int vplane)
{
/* TODO: Implement up_fbgetvplane */
ginfo("vplane: %d\n", vplane);
DEBUGASSERT(display == 0);
_err("up_fbgetvplane not implemented\n");
if (vplane == 0)
{
return &g_pinephone_vtable;
}
return NULL;
}
@ -631,3 +805,127 @@ void up_fbuninitialize(int display)
UNUSED(display);
}
/****************************************************************************
* Name: pinephone_display_test_pattern
*
* Description:
* Fill the 3 Frame Buffers with a Test Pattern. Should be called after
* Display Engine is Enabled, or the rendered image will have missing
* rows.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void pinephone_display_test_pattern(void)
{
int i;
int x;
int y;
const int fb0_len = sizeof(g_pinephone_fb0) / sizeof(g_pinephone_fb0[0]);
const int fb1_len = sizeof(g_pinephone_fb1) / sizeof(g_pinephone_fb1[0]);
/* Zero the Framebuffers */
memset(g_pinephone_fb0, 0, sizeof(g_pinephone_fb0));
memset(g_pinephone_fb1, 0, sizeof(g_pinephone_fb1));
memset(g_pinephone_fb2, 0, sizeof(g_pinephone_fb2));
/* Init Framebuffer 0:
* Fill with Blue, Green and Red
*/
for (i = 0; i < fb0_len; i++)
{
/* Colours are in XRGB 8888 format */
if (i < fb0_len / 4)
{
/* Blue for top quarter */
g_pinephone_fb0[i] = 0x80000080;
}
else if (i < fb0_len / 2)
{
/* Green for next quarter */
g_pinephone_fb0[i] = 0x80008000;
}
else
{
/* Red for lower half */
g_pinephone_fb0[i] = 0x80800000;
}
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
/* Init Framebuffer 1:
* Fill with Semi-Transparent White
*/
for (i = 0; i < fb1_len; i++)
{
/* Semi-Transparent White in ARGB 8888 format */
g_pinephone_fb1[i] = 0x40ffffff;
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
/* Init Framebuffer 2:
* Fill with Semi-Transparent Green Circle
*/
for (y = 0; y < PANEL_HEIGHT; y++)
{
for (x = 0; x < PANEL_WIDTH; x++)
{
/* Get pixel index */
const int p = (y * PANEL_WIDTH) + x;
/* Shift coordinates so that centre of screen is (0,0) */
const int half_width = PANEL_WIDTH / 2;
const int half_height = PANEL_HEIGHT / 2;
const int x_shift = x - half_width;
const int y_shift = y - half_height;
/* If x^2 + y^2 < radius^2, set to Semi-Transparent Green */
if (x_shift*x_shift + y_shift*y_shift < half_width*half_width)
{
/* Semi-Transparent Green in ARGB 8888 Format */
g_pinephone_fb2[p] = 0x80008000;
}
else /* Otherwise set to Transparent Black */
{
/* Transparent Black in ARGB 8888 Format */
g_pinephone_fb2[p] = 0x00000000;
}
/* Fixes missing rows in the rendered image, not sure why */
ARM64_DMB();
ARM64_DSB();
ARM64_ISB();
}
}
}

View File

@ -0,0 +1,52 @@
/****************************************************************************
* boards/arm64/a64/pinephone/src/pinephone_display.h
*
* 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.
*
****************************************************************************/
#ifndef __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_DISPLAY_H
#define __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_DISPLAY_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: pinephone_display_test_pattern
*
* Description:
* Fill the 3 Frame Buffers with a Test Pattern. Should be called after
* Display Engine is Enabled, or the rendered image will have missing
* rows.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void pinephone_display_test_pattern(void);
#endif /* __BOARDS_ARM64_A64_PINEPHONE_SRC_PINEPHONE_DISPLAY_H */