mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-02-06 00:45:22 +08:00
[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:
@@ -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
|
||||
|
||||
23
components/drivers/graphic/SConscript
Normal file
23
components/drivers/graphic/SConscript
Normal 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')
|
||||
7
components/drivers/graphic/backlight/Kconfig
Normal file
7
components/drivers/graphic/backlight/Kconfig
Normal 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
|
||||
14
components/drivers/graphic/backlight/SConscript
Normal file
14
components/drivers/graphic/backlight/SConscript
Normal 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')
|
||||
231
components/drivers/graphic/backlight/backlight.c
Normal file
231
components/drivers/graphic/backlight/backlight.c
Normal 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;
|
||||
}
|
||||
8
components/drivers/graphic/framebuffer/Kconfig
Normal file
8
components/drivers/graphic/framebuffer/Kconfig
Normal 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
|
||||
16
components/drivers/graphic/framebuffer/SConscript
Normal file
16
components/drivers/graphic/framebuffer/SConscript
Normal 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')
|
||||
1495
components/drivers/graphic/graphic.c
Normal file
1495
components/drivers/graphic/graphic.c
Normal file
File diff suppressed because it is too large
Load Diff
381
components/drivers/graphic/graphic_primary.c
Normal file
381
components/drivers/graphic/graphic_primary.c
Normal 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;
|
||||
}
|
||||
295
components/drivers/graphic/graphic_simple.c
Normal file
295
components/drivers/graphic/graphic_simple.c
Normal 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);
|
||||
}
|
||||
1
components/drivers/graphic/logo/.gitignore
vendored
Normal file
1
components/drivers/graphic/logo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
logo.inc
|
||||
31
components/drivers/graphic/logo/Kconfig
Normal file
31
components/drivers/graphic/logo/Kconfig
Normal 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"
|
||||
84
components/drivers/graphic/logo/SConscript
Normal file
84
components/drivers/graphic/logo/SConscript
Normal 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')
|
||||
216
components/drivers/graphic/logo/logo.c
Normal file
216
components/drivers/graphic/logo/logo.c
Normal 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;
|
||||
}
|
||||
243
components/drivers/graphic/logo/logo.html
Normal file
243
components/drivers/graphic/logo/logo.html
Normal 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">×</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>
|
||||
367
components/drivers/include/drivers/graphic.h
Normal file
367
components/drivers/include/drivers/graphic.h
Normal 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__ */
|
||||
@@ -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
|
||||
|
||||
310
examples/test/dm_graphic_test.c
Normal file
310
examples/test/dm_graphic_test.c
Normal 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
421
examples/test/dm_hmi_test.c
Normal 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 */
|
||||
Reference in New Issue
Block a user