[dm][graphic] support dm mode

1. Add backlight framework for graphic.
2. Add framebuffer and plane, power, EDID for graphic framework
3. Add boot logo render for graphic
4. Update lcd.h

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI
2025-12-12 15:58:12 +08:00
committed by R b b666
parent 35bae6824b
commit 5abecc1fd0
19 changed files with 4188 additions and 1 deletions

View File

@@ -1,3 +1,14 @@
config RT_USING_LCD
bool "Using LCD graphic drivers"
bool "Using LCD graphic drivers" if !RT_USING_DM
default n
menuconfig RT_USING_GRAPHIC
bool "Using Graphics device drivers"
depends on RT_USING_DM
default n
if RT_USING_GRAPHIC
rsource "backlight/Kconfig"
rsource "framebuffer/Kconfig"
rsource "logo/Kconfig"
endif

View File

@@ -0,0 +1,23 @@
from building import *
group = []
objs = []
if not GetDepend(['RT_USING_GRAPHIC']):
Return('group')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../include']
src = ['graphic.c', 'graphic_primary.c', 'graphic_simple.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')

View File

@@ -0,0 +1,7 @@
menuconfig RT_GRAPHIC_BACKLIGHT
bool "Backlight support"
default n
if RT_GRAPHIC_BACKLIGHT
osource "$(SOC_DM_GRAPHIC_BACKLIGHT_DIR)/Kconfig"
endif

View File

@@ -0,0 +1,14 @@
from building import *
group = []
if not GetDepend(['RT_GRAPHIC_BACKLIGHT']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
src = ['backlight.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

View File

@@ -0,0 +1,231 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#define DBG_TAG "rtdm.backlight"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <stdlib.h>
#include <drivers/core/dm.h>
#include <drivers/backlight.h>
static struct rt_dm_ida backlight_ida = RT_DM_IDA_INIT(GRAPHIC_BACKLIGHT);
static rt_ssize_t _backlight_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_ssize_t res;
int brightness_len;
rt_uint32_t brightness;
char string[sizeof("4294967295")];
struct rt_backlight_device *bl = rt_container_of(dev, struct rt_backlight_device, parent);
if ((res = rt_backlight_get_brightness(bl, &brightness)))
{
return res;
}
brightness_len = rt_sprintf(string, "%u", brightness);
if (pos < brightness_len)
{
size = rt_min_t(rt_size_t, size, brightness_len - pos);
rt_strncpy(buffer, &string[pos], size);
return size;
}
else
{
return 0;
}
}
static rt_ssize_t _backlight_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
rt_ssize_t res;
rt_uint32_t brightness = atoi(buffer);
struct rt_backlight_device *bl = rt_container_of(dev, struct rt_backlight_device, parent);
if (brightness > bl->props.max_brightness)
{
LOG_D("%s: brightness(%u) > max_brightness(%u)",
rt_dm_dev_get_name(dev), brightness, bl->props.max_brightness);
return -RT_EINVAL;
}
if ((res = rt_backlight_set_brightness(bl, brightness)))
{
return res;
}
LOG_D("%s: brightness to %u", rt_dm_dev_get_name(dev), brightness);
return size;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops _backlight_ops =
{
.read = _backlight_read,
.write = _backlight_write,
};
#endif
rt_err_t rt_backlight_register(struct rt_backlight_device *bl)
{
rt_err_t err;
int device_id;
const char *dev_name;
if (!bl || !bl->ops)
{
return -RT_EINVAL;
}
if ((device_id = rt_dm_ida_alloc(&backlight_ida)) < 0)
{
return -RT_EFULL;
}
rt_dm_dev_set_name(&bl->parent, "backlight%u", device_id);
dev_name = rt_dm_dev_get_name(&bl->parent);
rt_mutex_init(&bl->lock, dev_name, RT_IPC_FLAG_PRIO);
bl->parent.type = RT_Device_Class_Char;
#ifdef RT_USING_DEVICE_OPS
bl->parent.ops = &_backlight_ops;
#else
bl->parent.read = _backlight_read;
bl->parent.write = _backlight_write;
#endif
bl->parent.master_id = backlight_ida.master_id;
bl->parent.device_id = device_id;
if ((err = rt_device_register(&bl->parent, dev_name, RT_DEVICE_FLAG_RDWR)))
{
rt_dm_ida_free(&backlight_ida, device_id);
return err;
}
return RT_EOK;
}
rt_err_t rt_backlight_unregister(struct rt_backlight_device *bl)
{
if (!bl)
{
return -RT_EINVAL;
}
rt_backlight_set_power(bl, RT_BACKLIGHT_POWER_POWERDOWN);
rt_dm_ida_free(&backlight_ida, bl->parent.device_id);
rt_device_unregister(&bl->parent);
return RT_EOK;
}
rt_err_t rt_backlight_set_power(struct rt_backlight_device *bl, enum rt_backlight_power power)
{
rt_err_t err;
enum rt_backlight_power old_power;
if (!bl || power >= RT_BACKLIGHT_POWER_NR)
{
return -RT_EINVAL;
}
rt_mutex_take(&bl->lock, RT_WAITING_FOREVER);
old_power = bl->props.power;
bl->props.power = power;
if ((err = bl->ops->update_status(bl)))
{
bl->props.power = old_power;
}
rt_mutex_release(&bl->lock);
return err;
}
rt_err_t rt_backlight_get_power(struct rt_backlight_device *bl, enum rt_backlight_power *out_power)
{
if (!bl || !out_power)
{
return -RT_EINVAL;
}
rt_mutex_take(&bl->lock, RT_WAITING_FOREVER);
*out_power = bl->props.power;
rt_mutex_release(&bl->lock);
return RT_EOK;
}
rt_err_t rt_backlight_set_brightness(struct rt_backlight_device *bl, rt_uint32_t brightness)
{
rt_err_t err;
rt_uint32_t old_brightness;
if (!bl || brightness > bl->props.max_brightness)
{
return -RT_EINVAL;
}
rt_mutex_take(&bl->lock, RT_WAITING_FOREVER);
old_brightness = bl->props.brightness;
bl->props.brightness = brightness;
if ((err = bl->ops->update_status(bl)))
{
bl->props.brightness = old_brightness;
}
rt_mutex_release(&bl->lock);
return err;
}
rt_err_t rt_backlight_get_brightness(struct rt_backlight_device *bl, rt_uint32_t *out_brightness)
{
rt_err_t err;
if (!bl || !out_brightness)
{
return -RT_EINVAL;
}
rt_mutex_take(&bl->lock, RT_WAITING_FOREVER);
if (bl->ops->get_brightness)
{
err = bl->ops->get_brightness(bl, out_brightness);
}
else
{
*out_brightness = rt_backlight_power_brightness(bl);
err = RT_EOK;
}
rt_mutex_release(&bl->lock);
return err;
}

View File

@@ -0,0 +1,8 @@
menuconfig RT_GRAPHIC_FB
bool "LCD and Frame buffer support"
select RT_USING_LCD
default y
if RT_GRAPHIC_FB
osource "$(SOC_DM_GRAPHIC_FB_DIR)/Kconfig"
endif

View File

@@ -0,0 +1,16 @@
from building import *
group = []
if not GetDepend(['RT_GRAPHIC_FB']):
Return('group')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../../include']
src = []
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,381 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "graphic.primary"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static struct rt_graphic_device *primary_gdev = RT_NULL;
#define framebuffer_drift(plane, x, y, bpp) \
((plane)->framebuffer + (x) * ((bpp) / 8) + (y) * (plane)->line_length)
#define fixup_dir(a1, a2) \
if ((a1) > (a2)) { (a1) ^= (a2); (a2) ^= (a1); (a1) ^= (a2); }
static void graphic_primary_set_pixel_8bit(const char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint8_t *)framebuffer_drift(plane, x, y, 8) = *(rt_uint8_t *)pixel;
}
static void graphic_primary_get_pixel_8bit(char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint8_t *)pixel = *(rt_uint8_t *)framebuffer_drift(plane, x, y, 8);
}
static void graphic_primary_draw_hline_8bit(const char *pixel, int x1, int x2, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(x1, x2);
rt_memset(framebuffer_drift(plane, x1, y, 8), *(rt_uint8_t *)pixel, x2 - x1);
}
static void graphic_primary_draw_vline_8bit(const char *pixel, int x, int y1, int y2)
{
rt_uint8_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(y1, y2);
fb = framebuffer_drift(plane, x, y1, 8);
for (; y1 < y2; ++y1)
{
*fb = *(rt_uint8_t *)pixel;
fb += plane->line_length;
}
}
static void graphic_primary_blit_line_8bit(const char *pixel, int x, int y, rt_size_t size)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
rt_memcpy(framebuffer_drift(plane, x, y, 8), pixel, size);
}
static struct rt_device_graphic_ops graphic_primary_8bit_ops =
{
.set_pixel = graphic_primary_set_pixel_8bit,
.get_pixel = graphic_primary_get_pixel_8bit,
.draw_hline = graphic_primary_draw_hline_8bit,
.draw_vline = graphic_primary_draw_vline_8bit,
.blit_line = graphic_primary_blit_line_8bit,
};
static void graphic_primary_set_pixel_16bit(const char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint16_t *)framebuffer_drift(plane, x, y, 16) = *(rt_uint16_t *)pixel;
}
static void graphic_primary_get_pixel_16bit(char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint16_t *)pixel = *(rt_uint16_t *)framebuffer_drift(plane, x, y, 16);
}
static void graphic_primary_draw_hline_16bit(const char *pixel, int x1, int x2, int y)
{
rt_uint16_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(x1, x2);
fb = framebuffer_drift(plane, x1, y, 16);
for (; x1 < x2; ++x1)
{
*fb++ = *(rt_uint16_t *)pixel;
}
}
static void graphic_primary_draw_vline_16bit(const char *pixel, int x, int y1, int y2)
{
rt_uint16_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(y1, y2);
fb = framebuffer_drift(plane, x, y1, 16);
for (; y1 < y2; ++y1)
{
*fb = *(rt_uint16_t *)pixel;
fb = (void *)fb + plane->line_length;
}
}
static void graphic_primary_blit_line_16bit(const char *pixel, int x, int y, rt_size_t size)
{
rt_uint16_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fb = framebuffer_drift(plane, x, y, 16);
while (size --> 0)
{
*fb++ = *(rt_uint16_t *)pixel;
pixel += 2;
}
}
static struct rt_device_graphic_ops graphic_primary_16bit_ops =
{
.set_pixel = graphic_primary_set_pixel_16bit,
.get_pixel = graphic_primary_get_pixel_16bit,
.draw_hline = graphic_primary_draw_hline_16bit,
.draw_vline = graphic_primary_draw_vline_16bit,
.blit_line = graphic_primary_blit_line_16bit,
};
static void graphic_primary_set_pixel_24bit(const char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
rt_memcpy(framebuffer_drift(plane, x, y, 24), pixel, 3);
}
static void graphic_primary_get_pixel_24bit(char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
rt_memcpy(pixel, framebuffer_drift(plane, x, y, 24), 3);
}
static void graphic_primary_draw_hline_24bit(const char *pixel, int x1, int x2, int y)
{
rt_uint8_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(x1, x2);
fb = framebuffer_drift(plane, x1, y, 24);
for (; x1 < x2; ++x1)
{
*fb++ = ((rt_uint8_t *)pixel)[0];
*fb++ = ((rt_uint8_t *)pixel)[1];
*fb++ = ((rt_uint8_t *)pixel)[2];
}
}
static void graphic_primary_draw_vline_24bit(const char *pixel, int x, int y1, int y2)
{
rt_uint8_t *fb;
rt_size_t xlate;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(y1, y2);
fb = framebuffer_drift(plane, x, y1, 24);
xlate = plane->line_length - 3;
for (; y1 < y2; ++y1)
{
*fb++ = ((rt_uint8_t *)pixel)[0];
*fb++ = ((rt_uint8_t *)pixel)[1];
*fb++ = ((rt_uint8_t *)pixel)[2];
fb = (void *)fb + xlate;
}
}
static void graphic_primary_blit_line_24bit(const char *pixel, int x, int y, rt_size_t size)
{
rt_uint8_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fb = framebuffer_drift(plane, x, y, 24);
while (size --> 0)
{
*fb++ = *(rt_uint8_t *)pixel++;
*fb++ = *(rt_uint8_t *)pixel++;
*fb++ = *(rt_uint8_t *)pixel++;
}
}
static struct rt_device_graphic_ops graphic_primary_24bit_ops =
{
.set_pixel = graphic_primary_set_pixel_24bit,
.get_pixel = graphic_primary_get_pixel_24bit,
.draw_hline = graphic_primary_draw_hline_24bit,
.draw_vline = graphic_primary_draw_vline_24bit,
.blit_line = graphic_primary_blit_line_24bit,
};
static void graphic_primary_set_pixel_32bit(const char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint32_t *)framebuffer_drift(plane, x, y, 32) = *(rt_uint32_t *)pixel;
}
static void graphic_primary_get_pixel_32bit(char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint32_t *)pixel = *(rt_uint32_t *)framebuffer_drift(plane, x, y, 32);
}
static void graphic_primary_draw_hline_32bit(const char *pixel, int x1, int x2, int y)
{
rt_uint32_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(x1, x2);
fb = framebuffer_drift(plane, x1, y, 32);
for (; x1 < x2; ++x1)
{
*fb++ = *(rt_uint32_t *)pixel;
}
}
static void graphic_primary_draw_vline_32bit(const char *pixel, int x, int y1, int y2)
{
rt_uint32_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(y1, y2);
fb = framebuffer_drift(plane, x, y1, 32);
for (; y1 < y2; ++y1)
{
*fb = *(rt_uint32_t *)pixel;
fb = (void *)fb + plane->line_length;
}
}
static void graphic_primary_blit_line_32bit(const char *pixel, int x, int y, rt_size_t size)
{
rt_uint32_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fb = framebuffer_drift(plane, x, y, 32);
while (size --> 0)
{
*fb++ = *(rt_uint32_t *)pixel;
pixel += 4;
}
}
static struct rt_device_graphic_ops graphic_primary_32bit_ops =
{
.set_pixel = graphic_primary_set_pixel_32bit,
.get_pixel = graphic_primary_get_pixel_32bit,
.draw_hline = graphic_primary_draw_hline_32bit,
.draw_vline = graphic_primary_draw_vline_32bit,
.blit_line = graphic_primary_blit_line_32bit,
};
static void graphic_primary_set_pixel_64bit(const char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint64_t *)framebuffer_drift(plane, x, y, 64) = *(rt_uint64_t *)pixel;
}
static void graphic_primary_get_pixel_64bit(char *pixel, int x, int y)
{
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
*(rt_uint64_t *)pixel = *(rt_uint64_t *)framebuffer_drift(plane, x, y, 64);
}
static void graphic_primary_draw_hline_64bit(const char *pixel, int x1, int x2, int y)
{
rt_uint64_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(x1, x2);
fb = framebuffer_drift(plane, x1, y, 64);
for (; x1 < x2; ++x1)
{
*fb++ = *(rt_uint64_t *)pixel;
}
}
static void graphic_primary_draw_vline_64bit(const char *pixel, int x, int y1, int y2)
{
rt_uint64_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fixup_dir(y1, y2);
fb = framebuffer_drift(plane, x, y1, 64);
for (; y1 < y2; ++y1)
{
*fb = *(rt_uint64_t *)pixel;
fb = (void *)fb + plane->line_length;
}
}
static void graphic_primary_blit_line_64bit(const char *pixel, int x, int y, rt_size_t size)
{
rt_uint64_t *fb;
struct rt_graphic_plane *plane = primary_gdev->primary_plane;
fb = framebuffer_drift(plane, x, y, 64);
while (size --> 0)
{
*fb++ = *(rt_uint64_t *)pixel;
pixel += 8;
}
}
static struct rt_device_graphic_ops graphic_primary_64bit_ops =
{
.set_pixel = graphic_primary_set_pixel_64bit,
.get_pixel = graphic_primary_get_pixel_64bit,
.draw_hline = graphic_primary_draw_hline_64bit,
.draw_vline = graphic_primary_draw_vline_64bit,
.blit_line = graphic_primary_blit_line_64bit,
};
struct rt_device_graphic_ops *rt_graphic_device_switch_primary(struct rt_graphic_device *gdev)
{
primary_gdev = gdev;
RT_ASSERT(primary_gdev != RT_NULL);
RT_ASSERT(primary_gdev->primary_plane != RT_NULL);
switch (rt_graphic_mode_bpp(primary_gdev->primary_plane->mode))
{
case 32: return &graphic_primary_32bit_ops;
case 24: return &graphic_primary_24bit_ops;
case 16: return &graphic_primary_16bit_ops;
case 64: return &graphic_primary_64bit_ops;
case 8: return &graphic_primary_8bit_ops;
default:
LOG_E("What the fuck format(%u)", primary_gdev->primary_plane->mode);
RT_ASSERT(0);
break;
}
return RT_NULL;
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "graphic.simple"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static rt_uint32_t edid_dpi_to_mm(rt_uint32_t dpi, rt_uint32_t res)
{
return res * 254 / 10 / dpi;
}
static void edid_checksum(rt_uint8_t *edid, rt_size_t len)
{
rt_uint32_t sum = 0;
for (int i = 0; i < len; ++i)
{
sum += edid[i];
}
sum &= 0xff;
if (sum)
{
edid[len] = 0x100 - sum;
}
}
static void fill_edid(struct rt_graphic_device *gdev,
rt_uint32_t width, rt_uint32_t height, rt_uint32_t refresh_hz)
{
union
{
rt_uint32_t u32;
rt_uint16_t u16;
} value;
int dt_idx = 0;
int width_mm, height_mm;
rt_uint64_t clock;
rt_uint32_t xfront, xsync, xblank, yfront, ysync, yblank;
struct edid *edid = &gdev->edid;
struct detailed_timing *dt;
struct detailed_pixel_timing *dpt;
struct detailed_non_pixel *dnp;
refresh_hz = refresh_hz ? : 75000;
width_mm = edid_dpi_to_mm(100, width);
height_mm = edid_dpi_to_mm(100, height);
/* EDID: timings */
xfront = width * 25 / 100;
xsync = width * 3 / 100;
xblank = width * 35 / 100;
yfront = height * 5 / 1000;
ysync = height * 5 / 1000;
yblank = height * 35 / 1000;
clock = ((rt_uint64_t)refresh_hz * (width + xblank) * (height + yblank)) / 10000000;
if (width >= 4096 || height >= 4096 || clock >= 65536)
{
LOG_E("%s: Large screen %ux%u@%uHz is not supported in simple",
rt_dm_dev_get_name(&gdev->parent), width, height, clock);
RT_ASSERT(0);
}
/* EDID: extensions */
/* EDID: header information */
edid->header[0] = 0x00;
edid->header[1] = 0xff;
edid->header[2] = 0xff;
edid->header[3] = 0xff;
edid->header[4] = 0xff;
edid->header[5] = 0xff;
edid->header[6] = 0xff;
edid->header[7] = 0x00;
/* Vendor id */
value.u16 = rt_cpu_to_be16(
((('R' - '@') & 0x1f) << 10) |
((('T' - '@') & 0x1f) << 5) |
((('T' - '@') & 0x1f) << 0));
rt_memcpy(edid->mfg_id, &value.u16, sizeof(edid->mfg_id));
/* Product code */
value.u16 = rt_cpu_to_le16(0x1234);
rt_memcpy(edid->prod_code, &value.u16, sizeof(edid->prod_code));
/* Serial number */
edid->serial = rt_cpu_to_le32(0);
/* Manufacture week and year */
edid->mfg_week = 42;
edid->mfg_year = 2014 - 1990;
/* Version */
edid->version = 1;
edid->revision = 4;
/* EDID: basic display parameters */
/* Video input: digital, 8bpc, displayport */
edid->input = 0xa5;
/* Screen size */
edid->width_cm = width_mm / 10;
edid->height_cm = height_mm / 10;
/* Gamma: 2.2 */
edid->gamma = 220 - 100;
/* Features: STD sRGB, preferred timing */
edid->features = 0x06;
/* EDID: chromaticity coordinates */
/*
* STD sRGB colorspace:
* X Y
* red: 0.6400, 0.3300
* green: 0.3000, 0.6000
* blue: 0.1500, 0.0600
* white point: 0.3127, 0.3290
*
* value = (uint32_t)(value * 1024 + 0.5)
*
* red_x = 0.6400 * 1024 + 0.5 = 655.86 => 655
* red_y = 0.3300 * 1024 + 0.5 = 338.42 => 338
* green_x = 0.3000 * 1024 + 0.5 = 307.7 => 307
* green_y = 0.6000 * 1024 + 0.5 = 614.9 => 614
* blue_x = 0.1500 * 1024 + 0.5 = 154.1 => 154
* blue_y = 0.0600 * 1024 + 0.5 = 61.94 => 61
* white_x = 0.3127 * 1024 + 0.5 = 320.7048 => 320
* white_y = 0.3290 * 1024 + 0.5 = 337.396 => 337
*/
edid->red_green_lo = (((655 & 0x03) << 6) | /* red_x */
((338 & 0x03) << 4) | /* red_y */
((307 & 0x03) << 2) | /* green_x */
((614 & 0x03) << 0)); /* green_y */
edid->black_white_lo = (((154 & 0x03) << 6) | /* blue_x */
((154 & 0x03) << 4) | /* blue_y */
((320 & 0x03) << 2) | /* white_x */
((337 & 0x03) << 0)); /* white_y */
edid->red_x = 655 >> 2; /* red_x */
edid->red_y = 338 >> 2; /* red_y */
edid->green_x = 307 >> 2; /* green_x */
edid->green_y = 614 >> 2; /* green_y */
edid->blue_x = 154 >> 2; /* blue_x */
edid->blue_y = 154 >> 2; /* blue_y */
edid->white_x = 320 >> 2; /* white_x */
edid->white_y = 337 >> 2; /* white_y */
/* EDID: established timing bitmap */
/* EDID: standard timing information */
/* EDID: descriptor blocks */
dt = &edid->detailed_timings[dt_idx++];
dpt = &dt->data.pixel_data;
dt->pixel_clock = rt_cpu_to_le16(clock);
dpt->hactive_lo = width & 0xff;
dpt->hblank_lo = xblank & 0xff;
dpt->hactive_hblank_hi = (((width & 0xf00) >> 4) | ((xblank & 0xf00) >> 8));
dpt->vactive_lo = height & 0xff;
dpt->vblank_lo = yblank & 0xff;
dpt->vactive_vblank_hi = (((height & 0xf00) >> 4) | ((yblank & 0xf00) >> 8));
dpt->hsync_offset_lo = xfront & 0xff;
dpt->hsync_pulse_width_lo = xsync & 0xff;
dpt->vsync_offset_pulse_width_lo = (((yfront & 0x00f) << 4) |
((ysync & 0x00f) << 0));
dpt->hsync_vsync_offset_pulse_width_hi = (((xfront & 0x300) >> 2) |
((xsync & 0x300) >> 4) |
((yfront & 0x030) >> 2) |
((ysync & 0x030) >> 4));
dpt->width_mm_lo = width_mm & 0xff;
dpt->height_mm_lo = height_mm & 0xff;
dpt->width_height_mm_hi = (((width_mm & 0xf00) >> 4) |
((height_mm & 0xf00) >> 8));
dpt->misc = 0x18;
/* XTRA3 STD */
dt = &edid->detailed_timings[dt_idx++];
dnp = &dt->data.other_data;
dnp->type = EDID_DETAIL_EST_TIMINGS;
dnp->data.timings[0].hsize = 10;
/* Ranges */
dt = &edid->detailed_timings[dt_idx++];
dnp = &dt->data.other_data;
dnp->type = EDID_DETAIL_MONITOR_RANGE;
dnp->data.range.min_vfreq = 50;
dnp->data.range.max_vfreq = 125;
dnp->data.range.min_hfreq_khz = 30;
dnp->data.range.max_hfreq_khz = 160;
dnp->data.range.pixel_clock_mhz = 2550 / 10;
dnp->data.range.flags = 0x01;
rt_memcpy(&dnp->data.range.flags + 1, "\n ", 7);
while (dt_idx < RT_ARRAY_SIZE(edid->detailed_timings))
{
/* Dummy */
dt = &edid->detailed_timings[dt_idx++];
dnp = &dt->data.other_data;
dnp->type = 0x10;
}
/* EDID: display id extensions */
/* EDID: checksum */
edid_checksum((void *)edid, 127);
}
rt_err_t rt_graphic_device_simple_edid(struct rt_graphic_device *gdev,
rt_uint32_t width, rt_uint32_t height, rt_uint32_t refresh_hz)
{
if (!gdev || !width || !height)
{
return -RT_EINVAL;
}
fill_edid(gdev, width, height, refresh_hz);
return RT_EOK;
}
static const struct rt_graphic_device_ops graphic_device_simple_ops =
{
};
rt_err_t rt_graphic_device_simple_register(struct rt_graphic_device *gdev,
rt_uint32_t width, rt_uint32_t height, rt_uint32_t refresh_hz,
const struct rt_graphic_plane_ops *plane_ops,
const rt_uint32_t *modes, rt_uint32_t modes_nr)
{
rt_err_t err;
struct rt_graphic_plane *plane;
if (!gdev || !width || !height || !plane_ops || !modes || !modes_nr)
{
return -RT_EINVAL;
}
if (!gdev->ops)
{
gdev->ops = &graphic_device_simple_ops;
}
plane = rt_graphic_device_alloc_plane(gdev, 0, plane_ops, modes, modes_nr,
RT_GRAPHIC_PLANE_TYPE_PRIMARY);
if (!plane)
{
return -RT_EINVAL;
}
if ((err = rt_graphic_device_add_plane(gdev, plane)))
{
goto _free_plane;
}
rt_graphic_device_simple_edid(gdev, width, height, refresh_hz);
err = rt_graphic_device_register(gdev);
_free_plane:
if (err)
{
rt_free(plane);
}
return err;
}
rt_err_t rt_graphic_device_simple_unregister(struct rt_graphic_device *gdev)
{
return rt_graphic_device_unregister(gdev);
}

View File

@@ -0,0 +1 @@
logo.inc

View File

@@ -0,0 +1,31 @@
menuconfig RT_GRAPHIC_LOGO
bool "Startup Logo"
select RT_GRAPHIC_FB
default y
choice
prompt "Rendering image(ppm)"
default RT_GRAPHIC_LOGO_RT_THREAD_CLUT224
depends on RT_GRAPHIC_LOGO
config RT_GRAPHIC_LOGO_NONE
bool "None logo (Change in runtime)"
osource "$(SOC_DM_GRAPHIC_LOGO_DIR)/Kconfig"
endchoice
# Provide the logos path for the BSP path:
#
# RT_GRAPHIC_LOGO_<XYZ> in Kconfig:
#
# config RT_GRAPHIC_LOGO_<XYZ>
# bool "<XYZ> logo"
#
# RT_GRAPHIC_LOGO_<XYZ>_PATH in Kconfig.path:
#
# if RT_GRAPHIC_LOGO_<XYZ>
# config RT_GRAPHIC_LOGO_<XYZ>_PATH
# string
# default "dm/graphic/logo/[filename].ppm"
# endif
osource "$(SOC_DM_GRAPHIC_LOGO_DIR)/Kconfig.path"

View File

@@ -0,0 +1,84 @@
from building import *
import os, re
group = []
if not GetDepend(['RT_GRAPHIC_LOGO']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
CPPDEFINES = []
src = ['logo.c']
logo_path = None
logo_width = 0
logo_height = 0
logo_max_val = 0
if logo_path == None:
# Find in BSP
paths = None
for key in BuildOptions.keys():
if re.match(r'RT_GRAPHIC_LOGO_.*_PATH', key):
paths = BuildOptions[key]
break
if paths != None and len(paths) > 0:
logo_path = Dir('#').abspath + '/' + paths[1:-1]
if not os.path.exists(logo_path):
print("Logo file '{}' not found!".format(logo_path))
exit(-1)
if logo_path != None:
with open(logo_path, 'rb') as ppm:
data = ppm.read().split(b'\n')
# PPM: <magic number>
magic = data[0].decode('utf-8')
# PPM: <comment>
offset = 1
while True:
comment = str(data[offset].decode('utf-8'))
if comment[0] != '#':
break
offset += 1
# PPM: <width> <height>
logo_width, logo_height = map(int, data[offset].split())
# PPM: <max pixel value>
logo_max_val = int(data[offset + 1])
# PPM: <data>
ppm.seek(0)
pixels = b''.join(ppm.readlines()[offset + 2:])
ppm.close()
if magic == 'P1' or magic == 'P2' or magic == 'P3':
# ASCII
pixels = re.sub(b'\\s+', b'\n', pixels.strip()).decode('utf-8').split('\n')
logo = open(cwd + '/logo.inc', "w")
for dy in range(logo_height):
for dx in range(logo_width):
index = (dy * logo_width + dx) * 3
# Red
logo.write(str(pixels[index]).rjust(4) + ",")
# Green
logo.write(str(pixels[index + 1]).rjust(4) + ",")
# Blue
logo.write(str(pixels[index + 2]).rjust(4) + ",")
logo.write("\n")
logo.close()
CPPDEFINES += ['__STARTUP_LOGO_WIDTH__=' + str(logo_width)]
CPPDEFINES += ['__STARTUP_LOGO_HEIGHT__=' + str(logo_height)]
CPPDEFINES += ['__STARTUP_LOGO_COLOR_MAX__=' + str(logo_max_val)]
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES)
Return('group')

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "graphic.logo"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#if __STARTUP_LOGO_WIDTH__ && __STARTUP_LOGO_HEIGHT__ && __STARTUP_LOGO_COLOR_MAX__
static rt_uint8_t builtin_logo[] =
{
#include "logo.inc"
};
static void *startup_logo = builtin_logo;
#else
static void *startup_logo = RT_NULL;
#endif
static int startup_logo_width = __STARTUP_LOGO_WIDTH__;
static int startup_logo_height = __STARTUP_LOGO_HEIGHT__;
static int startup_logo_color_max = __STARTUP_LOGO_COLOR_MAX__;
rt_err_t rt_graphic_logo_change(void *data, int width, int height, int color_max)
{
if (!data && !width && !height && !color_max)
{
/* Disable logo */
startup_logo = RT_NULL;
}
else if (data && width > 0 && height > 0 && color_max > 0)
{
startup_logo = data;
startup_logo_width = width;
startup_logo_height = height;
startup_logo_color_max = color_max;
}
else
{
return -RT_EINVAL;
}
return RT_EOK;
}
static rt_ubase_t to_grayscale(rt_ubase_t red, rt_ubase_t green, rt_ubase_t blue)
{
return (299 * red + 587 * green + 114 * blue) / 1000;
}
static rt_ubase_t to_color(rt_ubase_t color, rt_ubase_t in_color_max, rt_ubase_t out_color_max)
{
return color * out_color_max / in_color_max;
}
static rt_ubase_t gray_reordering(rt_ubase_t red, rt_ubase_t red_off,
rt_ubase_t green, rt_ubase_t green_off,
rt_ubase_t blue, rt_ubase_t blue_off,
rt_ubase_t in_color_max, rt_ubase_t out_color_max)
{
return to_grayscale(to_color(red, in_color_max, out_color_max),
to_color(green, in_color_max, out_color_max),
to_color(blue, in_color_max, out_color_max));
}
static rt_ubase_t rgb_reordering(rt_ubase_t red, rt_ubase_t red_off,
rt_ubase_t green, rt_ubase_t green_off,
rt_ubase_t blue, rt_ubase_t blue_off,
rt_ubase_t in_color_max, rt_ubase_t out_color_max)
{
return (to_color(red, in_color_max, out_color_max) << red_off) |
(to_color(green, in_color_max, out_color_max) << green_off) |
(to_color(blue, in_color_max, out_color_max) << blue_off);
}
rt_err_t rt_graphic_logo_render(struct rt_graphic_device *gdev)
{
rt_err_t err;
int fb_color_max;
rt_ubase_t xlate, none_alpha;
rt_ubase_t red_off, green_off, blue_off;
rt_ubase_t red_mask, green_mask, blue_mask;
rt_uint8_t *logo, *fb, bytes_per_pixel;
rt_ubase_t (*color_reordering)(rt_ubase_t, rt_ubase_t,
rt_ubase_t, rt_ubase_t,
rt_ubase_t, rt_ubase_t,
rt_ubase_t, rt_ubase_t);
struct fb_var_screeninfo var;
struct rt_device_rect_info rect;
struct rt_device_graphic_info info;
struct rt_device *fbdev = &gdev->parent;
if (!startup_logo)
{
return RT_EOK;
}
if ((err = rt_device_open(fbdev, 0)))
{
return err;
}
if ((err = rt_device_control(fbdev, FBIOGET_VSCREENINFO, &var)))
{
LOG_E("Get framebuffer %s error = %s", "var", rt_strerror(err));
goto _close_fbdev;
}
if (startup_logo_width > var.xres || startup_logo_height > var.yres)
{
LOG_E("PPM logo[%u, %u] Out of screen[%u, %u]",
startup_logo_width, startup_logo_height, var.xres, var.yres);
err = -RT_EINVAL;
goto _close_fbdev;
}
if ((err = rt_device_control(fbdev, RTGRAPHIC_CTRL_GET_INFO, &info)))
{
LOG_E("Get framebuffer %s error = %s", "info", rt_strerror(err));
goto _close_fbdev;
}
if ((err = rt_device_control(fbdev, RTGRAPHIC_CTRL_POWERON, RT_NULL)))
{
LOG_E("Power on graphic device error = %s", rt_strerror(err));
goto _close_fbdev;
}
if (var.grayscale)
{
color_reordering = &gray_reordering;
}
else
{
color_reordering = &rgb_reordering;
}
bytes_per_pixel = var.bits_per_pixel / 8;
xlate = (var.xres - startup_logo_width) * bytes_per_pixel;
rect.x = (var.xres - startup_logo_width) >> 1;
rect.y = (var.yres - startup_logo_height) >> 1;
rect.width = startup_logo_width,
rect.height = startup_logo_height,
fb = (void *)info.framebuffer;
fb += rect.x * bytes_per_pixel + rect.y * info.pitch;
logo = startup_logo;
red_off = var.red.offset;
red_mask = RT_GENMASK(var.red.length - 1, 0);
green_off = var.green.offset;
green_mask = RT_GENMASK(var.green.length - 1, 0);
blue_off = var.blue.offset;
blue_mask = RT_GENMASK(var.blue.length - 1, 0);
fb_color_max = rt_max_t(int, rt_max_t(int, red_mask, green_mask), blue_mask);
if (var.transp.length)
{
none_alpha = RT_GENMASK(var.transp.length - 1, 0) << var.transp.offset;
}
else
{
none_alpha = 0;
}
for (int dy = 0; dy < startup_logo_height; ++dy)
{
for (int dx = 0; dx < startup_logo_width; ++dx)
{
rt_ubase_t color = color_reordering(logo[0], red_off,
logo[1], green_off,
logo[2], blue_off,
fb_color_max, startup_logo_color_max) |
none_alpha;
rt_memcpy(fb, &color, bytes_per_pixel);
fb += bytes_per_pixel;
logo += 3;
}
fb += xlate;
}
rt_device_control(fbdev, RTGRAPHIC_CTRL_RECT_UPDATE, &rect);
rt_device_control(fbdev, RTGRAPHIC_CTRL_WAIT_VSYNC, RT_NULL);
/* Complete */
startup_logo = RT_NULL;
/*
* Should recycle here, logo takes up too much memory
* if builtin is not RT_GRAPHIC_LOGO_NONE.
*/
_close_fbdev:
rt_device_close(fbdev);
return err;
}

View File

@@ -0,0 +1,243 @@
<!--
Copyright (c) 2006-2023, RT-Thread Development Team
SPDX-License-Identifier: Apache-2.0
Change Logs:
Date Author Notes
2023-02-25 GuEe-GUI the first version
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="https://www.rt-thread.org/favicon.ico" type="image/x-icon">
<title>Logo PPM Previews</title>
<style type="text/css">
body {
min-width: 860px;
margin: 0px;
padding: 0px;
font-family: Consolas, "Cascadia Code", Monaco;
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB2aWV3Qm94PSIwIDAgMTAgMTAiIGlkPSJjb2RlLWJhY2tncm91bmQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPg0KICAgIDx0aXRsZT5jb2RlLWJhY2tncm91bmQ8L3RpdGxlPg0KICAgIDxnPg0KICAgICAgICA8bGluZSB5Mj0iMTAiIHgyPSIxMCIgeTE9IjAiIHgxPSIxMCIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiNlNWU1ZTUiLz4NCiAgICAgICAgPGxpbmUgeTI9IjEwIiB4Mj0iMTAiIHkxPSIxMCIgeDE9IjAiIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlPSIjZTVlNWU1Ii8+DQogICAgPC9nPg0KPC9zdmc+);
}
.main {
display: flex;
justify-content: center;
}
.controls > div {
padding: 10px;
height: 52px;
line-height: 52px;
}
label {
user-select: none;
font-size: 24px;
font-weight: 400;
color: #494949;
}
.res-input, .ppm-input, #background-color {
cursor: pointer;
border: 2px solid #494949;
border-radius: 8px;
outline: none;
font-size: 18px;
background-color: #fff;
}
.res-input {
height: 32px;
padding: 0 4px;
line-height: 52px;
}
.background-color, .ppm-input {
display: block;
text-align: center;
}
#background-color {
width: 20%;
height: 32px;
}
.ppm-input:hover {
background-color: #eee;
}
#ppm-input {
display: none;
}
.canvas {
justify-content: center;
display: flex;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>
<div class="controls">
<div style="display: flex; justify-content: center;">
<div>
<label class="resolution">Screen Resolution</label>
<input type="number" class="res-input" id="canvas-width" placeholder="width" min="1" step="1">
<label class="resolution">&times;</label>
<input type="number" class="res-input" id="canvas-height" placeholder="height" min="1" step="1">
</div>
</div>
<div>
<label class="background-color" for="background-color">
<span>Background Color</span>
<input type="color" id="background-color">
</label>
</div>
<div>
<label class="ppm-input">
<input type="file" id="ppm-input" accept=".ppm" placeholder="PPM">
<span>Open PPM File</span>
</label>
</div>
</div>
<div class="canvas">
<canvas id="canvas"></canvas>
</div>
</div>
</div>
</body>
<script type="text/javascript">
var Screen = new Object;
window.onload = function() {
Screen.canvas = document.getElementById('canvas');
Screen.ctx = canvas.getContext('2d');
Screen.ppmWidth = 0;
Screen.ppmHeight = 0;
Screen.ppmPixels = [];
Screen.widthInput = document.getElementById('canvas-width');
Screen.heightInput = document.getElementById('canvas-height');
Screen.ColorPicker = document.getElementById('background-color');
Screen.widthInput.value = 1;
Screen.heightInput.value = 1;
Screen.ColorPicker.value = "#000000";
document.getElementById('canvas-width').addEventListener('input', function(e) {
applyCanvasSize();
});
document.getElementById('canvas-height').addEventListener('input', function(e) {
applyCanvasSize();
});
document.getElementById('background-color').addEventListener('input', function(e) {
applyBackgroundColor();
});
document.getElementById('ppm-input').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const ppm_data = e.target.result;
const { width, height, pixels } = parsePPM(ppm_data);
Screen.ppmWidth = width;
Screen.ppmHeight = height;
Screen.ppmPixels = pixels;
Screen.ctx.reset();
applyBackgroundColor();
};
reader.readAsText(file);
});
function parsePPM(data) {
let cursor = 0;
const lines = data.split('\n').filter(line => !line.startsWith('#') && line.trim() !== '');
const magic = lines[cursor++].trim();
if (!['P1', 'P2', 'P3'].includes(magic)) {
alert('Unsupported format');
return;
}
let maxVal = 1;
const [width, height] = lines[cursor++].split(/\s+/).map(Number);
if (magic !== 'P1') {
maxVal = parseInt(lines[cursor++]);
}
const pixels = [];
const values = lines.slice(cursor).join(' ').split(/\s+/).filter(v => v !== '');
if (magic === 'P1' || magic === 'P2') {
for (var i = 0; i < values.length; ++i) {
const val = parseInt(values[i]);
const scaled = magic === 'P1' ? val * 255 : Math.round((val / maxVal) * 255);
pixels.push([scaled, scaled, scaled]);
}
} else if (magic === 'P3') {
for (var i = 0; i < values.length; i += 3) {
const r = Math.round((values[i] / maxVal) * 255);
const g = Math.round((values[i+1] / maxVal) * 255);
const b = Math.round((values[i+2] / maxVal) * 255);
pixels.push([r, g, b]);
}
}
return { width, height, pixels };
}
function renderPPM() {
if (Screen.ppmWidth == 0 || Screen.ppmHeight == 0) {
return;
}
const imageData = Screen.ctx.createImageData(Screen.ppmWidth, Screen.ppmHeight);
for (var i = 0; i < Screen.ppmPixels.length; ++i) {
const [r, g, b] = Screen.ppmPixels[i];
const j = i * 4;
imageData.data[j] = r;
imageData.data[j + 1] = g;
imageData.data[j + 2] = b;
imageData.data[j + 3] = 255;
}
const offsetX = (Screen.canvas.width - Screen.ppmWidth) / 2;
const offsetY = (Screen.canvas.height - Screen.ppmHeight) / 2;
Screen.ctx.putImageData(imageData, offsetX, offsetY);
}
function applyCanvasSize() {
var canvasWidth = parseInt(Screen.widthInput.value);
var canvasHeight = parseInt(Screen.heightInput.value);
if (canvasWidth == 0 || canvasHeight == 0) {
return;
}
Screen.canvas.width = canvasWidth;
Screen.canvas.height = canvasHeight;
Screen.canvas.style.boxShadow = "1px 1px 2px rgba(0, 0, 0, 0.2)";
applyBackgroundColor();
}
function applyBackgroundColor() {
Screen.ctx.fillStyle = Screen.ColorPicker.value;
Screen.ctx.fillRect(0, 0, Screen.canvas.width, Screen.canvas.height);
renderPPM();
}
}
</script>
</html>

View File

@@ -0,0 +1,367 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#ifndef __GRAPHIC_DM_H__
#define __GRAPHIC_DM_H__
#include <rthw.h>
#include <rtthread.h>
#include <drivers/lcd.h>
#include <drivers/core/dm.h>
#include <drivers/byteorder.h>
#include <drivers/classes/graphic.h>
#undef rt_graphix_ops
#define rt_graphix_ops(dev) \
rt_graphic_device_switch_primary(rt_container_of(dev, struct rt_graphic_device, parent))
rt_packed(struct est_timings
{
rt_uint8_t t1;
rt_uint8_t t2;
rt_uint8_t mfg_rsvd;
});
rt_packed(struct std_timing
{
/* Need to multiply by 8 then add 248 */
rt_uint8_t hsize;
rt_uint8_t vfreq_aspect;
});
rt_packed(struct detailed_pixel_timing
{
rt_uint8_t hactive_lo;
rt_uint8_t hblank_lo;
rt_uint8_t hactive_hblank_hi;
rt_uint8_t vactive_lo;
rt_uint8_t vblank_lo;
rt_uint8_t vactive_vblank_hi;
rt_uint8_t hsync_offset_lo;
rt_uint8_t hsync_pulse_width_lo;
rt_uint8_t vsync_offset_pulse_width_lo;
rt_uint8_t hsync_vsync_offset_pulse_width_hi;
rt_uint8_t width_mm_lo;
rt_uint8_t height_mm_lo;
rt_uint8_t width_height_mm_hi;
rt_uint8_t hborder;
rt_uint8_t vborder;
rt_uint8_t misc;
});
rt_packed(struct detailed_data_string
{
rt_uint8_t str[13];
});
rt_packed(struct detailed_data_monitor_range
{
rt_uint8_t min_vfreq;
rt_uint8_t max_vfreq;
rt_uint8_t min_hfreq_khz;
rt_uint8_t max_hfreq_khz;
/* Need to multiply by 10 */
rt_uint8_t pixel_clock_mhz;
rt_uint8_t flags;
union
{
rt_packed(struct
{
rt_uint8_t reserved;
/* Need to multiply by 2 */
rt_uint8_t hfreq_start_khz;
/* Need to divide by 2 */
rt_uint8_t c;
rt_le16_t m;
rt_uint8_t k;
/* Need to divide by 2 */
rt_uint8_t j;
}) gtf2;
rt_packed(struct
{
rt_uint8_t version;
/* High 6 bits: extra clock resolution */
rt_uint8_t data1;
/* Plus low 2 of above: max hactive */
rt_uint8_t data2;
rt_uint8_t supported_aspects;
/* Preferred aspect and blanking support */
rt_uint8_t flags;
rt_uint8_t supported_scalings;
rt_uint8_t preferred_refresh;
}) cvt;
} formula;
});
rt_packed(struct detailed_data_wpindex
{
/* Lower 2 bits each */
rt_uint8_t white_yx_lo;
rt_uint8_t white_x_hi;
rt_uint8_t white_y_hi;
/* Need to divide by 100 then add 1 */
rt_uint8_t gamma;
});
rt_packed(struct cvt_timing
{
rt_uint8_t code[3];
});
rt_packed(struct detailed_non_pixel
{
rt_uint8_t pad1;
#define EDID_DETAIL_EST_TIMINGS 0xf7
#define EDID_DETAIL_CVT_3BYTE 0xf8
#define EDID_DETAIL_COLOR_MGMT_DATA 0xf9
#define EDID_DETAIL_STD_MODES 0xfa
#define EDID_DETAIL_MONITOR_CPDATA 0xfb
#define EDID_DETAIL_MONITOR_NAME 0xfc
#define EDID_DETAIL_MONITOR_RANGE 0xfd
#define EDID_DETAIL_MONITOR_STRING 0xfe
#define EDID_DETAIL_MONITOR_SERIAL 0xff
rt_uint8_t type;
rt_uint8_t pad2;
union
{
struct detailed_data_string str;
struct detailed_data_monitor_range range;
struct detailed_data_wpindex color;
struct std_timing timings[6];
struct cvt_timing cvt[4];
} data;
});
rt_packed(struct detailed_timing
{
/* Need to multiply by 10 KHz */
rt_le16_t pixel_clock;
union
{
struct detailed_pixel_timing pixel_data;
struct detailed_non_pixel other_data;
} data;
});
rt_packed(struct edid
{
rt_uint8_t header[8];
/* Vendor & product info */
rt_uint8_t mfg_id[2];
rt_uint8_t prod_code[2];
rt_le32_t serial;
rt_uint8_t mfg_week;
rt_uint8_t mfg_year;
/* EDID version */
rt_uint8_t version;
rt_uint8_t revision;
/* Display info */
rt_uint8_t input;
rt_uint8_t width_cm;
rt_uint8_t height_cm;
rt_uint8_t gamma;
rt_uint8_t features;
/* Color characteristics */
rt_uint8_t red_green_lo;
rt_uint8_t black_white_lo;
rt_uint8_t red_x;
rt_uint8_t red_y;
rt_uint8_t green_x;
rt_uint8_t green_y;
rt_uint8_t blue_x;
rt_uint8_t blue_y;
rt_uint8_t white_x;
rt_uint8_t white_y;
/* Est. timings and mfg rsvd timings */
struct est_timings established_timings;
/* Standard timings 1-8 */
struct std_timing standard_timings[8];
/* Detailing timings 1-4, 18 * 4 = 72 bytes */
struct detailed_timing detailed_timings[4];
/* Number of 128 byte ext. blocks */
rt_uint8_t extensions;
/* Checksum */
rt_uint8_t checksum;
});
struct rt_graphic_device;
struct rt_graphic_device_ops;
struct rt_graphic_plane_ops;
enum rt_graphic_plane_prop
{
RT_GRAPHIC_PLANE_PROP_Z = 0,
RT_GRAPHIC_PLANE_PROP_ROTATE,
RT_GRAPHIC_PLANE_PROP_ALPHA,
RT_GRAPHIC_PLANE_PROP_MAX,
};
struct rt_graphic_plane
{
rt_list_t list;
char name[RT_NAME_MAX];
int id;
#define RT_GRAPHIC_PLANE_TYPE_OVERLAY 0
#define RT_GRAPHIC_PLANE_TYPE_PRIMARY 1 /* Only one, add before register */
#define RT_GRAPHIC_PLANE_TYPE_CURSOR 2 /* Only one */
rt_uint8_t type;
rt_uint32_t x;
rt_uint32_t y;
rt_uint32_t z;
rt_uint32_t width;
rt_uint32_t height;
#define RT_GRAPHIC_PLANE_ROTATE_0 0 /* +0 degrees */
#define RT_GRAPHIC_PLANE_ROTATE_90 1 /* +90 degrees */
#define RT_GRAPHIC_PLANE_ROTATE_180 2 /* +180 degrees */
#define RT_GRAPHIC_PLANE_ROTATE_270 3 /* +270 degrees */
rt_uint8_t rotate;
rt_uint8_t alpha; /* 0 ~ 100 */
rt_uint32_t line_length;
rt_uint32_t bits_per_pixel;
/* Support color modes: RTGRAPHIC_PIXEL_FORMAT_* */
rt_uint32_t mode;
rt_uint32_t modes_nr;
const rt_uint32_t *modes;
/* fb count = framebuffer_len / screen_len */
void *framebuffer;
rt_size_t screen_len;
rt_size_t framebuffer_len;
struct rt_graphic_device *graphic;
const struct rt_graphic_plane_ops *ops;
rt_uint8_t priv[0];
};
struct rt_graphic_plane_ops
{
rt_err_t (*update)(struct rt_graphic_plane *plane, struct rt_device_rect_info *rect);
rt_err_t (*fb_remap)(struct rt_graphic_plane *plane, rt_uint32_t mode, struct rt_device_rect_info *rect);
rt_err_t (*fb_pan_display)(struct rt_graphic_plane *plane, struct rt_device_rect_info *rect);
rt_err_t (*fb_cleanup)(struct rt_graphic_plane *plane);
rt_err_t (*prop_set)(struct rt_graphic_plane *plane, enum rt_graphic_plane_prop prop, void *value);
};
struct rt_graphic_device
{
struct rt_device parent;
const struct rt_graphic_device_ops *ops;
/* Display Power Manage System */
#define RT_GRAPHIC_DPMS_ON 0
#define RT_GRAPHIC_DPMS_STANDBY 1
#define RT_GRAPHIC_DPMS_SUSPEND 2
#define RT_GRAPHIC_DPMS_OFF 3
rt_uint32_t dpms;
rt_list_t overlay_nodes;
struct rt_graphic_plane *primary_plane;
struct rt_graphic_plane *cursor_plane;
struct rt_dm_ida plane_ida;
/* Display information */
struct edid edid;
#ifdef RT_GRAPHIC_BACKLIGHT
struct rt_backlight_device *backlight;
#endif
#define RT_GRAPHIC_UPDATE_MS 16
struct rt_timer *update_timer;
rt_atomic_t event_notifying;
struct rt_device_notify event_notify;
struct rt_spinlock lock;
};
struct rt_graphic_device_ops
{
rt_err_t (*dpms_switch)(struct rt_graphic_device *gdev, rt_uint32_t dpms);
rt_err_t (*set_brightness)(struct rt_graphic_device *gdev, rt_uint32_t brightness);
rt_err_t (*get_brightness)(struct rt_graphic_device *gdev, rt_uint32_t *out_brightness);
rt_err_t (*get_status)(struct rt_graphic_device *gdev, rt_uint32_t *out_status);
rt_err_t (*wait_vsync)(struct rt_graphic_device *gdev);
rt_err_t (*control)(struct rt_graphic_device *gdev, int cmd, void *args);
/* Switching planes supported by device driver */
struct rt_graphic_plane *(*current_plane)(struct rt_graphic_device *gdev);
};
rt_err_t rt_graphic_device_register(struct rt_graphic_device *gdev);
rt_err_t rt_graphic_device_unregister(struct rt_graphic_device *gdev);
struct rt_graphic_plane *rt_graphic_device_alloc_plane(struct rt_graphic_device *gdev,
rt_size_t priv_size, const struct rt_graphic_plane_ops *ops,
const rt_uint32_t *modes, rt_uint32_t modes_nr, rt_uint8_t type);
void rt_graphic_device_free_plane(struct rt_graphic_plane *plane);
rt_err_t rt_graphic_device_add_plane(struct rt_graphic_device *gdev,
struct rt_graphic_plane *plane);
rt_err_t rt_graphic_device_del_plane(struct rt_graphic_device *gdev,
struct rt_graphic_plane *plane);
void rt_graphic_device_hotplug_event(struct rt_graphic_device *gdev);
rt_err_t rt_graphic_device_update_auto(struct rt_graphic_device *gdev, rt_uint32_t update_ms);
void rt_graphic_device_enter(struct rt_graphic_device *gdev);
void rt_graphic_device_leave(struct rt_graphic_device *gdev);
rt_uint32_t rt_graphic_mode_bpp(rt_uint32_t mode);
struct rt_device_graphic_ops *rt_graphic_device_switch_primary(struct rt_graphic_device *gdev);
rt_err_t rt_graphic_device_simple_edid(struct rt_graphic_device *gdev,
rt_uint32_t width, rt_uint32_t height, rt_uint32_t refresh_hz);
rt_err_t rt_graphic_device_simple_register(struct rt_graphic_device *gdev,
rt_uint32_t width, rt_uint32_t height, rt_uint32_t refresh_hz,
const struct rt_graphic_plane_ops *plane_ops,
const rt_uint32_t *modes, rt_uint32_t modes_nr);
rt_err_t rt_graphic_device_simple_unregister(struct rt_graphic_device *gdev);
#ifdef RT_GRAPHIC_LOGO
rt_err_t rt_graphic_logo_change(void *data, int width, int height, int color_max);
rt_err_t rt_graphic_logo_render(struct rt_graphic_device *gdev);
#else
rt_inline rt_err_t rt_graphic_logo_change(void *data, int width, int height, int color_max)
{
return RT_EOK;
}
rt_inline rt_err_t rt_graphic_logo_render(struct rt_graphic_device *gdev)
{
return RT_EOK;
}
#endif /* RT_GRAPHIC_LOGO */
#endif /* __GRAPHIC_DM_H__ */

View File

@@ -11,6 +11,8 @@
#ifndef RT_LCD_H__
#define RT_LCD_H__
#include <stdint.h>
/* ioctls
0x46 is 'F' */
@@ -109,4 +111,35 @@ struct fb_fix_screeninfo
uint16_t reserved[2]; /* Reserved for future compatibility */
};
struct fb_cmap
{
uint32_t start; /* First entry */
uint32_t len; /* Number of entries */
uint16_t *red; /* Red values */
uint16_t *green;
uint16_t *blue;
uint16_t *transp; /* transparency, can be NULL */
};
struct fb_con2fbmap
{
uint32_t console;
uint32_t framebuffer;
};
/* VESA Blanking Levels */
#define VESA_NO_BLANKING 0
#define VESA_VSYNC_SUSPEND 1
#define VESA_HSYNC_SUSPEND 2
#define VESA_POWERDOWN 3
enum
{
FB_BLANK_UNBLANK = VESA_NO_BLANKING, /* screen: unblanked, hsync: on, vsync: on */
FB_BLANK_NORMAL = VESA_NO_BLANKING + 1, /* screen: blanked, hsync: on, vsync: on */
FB_BLANK_VSYNC_SUSPEND = VESA_VSYNC_SUSPEND + 1, /* screen: blanked, hsync: on, vsync: off */
FB_BLANK_HSYNC_SUSPEND = VESA_HSYNC_SUSPEND + 1, /* screen: blanked, hsync: off, vsync: on */
FB_BLANK_POWERDOWN = VESA_POWERDOWN + 1, /* screen: blanked, hsync: off, vsync: off */
};
#endif

View File

@@ -0,0 +1,310 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/misc.h>
#ifdef RT_USING_GRAPHIC
typedef rt_int32_t fixed; /* Q16.16 */
#define FIX_ONE 65536
#define FIX_06 19661 /* 0.3 */
#define FIX_35 22938 /* 0.35 */
#define FIX_14 917504 /* 14.0 */
#define FIX_286 187432 /* 2.86 */
#define FIX_75 49152 /* 0.75 */
#define FIX_1_3 21845
#define FIX_2_3 43690
#define FIX_6 393216
#define FIX_3 196608
#define FIX_255 16711680 /* 255 */
#define FMUL(a,b) ((fixed)(((rt_int64_t)(a) * (b)) >> 16))
#define FDIV(a,b) ((fixed)(((rt_int64_t)(a) << 16) / (b)))
typedef struct { fixed x, y, z; } vec3f;
rt_inline fixed fix_floor(fixed x)
{
return x & 0xffff0000;
}
rt_inline fixed fix_fract(fixed x)
{
return x & 0x0000ffff;
}
rt_inline fixed fix_abs(fixed x)
{
return rt_abs(x);
}
rt_inline fixed fix_clamp(fixed x, fixed a, fixed b)
{
return rt_clamp(x, a, b);
}
static vec3f clamp3(vec3f v, fixed a, fixed b)
{
vec3f r =
{
.x = fix_clamp(v.x, a, b),
.y = fix_clamp(v.y, a, b),
.z = fix_clamp(v.z, a, b),
};
return r;
}
static vec3f mix3(vec3f A, vec3f B, fixed t)
{
fixed omt = FIX_ONE - t;
vec3f r =
{
.x = FMUL(A.x, omt) + FMUL(B.x, t),
.y = FMUL(A.y, omt) + FMUL(B.y, t),
.z = FMUL(A.z, omt) + FMUL(B.z, t),
};
return r;
}
static vec3f hsv2rgb(vec3f c)
{
vec3f t, r, base, mixed;
fixed pX = FMUL(fix_fract(c.x + FIX_ONE), FIX_6);
fixed pY = FMUL(fix_fract(c.x + FIX_2_3), FIX_6);
fixed pZ = FMUL(fix_fract(c.x + FIX_1_3), FIX_6);
pX = fix_abs(pX - FIX_3);
pY = fix_abs(pY - FIX_3);
pZ = fix_abs(pZ - FIX_3);
t.x = pX - FIX_ONE;
t.y = pY - FIX_ONE;
t.z = pZ - FIX_ONE;
t = clamp3(t, 0, FIX_ONE);
base.x = FIX_ONE;
base.y = FIX_ONE;
base.z = FIX_ONE;
mixed = mix3(base, t, c.y);
r.x = FMUL(c.z, mixed.x);
r.y = FMUL(c.z, mixed.y);
r.z = FMUL(c.z, mixed.z);
return r;
}
static void shader_frame(int px, int py, int width, int height, int frame,
rt_uint8_t *r, rt_uint8_t *g, rt_uint8_t *b)
{
vec3f col;
fixed uv_x, uv_y, shift, size_computed, st, x;
uv_x = FDIV((fixed)(px << 16), (fixed)(width << 16));
uv_y = FDIV((fixed)(py << 16), (fixed)(width << 16));
shift = fix_fract(FDIV(FMUL((frame << 16), FIX_06), FIX_286));
shift = FMUL(shift, FIX_286);
size_computed = FMUL(uv_x + shift - uv_y, FIX_35);
st = FMUL(size_computed, FIX_14);
x = FDIV(fix_floor(st), FIX_14);
col = hsv2rgb((vec3f){ x, FIX_75, FIX_ONE });
*r = (rt_uint8_t)((FMUL(col.x, FIX_255) >> 16) & 255);
*g = (rt_uint8_t)((FMUL(col.y, FIX_255) >> 16) & 255);
*b = (rt_uint8_t)((FMUL(col.z, FIX_255) >> 16) & 255);
}
static void conv_gray4(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
rt_uint8_t gray = (r * 30 + g * 59 + b * 11) / 100;
*(rt_uint8_t *)pixel = gray >> 4;
}
static void conv_gray16(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
rt_uint16_t gray = (r * 30 + g * 59 + b * 11) / 100;
rt_uint16_t out = (gray << 8) | gray;
*(rt_uint16_t *)pixel = out;
}
static void conv_rgb332(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint8_t*)pixel = ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
}
static void conv_rgb444(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint16_t *)pixel = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4);
}
static void conv_rgb565(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint16_t *)pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
static void conv_rgb565p(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint16_t *)pixel = ((g >> 2) << 10) | ((r >> 3) << 5) | (b >> 3);
}
static void conv_bgr565(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint16_t *)pixel = ((b >> 3) << 11) | ((g >> 2) << 5) | (r >> 3);
}
static void conv_rgb666(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
rt_uint8_t *p = (rt_uint8_t *)pixel;
p[0] = r & 0xfc;
p[1] = g & 0xfc;
p[2] = b & 0xfc;
}
static void conv_rgb888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
rt_uint8_t *p = (rt_uint8_t *)pixel;
p[0] = r;
p[1] = g;
p[2] = b;
}
static void conv_bgr888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
rt_uint8_t *p = (rt_uint8_t*)pixel;
p[0] = b;
p[1] = g;
p[2] = r;
}
static void conv_argb888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint32_t *)pixel = (0xffU << 24) | (r << 16) | (g << 8) | b;
}
static void conv_abgr888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel)
{
*(rt_uint32_t *)pixel = (0xffU << 24) | (b << 16) | (g << 8) | r;
}
static void (*conv_funcs[])(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) =
{
[RTGRAPHIC_PIXEL_FORMAT_GRAY4] = conv_gray4,
[RTGRAPHIC_PIXEL_FORMAT_GRAY16] = conv_gray16,
[RTGRAPHIC_PIXEL_FORMAT_RGB332] = conv_rgb332,
[RTGRAPHIC_PIXEL_FORMAT_RGB444] = conv_rgb444,
[RTGRAPHIC_PIXEL_FORMAT_RGB565] = conv_rgb565,
[RTGRAPHIC_PIXEL_FORMAT_RGB565P] = conv_rgb565p,
[RTGRAPHIC_PIXEL_FORMAT_BGR565] = conv_bgr565,
[RTGRAPHIC_PIXEL_FORMAT_RGB666] = conv_rgb666,
[RTGRAPHIC_PIXEL_FORMAT_RGB888] = conv_rgb888,
[RTGRAPHIC_PIXEL_FORMAT_BGR888] = conv_bgr888,
[RTGRAPHIC_PIXEL_FORMAT_ARGB888] = conv_argb888,
[RTGRAPHIC_PIXEL_FORMAT_ABGR888] = conv_abgr888,
};
rt_err_t graphic_start(const char *gdev, int count)
{
rt_err_t err;
rt_uint8_t *vfb, *fb, *pixel, bpp;
struct rt_device_graphic_info info;
struct rt_device *dev = rt_device_find(gdev);
void (*conv_func)(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel);
if (!dev)
{
return -RT_EINVAL;
}
if ((err = rt_device_open(dev, 0)))
{
return err;
}
if ((err = rt_device_control(dev, RTGRAPHIC_CTRL_GET_INFO, &info)))
{
goto _end;
}
if (!(vfb = rt_malloc(info.smem_len)))
{
err = -RT_ENOMEM;
goto _end;
}
bpp = info.bits_per_pixel / 8;
conv_func = conv_funcs[info.pixel_format];
for (int frame = 0; frame < count; ++frame)
{
fb = vfb;
for (int y = 0; y < info.height; ++y)
{
pixel = fb;
for (int x = 0; x < info.width; ++x)
{
rt_uint8_t r, g, b;
shader_frame(x, y, info.width, info.height, frame, &r, &g, &b);
conv_func(r, g, b, pixel);
pixel += bpp;
}
fb += info.pitch;
}
rt_memcpy(info.framebuffer, vfb, info.smem_len);
}
rt_free(vfb);
_end:
rt_device_close(dev);
return err;
}
#ifdef RT_USING_FINSH
#include <stdlib.h>
static int _graphic_start(int argc, char**argv)
{
int count = 10;
const char *gdev = "fb0";
if (argc > 1)
{
gdev = argv[1];
}
if (argc > 2)
{
count = atoi(argv[2]);
}
return (int)graphic_start(gdev, count);
}
MSH_CMD_EXPORT_ALIAS(_graphic_start, graphic_start, fixed resolution only e.g: graphic_start("fb0", 10));
#endif /* RT_USING_FINSH */
#endif /* RT_USING_GRAPHIC */

421
examples/test/dm_hmi_test.c Normal file
View File

@@ -0,0 +1,421 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <cpuport.h>
#include <drivers/misc.h>
#if defined(RT_USING_GRAPHIC) && defined(RT_USING_INPUT)
#define CURSOR_WIDTH 64
#define CURSOR_HEIGHT 64
struct hmi_info
{
struct rt_device *gdev;
struct rt_device *idev;
struct rt_device_graphic_info info;
struct rt_device_notify event_notify;
struct rt_input_handler handler;
rt_bool_t event;
rt_bool_t vsync;
rt_bool_t keydown;
rt_uint32_t x, y;
rt_uint32_t dx, dy;
rt_uint32_t bytes_per_pixel;
rt_ubase_t line[2];
rt_ubase_t colors[4];
void *backend_framebuffer;
};
static rt_bool_t hmi_working;
static struct rt_input_device *to_input_device(struct rt_device *idev)
{
return rt_container_of(idev, struct rt_input_device, parent);
}
static rt_ubase_t to_color(rt_uint8_t color255, rt_ubase_t color_max)
{
return (rt_ubase_t)color255 * color_max / 255;
}
static void hmi_reset(struct hmi_info *hmi)
{
void *cursor;
rt_ubase_t none_alpha;
rt_ubase_t red_off, green_off, blue_off, alpha_off;
rt_ubase_t red_mask, green_mask, blue_mask, alpha_mask;
struct fb_var_screeninfo var;
if (hmi->backend_framebuffer)
{
rt_free(hmi->backend_framebuffer);
}
rt_device_control(hmi->gdev, FBIOGET_VSCREENINFO, &var);
rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_GET_INFO, &hmi->info);
hmi->backend_framebuffer = rt_malloc(hmi->info.smem_len);
hmi->bytes_per_pixel = hmi->info.bits_per_pixel / 8;
red_off = var.red.offset;
red_mask = RT_GENMASK(var.red.length - 1, 0);
green_off = var.green.offset;
green_mask = RT_GENMASK(var.green.length - 1, 0);
blue_off = var.blue.offset;
blue_mask = RT_GENMASK(var.blue.length - 1, 0);
if (var.transp.length)
{
alpha_off = var.transp.offset;
alpha_mask = RT_GENMASK(var.transp.length - 1, 0);
}
else
{
alpha_off = 0;
alpha_mask = 0;
}
if ((cursor = rt_malloc(CURSOR_WIDTH * CURSOR_HEIGHT * hmi->bytes_per_pixel)))
{
rt_uint8_t *stream = cursor;
rt_ubase_t color = ((to_color(0x82, red_mask)) << red_off) |
(to_color(0x50, green_mask) << green_off) |
(to_color(0xdf, blue_mask) << blue_off) |
(to_color(0xcc, alpha_mask) << alpha_off);
for (int y = 0; y < CURSOR_HEIGHT; ++y)
{
for (int x = 0; x < CURSOR_WIDTH; ++x)
{
rt_memcpy(stream, &color, hmi->bytes_per_pixel);
stream += hmi->bytes_per_pixel;
}
}
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_TYPE, cursor);
rt_free(cursor);
}
none_alpha = alpha_mask << alpha_off;
hmi->line[0] = ~0UL;
hmi->line[1] = none_alpha;
hmi->colors[0] = ((to_color(0xff, red_mask)) << red_off) |
(to_color(0x4b, green_mask) << green_off) |
(to_color(0x00, blue_mask) << blue_off) | none_alpha;
hmi->colors[1] = ((to_color(0x7f, red_mask)) << red_off) |
(to_color(0xdb, green_mask) << green_off) |
(to_color(0x3b, blue_mask) << blue_off) | none_alpha;
hmi->colors[2] = ((to_color(0x00, red_mask)) << red_off) |
(to_color(0xa4, green_mask) << green_off) |
(to_color(0xef, blue_mask) << blue_off) | none_alpha;
hmi->colors[3] = ((to_color(0xff, red_mask)) << red_off) |
(to_color(0xb8, green_mask) << green_off) |
(to_color(0x1c, blue_mask) << blue_off) | none_alpha;
hmi->event = RT_FALSE;
}
static void hmi_graphic_notify(rt_device_t dev)
{
struct hmi_info *hmi = (void *)dev;
hmi->event = RT_TRUE;
}
static rt_bool_t hmi_input_callback(struct rt_input_handler *handler,
struct rt_input_event *ev)
{
struct hmi_info *hmi = handler->priv;
if (ev->type == EV_ABS)
{
if (ev->code == 0)
{
hmi->dx = (ev->value * hmi->info.width) /
(handler->idev->absinfo->maximum - handler->idev->absinfo->minimum);
}
else if (ev->code == 1)
{
hmi->dy = (ev->value * hmi->info.height) /
(handler->idev->absinfo->maximum - handler->idev->absinfo->minimum);
}
}
else if (ev->type == EV_KEY)
{
if (ev->code == BTN_LEFT)
{
if (hmi->keydown && ev->value == 0)
{
/* Swap lines color */
hmi->line[0] ^= hmi->line[1];
hmi->line[1] ^= hmi->line[0];
hmi->line[0] ^= hmi->line[1];
hmi->keydown = RT_FALSE;
hmi->vsync = RT_FALSE;
}
else
{
hmi->keydown = RT_TRUE;
}
}
}
else if (ev->type == EV_SYN)
{
hmi->vsync = RT_FALSE;
}
return RT_TRUE;
}
static void hmi_loop(void *param)
{
struct hmi_info *hmi = param;
struct rt_device_rect_info rect;
struct rt_device_graphic_ops *gops;
/* Graphic device event */
hmi->event_notify.notify = &hmi_graphic_notify;
hmi->event_notify.dev = (void *)hmi;
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_NOTIFY_SET, &hmi->event_notify);
/* Input device event */
hmi->handler.idev = to_input_device(hmi->idev);
hmi->handler.identify = RT_NULL;
hmi->handler.callback = &hmi_input_callback;
hmi->handler.priv = hmi;
rt_input_add_handler(&hmi->handler);
hmi->backend_framebuffer = RT_NULL;
hmi_reset(hmi);
hmi->dx = hmi->info.width >> 1;
hmi->dy = hmi->info.height >> 1;
rect.x = 0;
rect.y = 0;
gops = rt_graphix_ops(hmi->gdev);
rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_POWERON, RT_NULL);
while (hmi_working)
{
rt_ubase_t pos;
/* Wait graphic change */
if (hmi->event)
{
hmi_reset(hmi);
}
hmi->x = hmi->dx;
hmi->y = hmi->dy;
rect.width = hmi->info.width;
rect.height = hmi->info.height;
pos = RTGRAPHIC_PIXEL_POSITION(hmi->x, hmi->y);
for (int i = 0; i < RT_ARRAY_SIZE(hmi->colors); ++i)
{
rt_uint32_t x1, y1, x2, y2;
void *fb = hmi->backend_framebuffer ? : hmi->info.framebuffer;
switch (i)
{
case 0:
x1 = 0;
y1 = 0;
x2 = hmi->x;
y2 = hmi->y;
break;
case 1:
x1 = hmi->x;
y1 = 0;
x2 = hmi->info.width;
y2 = hmi->y;
break;
case 2:
x1 = 0;
y1 = hmi->y;
x2 = hmi->x;
y2 = hmi->info.height;
break;
case 3:
x1 = hmi->x;
y1 = hmi->y;
x2 = hmi->info.width;
y2 = hmi->info.height;
break;
}
fb += x1 * hmi->bytes_per_pixel + y1 * hmi->info.pitch;
for (int y = y1; y < y2; ++y)
{
void *fb_entry = fb;
for (int x = x1; x < x2; ++x)
{
rt_memcpy(fb, &hmi->colors[i], hmi->bytes_per_pixel);
fb += hmi->bytes_per_pixel;
}
fb = fb_entry + hmi->info.pitch;
}
}
if (hmi->backend_framebuffer)
{
rt_memcpy(hmi->info.framebuffer, hmi->backend_framebuffer, hmi->info.smem_len);
}
gops->draw_hline((void *)&hmi->line[0], 0, rect.width, hmi->y);
gops->draw_vline((void *)&hmi->line[1], hmi->x, 0, rect.height);
rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_RECT_UPDATE, &rect);
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_POSITION, (void *)pos);
/* Next position */
hmi->vsync = RT_TRUE;
rt_hw_wmb();
while (hmi_working && hmi->vsync)
{
rt_thread_mdelay(1);
}
}
rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_POWEROFF, RT_NULL);
rt_memset(&hmi->event_notify, 0, sizeof(hmi->event_notify));
rt_device_control(hmi->gdev, RT_DEVICE_CTRL_NOTIFY_SET, &hmi->event_notify);
rt_input_del_handler(&hmi->handler);
rt_device_close(hmi->gdev);
rt_device_close(hmi->idev);
if (hmi->backend_framebuffer)
{
rt_free(hmi->backend_framebuffer);
}
rt_free(hmi);
rt_thread_delete(rt_thread_self());
}
rt_err_t hmi_start(const char *gdev, const char *idev)
{
rt_err_t err;
struct hmi_info *hmi;
struct rt_thread *loop;
if (hmi_working)
{
rt_kprintf("HMI is running\n");
return -RT_EBUSY;
}
hmi = rt_malloc(sizeof(*hmi));
if (!hmi)
{
return -RT_ENOMEM;
}
hmi->gdev = rt_device_find(gdev);
hmi->idev = rt_device_find(idev);
if (!hmi->gdev || !hmi->idev)
{
rt_free(hmi);
return -RT_EINVAL;
}
if (!rt_bitmap_test_bit(to_input_device(hmi->idev)->cap, EV_ABS))
{
rt_kprintf("%s is not a ABS input\n", idev);
rt_free(hmi);
return -RT_EINVAL;
}
if ((err = rt_device_open(hmi->gdev, 0)))
{
rt_free(hmi);
return err;
}
if ((err = rt_device_open(hmi->idev, 0)))
{
rt_device_close(hmi->gdev);
rt_free(hmi);
return err;
}
loop = rt_thread_create("HMI", hmi_loop, hmi,
DM_THREAD_STACK_SIZE,
RT_THREAD_PRIORITY_MAX / 3,
rt_tick_from_millisecond(RT_GRAPHIC_UPDATE_MS));
if (!loop)
{
rt_device_close(hmi->gdev);
rt_device_close(hmi->idev);
rt_free(hmi);
return -RT_ENOMEM;
}
hmi_working = RT_TRUE;
rt_thread_startup(loop);
return RT_EOK;
}
rt_err_t hmi_stop(void)
{
hmi_working = RT_FALSE;
return RT_EOK;
}
#ifdef RT_USING_FINSH
static int _hmi_start(int argc, char**argv)
{
const char *gdev = "fb0", *idev = "input0";
if (argc == 3)
{
gdev = argv[1];
idev = argv[2];
}
return (int)hmi_start(gdev, idev);
}
MSH_CMD_EXPORT_ALIAS(_hmi_start, hmi_start, e.g: hmi_start("fb0", "input0"));
static int _hmi_stop(void)
{
return (int)hmi_stop();
}
MSH_CMD_EXPORT_ALIAS(_hmi_stop, hmi_stop, e.g: hmi_exit());
#endif /* RT_USING_FINSH */
#endif /* RT_USING_GRAPHIC && RT_USING_INPUT */