summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/drivers/graphic/Kconfig13
-rw-r--r--components/drivers/graphic/SConscript23
-rw-r--r--components/drivers/graphic/backlight/Kconfig7
-rw-r--r--components/drivers/graphic/backlight/SConscript14
-rw-r--r--components/drivers/graphic/backlight/backlight.c231
-rw-r--r--components/drivers/graphic/framebuffer/Kconfig8
-rw-r--r--components/drivers/graphic/framebuffer/SConscript16
-rw-r--r--components/drivers/graphic/graphic.c1495
-rw-r--r--components/drivers/graphic/graphic_primary.c381
-rw-r--r--components/drivers/graphic/graphic_simple.c295
-rw-r--r--components/drivers/graphic/logo/.gitignore1
-rw-r--r--components/drivers/graphic/logo/Kconfig31
-rw-r--r--components/drivers/graphic/logo/SConscript84
-rw-r--r--components/drivers/graphic/logo/logo.c216
-rw-r--r--components/drivers/graphic/logo/logo.html243
-rw-r--r--components/drivers/include/drivers/graphic.h367
-rw-r--r--components/drivers/include/drivers/lcd.h33
17 files changed, 3457 insertions, 1 deletions
diff --git a/components/drivers/graphic/Kconfig b/components/drivers/graphic/Kconfig
index 5ce0835743..768d594a99 100644
--- a/components/drivers/graphic/Kconfig
+++ b/components/drivers/graphic/Kconfig
@@ -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
diff --git a/components/drivers/graphic/SConscript b/components/drivers/graphic/SConscript
new file mode 100644
index 0000000000..300b71afd7
--- /dev/null
+++ b/components/drivers/graphic/SConscript
@@ -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')
diff --git a/components/drivers/graphic/backlight/Kconfig b/components/drivers/graphic/backlight/Kconfig
new file mode 100644
index 0000000000..1951e0c05a
--- /dev/null
+++ b/components/drivers/graphic/backlight/Kconfig
@@ -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
diff --git a/components/drivers/graphic/backlight/SConscript b/components/drivers/graphic/backlight/SConscript
new file mode 100644
index 0000000000..6e29db51b3
--- /dev/null
+++ b/components/drivers/graphic/backlight/SConscript
@@ -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')
diff --git a/components/drivers/graphic/backlight/backlight.c b/components/drivers/graphic/backlight/backlight.c
new file mode 100644
index 0000000000..dce54b112b
--- /dev/null
+++ b/components/drivers/graphic/backlight/backlight.c
@@ -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;
+}
diff --git a/components/drivers/graphic/framebuffer/Kconfig b/components/drivers/graphic/framebuffer/Kconfig
new file mode 100644
index 0000000000..9144377d20
--- /dev/null
+++ b/components/drivers/graphic/framebuffer/Kconfig
@@ -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
diff --git a/components/drivers/graphic/framebuffer/SConscript b/components/drivers/graphic/framebuffer/SConscript
new file mode 100644
index 0000000000..55d76c540f
--- /dev/null
+++ b/components/drivers/graphic/framebuffer/SConscript
@@ -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')
diff --git a/components/drivers/graphic/graphic.c b/components/drivers/graphic/graphic.c
new file mode 100644
index 0000000000..d1692c851e
--- /dev/null
+++ b/components/drivers/graphic/graphic.c
@@ -0,0 +1,1495 @@
+/*
+ * 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 "rtdm.graphic"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#define raw_to_graphic(dev) rt_container_of(dev, struct rt_graphic_device, parent)
+
+struct fb_format
+{
+ rt_uint32_t mode;
+ rt_uint32_t bits_per_pixel;
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+};
+
+static const struct fb_format graphic_formats[] =
+{
+ { RTGRAPHIC_PIXEL_FORMAT_GRAY4, 4, },
+ { RTGRAPHIC_PIXEL_FORMAT_GRAY16, 16, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB332, 8, { 5, 3 }, { 2, 3 }, { 0, 2 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB444, 12, { 8, 4 }, { 4, 4 }, { 0, 4 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB565, 16, { 11, 5 }, { 5, 6 }, { 0, 5 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB565P, 16, { 0, 5 }, { 5, 6 }, { 11, 5 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_BGR565, 16, { 0, 5 }, { 5, 6 }, { 11, 5 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB666, 18, { 12, 6 }, { 6, 6 }, { 0, 6 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_RGB888, 24, { 16, 8 }, { 8, 8 }, { 0, 8 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_BGR888, 24, { 0, 8 }, { 8, 8 }, { 16, 8 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_ARGB888, 32, { 16, 8 }, { 8, 8 }, { 0, 8 }, { 24, 8 }, },
+ { RTGRAPHIC_PIXEL_FORMAT_ABGR888, 32, { 0, 8 }, { 8, 8 }, { 16, 8 }, { 24, 8 }, },
+};
+
+/* RT-Thread device max id is 255 */
+static rt_uint8_t fbcon_map[256] = {};
+
+static struct rt_dm_ida graphic_ida = RT_DM_IDA_INIT(GRAPHIC_FRAMEBUFFER);
+
+rt_inline void spin_lock(struct rt_spinlock *spinlock)
+{
+ rt_hw_spin_lock(&spinlock->lock);
+}
+
+rt_inline void spin_unlock(struct rt_spinlock *spinlock)
+{
+ rt_hw_spin_unlock(&spinlock->lock);
+}
+
+static rt_bool_t plane_need_update(struct rt_graphic_plane *plane)
+{
+ if (plane->ops->update)
+ {
+ if (!plane->graphic->update_timer)
+ {
+ return RT_TRUE;
+ }
+ }
+
+ return RT_FALSE;
+}
+
+static struct rt_graphic_plane *plane_get_current(struct rt_graphic_device *gdev)
+{
+ if (gdev->ops->current_plane)
+ {
+ return gdev->ops->current_plane(gdev);
+ }
+
+ return gdev->primary_plane;
+}
+
+static rt_err_t plane_fb_remap(struct rt_graphic_plane *plane,
+ rt_uint32_t mode, struct rt_device_rect_info *rect)
+{
+ rt_err_t err;
+
+ if (plane->ops->fb_cleanup && (err = plane->ops->fb_cleanup(plane)))
+ {
+ return err;
+ }
+
+ if (!(err = plane->ops->fb_remap(plane, mode, rect)))
+ {
+ plane->mode = mode;
+ plane->x = rect->x;
+ plane->y = rect->y;
+ plane->width = rect->width;
+ plane->height = rect->height;
+ }
+
+ return err;
+}
+
+static rt_err_t plane_fb_pan_display(struct rt_graphic_plane *plane,
+ struct rt_device_rect_info *rect)
+{
+ void *framebuffer_end = plane->framebuffer;
+ rt_size_t byte_per_pixel = plane->bits_per_pixel / 8;
+
+ framebuffer_end += rect->x * byte_per_pixel;
+ framebuffer_end += rect->y * plane->line_length;
+ framebuffer_end += rect->width * rect->height * byte_per_pixel;
+
+ if (framebuffer_end < plane->framebuffer + plane->framebuffer_len)
+ {
+ return plane->ops->fb_pan_display(plane, rect);
+ }
+
+ return -RT_EINVAL;
+}
+
+static rt_err_t graphic_dpms_switch(struct rt_graphic_device *gdev, rt_uint32_t dpms)
+{
+ rt_err_t err;
+
+ if (!(err = gdev->ops->dpms_switch(gdev, dpms)))
+ {
+ gdev->dpms = dpms;
+ }
+
+ return err;
+}
+
+static rt_err_t _graphic_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ struct rt_graphic_device *gdev = raw_to_graphic(dev);
+ struct rt_graphic_plane *plane = gdev->primary_plane;
+
+ if (dev->ref_count > 0 && (oflag & RT_DEVICE_OFLAG_WRONLY))
+ {
+ return -RT_EBUSY;
+ }
+
+ if (plane->ops->fb_pan_display)
+ {
+ struct rt_device_rect_info rect;
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = plane->width;
+ rect.height = plane->height;
+
+ return plane->ops->fb_pan_display(plane, &rect);
+ }
+
+ return RT_EOK;
+}
+
+static rt_ssize_t _graphic_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_ssize_t res;
+ struct rt_graphic_device *gdev = raw_to_graphic(dev);
+ struct rt_graphic_plane *plane = gdev->primary_plane;
+
+ res = rt_min_t(rt_ssize_t, plane->framebuffer_len - pos, size);
+
+ if (res > 0)
+ {
+ rt_memcpy(buffer, plane->framebuffer + pos, res);
+ }
+ else
+ {
+ res = 0;
+ }
+
+ return res;
+}
+
+static rt_ssize_t _graphic_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ rt_ssize_t res;
+ struct rt_graphic_device *gdev = raw_to_graphic(dev);
+ struct rt_graphic_plane *plane = gdev->primary_plane;
+
+ res = rt_min_t(rt_ssize_t, plane->framebuffer_len - pos, size);
+
+ if (res > 0)
+ {
+ rt_memcpy(plane->framebuffer + pos, buffer, res);
+ }
+ else
+ {
+ res = 0;
+ }
+
+ return res;
+}
+
+static rt_err_t _graphic_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t err = RT_EOK;
+ rt_bool_t need_schedule = RT_FALSE;
+ struct rt_graphic_device *gdev = raw_to_graphic(dev);
+
+_retry:
+ if (need_schedule)
+ {
+ rt_thread_yield();
+ }
+
+ spin_lock(&gdev->lock);
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_CURSOR_SET_POSITION:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = gdev->cursor_plane;
+
+ if (plane)
+ {
+ rt_uint32_t position = (rt_uint32_t)(rt_ubase_t)args;
+
+ plane->x = position >> 16;
+ plane->y = position & 0xffff;
+
+ if (plane_need_update(plane))
+ {
+ struct rt_device_rect_info rect;
+
+ rect.x = plane->x;
+ rect.y = plane->y;
+ /* Ask driver to update position only */
+ rect.width = 0;
+ rect.height = 0;
+
+ err = plane->ops->update(plane, &rect);
+ }
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RT_DEVICE_CTRL_CURSOR_SET_TYPE:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = gdev->cursor_plane;
+
+ if (plane)
+ {
+ struct rt_device_rect_info rect =
+ {
+ .x = 0,
+ .y = 0,
+ .width = plane->width,
+ .height = plane->height,
+ };
+
+ if (!plane->framebuffer)
+ {
+ rt_uint32_t mode;
+
+ if (plane->mode == RTGRAPHIC_PIXEL_FORMAT_MONO)
+ {
+ mode = plane->modes[0];
+ }
+ else
+ {
+ mode = plane->mode;
+ }
+
+ if ((err = plane_fb_remap(plane, mode, &rect)))
+ {
+ break;
+ }
+ }
+
+ rt_memcpy(plane->framebuffer, args, plane->screen_len);
+
+ /* Force to update */
+ err = plane->ops->update(plane, &rect);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_RECT_UPDATE:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ if (plane_need_update(plane))
+ {
+ err = plane->ops->update(plane, args);
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_POWERON:
+ if (gdev->ops->dpms_switch)
+ {
+ err = graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_ON);
+ }
+ #ifdef RT_GRAPHIC_BACKLIGHT
+ if (!err && gdev->backlight)
+ {
+ spin_unlock(&gdev->lock);
+ err = rt_backlight_set_power(gdev->backlight, RT_BACKLIGHT_POWER_NORMAL);
+ spin_lock(&gdev->lock);
+ }
+ #endif /* RT_GRAPHIC_BACKLIGHT */
+ break;
+
+ case RTGRAPHIC_CTRL_POWEROFF:
+ if (gdev->ops->dpms_switch)
+ {
+ err = graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_OFF);
+ }
+ #ifdef RT_GRAPHIC_BACKLIGHT
+ if (!err && gdev->backlight)
+ {
+ spin_unlock(&gdev->lock);
+ err = rt_backlight_set_power(gdev->backlight, RT_BACKLIGHT_POWER_POWERDOWN);
+ spin_lock(&gdev->lock);
+ }
+ #endif /* RT_GRAPHIC_BACKLIGHT */
+ break;
+
+ case RTGRAPHIC_CTRL_GET_INFO:
+ if (args)
+ {
+ struct rt_device_graphic_info *info = args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ info->pixel_format = plane->mode;
+ info->bits_per_pixel = plane->bits_per_pixel;
+ info->pitch = plane->line_length;
+ info->width = plane->width;
+ info->height = plane->height;
+ info->framebuffer = plane->framebuffer;
+ info->smem_len = plane->framebuffer_len;
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_SET_MODE:
+ if (args)
+ {
+ rt_uint32_t mode = (rt_uint32_t)(rt_ubase_t)args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ if (mode != plane->mode)
+ {
+ err = -RT_ENOSYS;
+
+ if (plane->modes_nr > 1)
+ {
+ for (int i = 0; i < plane->modes_nr; ++i)
+ {
+ if (mode == plane->modes[i])
+ {
+ struct rt_device_rect_info rect =
+ {
+ .x = plane->x,
+ .y = plane->y,
+ .width = plane->width,
+ .height = plane->height,
+ };
+
+ err = plane_fb_remap(plane, mode, &rect);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_GET_EXT:
+ if (args)
+ {
+ rt_memcpy(args, &gdev->edid, sizeof(gdev->edid));
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_SET_BRIGHTNESS:
+ if (gdev->ops->set_brightness)
+ {
+ err = gdev->ops->set_brightness(gdev, (rt_uint32_t)(rt_ubase_t)args);
+ }
+ #ifdef RT_GRAPHIC_BACKLIGHT
+ if (!err && gdev->backlight)
+ {
+ spin_unlock(&gdev->lock);
+
+ return rt_backlight_set_brightness(gdev->backlight, (rt_uint32_t)(rt_ubase_t)args);
+ }
+ #endif /* RT_GRAPHIC_BACKLIGHT */
+ break;
+
+ case RTGRAPHIC_CTRL_GET_BRIGHTNESS:
+ if (args)
+ {
+ if (gdev->ops->get_brightness)
+ {
+ err = gdev->ops->get_brightness(gdev, args);
+ }
+ else
+ {
+ *(rt_uint32_t *)args = RT_UINT32_MAX >> 1;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ #ifdef RT_GRAPHIC_BACKLIGHT
+ if (!err && gdev->backlight)
+ {
+ spin_unlock(&gdev->lock);
+
+ return rt_backlight_get_brightness(gdev->backlight, args);
+ }
+ #endif /* RT_GRAPHIC_BACKLIGHT */
+ break;
+
+ case RTGRAPHIC_CTRL_GET_MODE:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ *(rt_uint32_t *)args = plane->mode;
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_GET_STATUS:
+ if (args)
+ {
+ if (gdev->ops->get_status)
+ {
+ err = gdev->ops->get_status(gdev, args);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_PAN_DISPLAY:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ if (plane->ops->fb_pan_display)
+ {
+ rt_size_t offset;
+ struct rt_device_rect_info rect;
+
+ offset = (rt_size_t)(args - plane->framebuffer);
+
+ rect.x = (offset % plane->line_length) / (plane->bits_per_pixel / 8);
+ rect.y = offset / plane->line_length;
+ rect.width = plane->width;
+ rect.height = plane->height;
+
+ err = plane_fb_pan_display(plane, &rect);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_WAIT_VSYNC:
+ if (gdev->ops->wait_vsync)
+ {
+ err = gdev->ops->wait_vsync(gdev);
+ }
+ break;
+
+ case RT_DEVICE_CTRL_NOTIFY_SET:
+ if (args)
+ {
+ if (rt_atomic_load(&gdev->event_notifying) == RT_TRUE)
+ {
+ need_schedule = RT_TRUE;
+
+ spin_unlock(&gdev->lock);
+ goto _retry;
+ }
+
+ rt_memcpy(&gdev->event_notify, args, sizeof(gdev->event_notify));
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOGET_VSCREENINFO:
+ if (args)
+ {
+ struct fb_var_screeninfo *var = args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ rt_memset(var, 0, sizeof(*var));
+
+ var->xres = plane->width;
+ var->yres = plane->height;
+ var->xres_virtual = plane->width;
+ var->yres_virtual = plane->height * (plane->framebuffer_len / plane->screen_len);
+ var->bits_per_pixel = plane->bits_per_pixel;
+
+ if (plane == gdev->primary_plane)
+ {
+ var->width = gdev->edid.width_cm ? gdev->edid.width_cm * 10 : -1;
+ var->height = gdev->edid.height_cm ? gdev->edid.height_cm * 10 : -1;
+ }
+ else
+ {
+ var->width = -1;
+ var->height = -1;
+ }
+
+ if (plane->mode == RTGRAPHIC_PIXEL_FORMAT_GRAY4 ||
+ plane->mode == RTGRAPHIC_PIXEL_FORMAT_GRAY16)
+ {
+ var->grayscale = 1;
+ }
+ else
+ {
+ const struct fb_format *fmt = &graphic_formats[0];
+
+ for (int i = 0; i < RT_ARRAY_SIZE(graphic_formats); ++i, ++fmt)
+ {
+ if (fmt->mode == plane->mode)
+ {
+ rt_memcpy(&var->red, &fmt->red, sizeof(fmt->red));
+ rt_memcpy(&var->green, &fmt->green, sizeof(fmt->green));
+ rt_memcpy(&var->blue, &fmt->blue, sizeof(fmt->blue));
+ rt_memcpy(&var->transp, &fmt->transp, sizeof(fmt->transp));
+
+ break;
+ }
+ }
+ }
+
+ if (gdev->update_timer)
+ {
+ rt_uint64_t update_ps;
+ rt_tick_t update_tick;
+
+ rt_timer_control(gdev->update_timer, RT_TIMER_CTRL_GET_TIME, &update_tick);
+ /*
+ * 1s update_ms * 1000
+ * -------------------- = ----------------- (second/tick)
+ * RT_TICK_PER_SECOND update_tick
+ *
+ * 1000000ps = 1ms
+ */
+ update_ps = (update_tick * 1000000) / (RT_TICK_PER_SECOND * 1000);
+
+ var->pixclock = update_ps / (var->xres * var->yres);
+ }
+ else
+ {
+ var->pixclock = (RT_GRAPHIC_UPDATE_MS * 1000000) / (var->xres * var->yres);
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOPUT_VSCREENINFO:
+ if (args)
+ {
+ rt_uint32_t mode;
+ struct fb_format fmt;
+ struct rt_device_rect_info rect;
+ struct fb_var_screeninfo *var = args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+ const rt_size_t cmp_offset = rt_offsetof(struct fb_format, bits_per_pixel);
+ const rt_size_t cmp_size = sizeof(struct fb_format) - cmp_offset;
+
+ if (!plane->ops->fb_pan_display)
+ {
+ if (var->xres != plane->width || var->yres != plane->height ||
+ var->xoffset != plane->x || var->yoffset != plane->y)
+ {
+ err = -RT_ENOSYS;
+ break;
+ }
+ }
+
+ mode = plane->mode;
+ rect.x = plane->x;
+ rect.y = plane->y;
+ rect.width = plane->width;
+ rect.height = plane->height;
+
+ fmt.bits_per_pixel = var->bits_per_pixel;
+ rt_memcpy(&fmt.red, &var->red, sizeof(fmt.red));
+ rt_memcpy(&fmt.green, &var->green, sizeof(fmt.green));
+ rt_memcpy(&fmt.blue, &var->blue, sizeof(fmt.blue));
+ rt_memcpy(&fmt.transp, &var->transp, sizeof(fmt.transp));
+
+ for (int i = 0; i < RT_ARRAY_SIZE(graphic_formats); ++i)
+ {
+ void *cmp_to = ((void *)&fmt) + cmp_offset;
+ void *cmp_from = ((void *)&graphic_formats[i]) + cmp_offset;
+
+ if (!rt_memcmp(cmp_to, cmp_from, cmp_size))
+ {
+ mode = graphic_formats[i].mode;
+ break;
+ }
+ }
+
+ err = -RT_ENOSYS;
+
+ for (int i = 0; i < plane->modes_nr; ++i)
+ {
+ /* Check supported and commit */
+ if (plane->modes[i] == mode && plane->mode != mode)
+ {
+ err = plane_fb_remap(plane, mode, &rect);
+ }
+ }
+
+ if (!err && plane->ops->fb_pan_display)
+ {
+ rect.x = var->xoffset;
+ rect.y = var->yoffset;
+ rect.width = var->xres;
+ rect.height = var->yres;
+
+ err = plane_fb_pan_display(plane, &rect);
+ }
+
+ if (!err && var->rotate && plane->ops->prop_set)
+ {
+ err = plane->ops->prop_set(plane, RT_GRAPHIC_PLANE_PROP_ROTATE,
+ (void *)(rt_ubase_t)((var->rotate % 360) / 90));
+ }
+
+ if (!err && plane == gdev->primary_plane && plane->ops->update)
+ {
+ rt_uint32_t update_ms = 0;
+
+ if (var->pixclock)
+ {
+ rt_uint64_t clock_cycles;
+
+ clock_cycles = var->pixclock;
+ clock_cycles *= rect.width;
+ clock_cycles *= rect.height;
+ /* Seconds in pico seconds */
+ clock_cycles /= 1000000000000ULL;
+
+ update_ms = (rt_uint32_t)clock_cycles * 1000;
+ }
+
+ err = rt_graphic_device_update_auto(gdev, update_ms);
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOGET_FSCREENINFO:
+ if (args)
+ {
+ struct fb_fix_screeninfo *fix = args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ rt_memset(fix, 0, sizeof(*fix));
+
+ rt_snprintf(fix->id, rt_min_t(int, sizeof(fix->id), RT_NAME_MAX),
+ "%s", rt_dm_dev_get_name(&gdev->parent));
+
+ fix->smem_start = (unsigned long)rt_kmem_v2p(plane->framebuffer);
+ fix->smem_len = plane->framebuffer_len;
+ fix->mmio_start = fix->smem_start;
+ fix->mmio_len = plane->screen_len;
+ fix->line_length = plane->line_length;
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOGET_PIXELINFO:
+ if (args)
+ {
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ *(rt_uint32_t *)args = plane->mode;
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOPAN_DISPLAY:
+ if (args)
+ {
+ struct fb_var_screeninfo *var = args;
+ struct rt_graphic_plane *plane = plane_get_current(gdev);
+
+ if (plane->ops->fb_pan_display)
+ {
+ struct rt_device_rect_info rect;
+
+ rect.x = var->xoffset;
+ rect.y = var->yoffset;
+ rect.width = var->xres;
+ rect.height = var->yres;
+
+ err = plane_fb_pan_display(plane, &rect);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIO_CURSOR:
+ err = -RT_EINVAL;
+ break;
+
+ case FBIOGET_CON2FBMAP:
+ if (args)
+ {
+ struct fb_con2fbmap *con2fbmap = args;
+
+ if (con2fbmap->console < RT_ARRAY_SIZE(fbcon_map))
+ {
+ con2fbmap->framebuffer = fbcon_map[con2fbmap->console];
+ }
+ else
+ {
+ err = -RT_EFULL;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOPUT_CON2FBMAP:
+ if (args)
+ {
+ struct fb_con2fbmap *con2fbmap = args;
+
+ if (con2fbmap->console < RT_ARRAY_SIZE(fbcon_map) &&
+ con2fbmap->framebuffer < RT_ARRAY_SIZE(fbcon_map))
+ {
+ struct rt_device *vt;
+
+ vt = rt_dm_device_find(MASTER_ID_TTY, con2fbmap->console);
+
+ #ifdef RT_SERIAL_VIRTUAL
+ if (!vt)
+ {
+ vt = rt_device_find("vuart");
+ }
+ #endif
+
+ if (vt)
+ {
+ if (!(err = rt_device_open(vt, RT_DEVICE_OFLAG_RDWR)))
+ {
+ err = rt_device_control(vt, FBIOPUT_CON2FBMAP, con2fbmap);
+
+ if (!err)
+ {
+ fbcon_map[con2fbmap->console] = con2fbmap->framebuffer;
+ }
+
+ rt_device_close(vt);
+ }
+ }
+ else
+ {
+ err = -RT_EEMPTY;
+ }
+ }
+ else
+ {
+ err = -RT_EFULL;
+ }
+ }
+ else
+ {
+ err = -RT_EINVAL;
+ }
+ break;
+
+ case FBIOBLANK:
+ if (gdev->ops->dpms_switch)
+ {
+ rt_uint32_t dpms;
+
+ switch ((rt_uint32_t)(rt_ubase_t)args)
+ {
+ case FB_BLANK_UNBLANK:
+ /* Display: On, HSync: On, VSync: On */
+ dpms = RT_GRAPHIC_DPMS_ON;
+ break;
+
+ case FB_BLANK_NORMAL:
+ /* Display: Off, HSync: On, VSync: On */
+ dpms = RT_GRAPHIC_DPMS_STANDBY;
+ break;
+
+ case FB_BLANK_HSYNC_SUSPEND:
+ /* Display: Off, HSync: Off, VSync: On */
+ dpms = RT_GRAPHIC_DPMS_STANDBY;
+ break;
+
+ case FB_BLANK_VSYNC_SUSPEND:
+ /* Display: Off, HSync: On, VSync: Off */
+ dpms = RT_GRAPHIC_DPMS_SUSPEND;
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ /* Display: Off, HSync: Off, VSync: Off */
+ dpms = RT_GRAPHIC_DPMS_OFF;
+ break;
+
+ default:
+ err = -RT_EINVAL;
+ break;
+ }
+
+ if (!err)
+ {
+ graphic_dpms_switch(gdev, dpms);
+ }
+ }
+ break;
+
+ case FBIO_WAITFORVSYNC:
+ if (gdev->ops->wait_vsync)
+ {
+ err = gdev->ops->wait_vsync(gdev);
+ }
+ break;
+
+ case FBIO_ALLOC:
+ case FBIO_FREE:
+ case FBIOGET_GLYPH:
+ case FBIOGET_HWCINFO:
+ case FBIOPUT_MODEINFO:
+ case FBIOGET_DISPINFO:
+ LOG_D("FB IOCTL (%x) only used for SiS 300/630/730/540/315/550/650/740 frame buffer device", cmd);
+ /* Fall through */
+ case FBIOGET_VBLANK:
+ case FBIOGETCMAP: /* fb_cmap */
+ case FBIOPUTCMAP: /* fb_cmap */
+ /* Fall through */
+ default:
+ if (gdev->ops->control)
+ {
+ err = gdev->ops->control(gdev, cmd, args);
+ }
+ else
+ {
+ err = -RT_ENOSYS;
+ }
+ }
+
+ spin_unlock(&gdev->lock);
+
+ return err;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops _graphic_ops =
+{
+ .open = _graphic_open,
+ .read = _graphic_read,
+ .write = _graphic_write,
+ .control = _graphic_control,
+};
+#endif
+
+static void graphic_ofw_init(struct rt_graphic_device *gdev)
+{
+#ifdef RT_USING_OFW
+ struct rt_ofw_node *np = gdev->parent.ofw_node;
+
+#ifdef RT_GRAPHIC_BACKLIGHT
+ if (!gdev->backlight)
+ {
+ struct rt_ofw_node *bl_np = rt_ofw_parse_phandle(np, "backlight", 0);
+
+ if (bl_np && (gdev->backlight = rt_ofw_data(bl_np)))
+ {
+ rt_device_open(&gdev->backlight->parent, RT_DEVICE_OFLAG_RDWR);
+ }
+ rt_ofw_node_put(bl_np);
+ }
+#endif /* RT_GRAPHIC_BACKLIGHT */
+
+ (void)np;
+#endif /* RT_USING_OFW */
+}
+
+static void graphic_edid_res(struct rt_graphic_device *gdev,
+ rt_uint32_t *out_width, rt_uint32_t *out_height)
+{
+ struct edid *edid = &gdev->edid;
+ struct detailed_timing *dt = &edid->detailed_timings[0];
+ struct detailed_pixel_timing *dpt = &dt->data.pixel_data;
+
+ *out_width = dpt->hactive_lo + ((dpt->hactive_hblank_hi & 0xf0) << 4);
+ *out_height = dpt->vactive_lo + ((dpt->vactive_vblank_hi & 0xf0) << 4);
+}
+
+#ifdef RT_USING_PM
+static rt_err_t _graphic_pm_dpms_switch(struct rt_graphic_device *gdev, rt_uint8_t mode)
+{
+ rt_err_t err;
+
+ spin_lock(&gdev->lock);
+
+ switch (mode)
+ {
+ case PM_SLEEP_MODE_NONE:
+ case PM_SLEEP_MODE_IDLE:
+ case PM_SLEEP_MODE_LIGHT:
+ case PM_SLEEP_MODE_DEEP:
+ err = graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_ON);
+ break;
+
+ case PM_SLEEP_MODE_STANDBY:
+ err = graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_STANDBY);
+ break;
+
+ case PM_SLEEP_MODE_SHUTDOWN:
+ err = graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_OFF);
+ break;
+
+ default:
+ err = -RT_EINVAL;
+ break;
+ }
+
+ spin_unlock(&gdev->lock);
+
+ return err;
+}
+
+static rt_err_t _graphic_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
+{
+ struct rt_graphic_device *gdev = raw_to_graphic(device);
+
+ return _graphic_pm_dpms_switch(gdev, mode);
+}
+
+static void _graphic_pm_resume(const struct rt_device *device, rt_uint8_t mode)
+{
+ struct rt_graphic_device *gdev = raw_to_graphic(device);
+
+ _graphic_pm_dpms_switch(gdev, mode);
+}
+
+static const struct rt_device_pm_ops _graphic_pm_ops =
+{
+ .suspend = _graphic_pm_suspend,
+ .resume = _graphic_pm_resume,
+};
+#endif /* RT_USING_PM */
+
+rt_err_t rt_graphic_device_register(struct rt_graphic_device *gdev)
+{
+ rt_err_t err;
+ int device_id;
+ const char *dev_name;
+
+ if (!gdev || !gdev->ops)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (!gdev->primary_plane)
+ {
+ LOG_E("%s: Not %s found", rt_dm_dev_get_name(&gdev->parent), "primary plane");
+
+ return -RT_EINVAL;
+ }
+
+ if ((device_id = rt_dm_ida_alloc(&graphic_ida)) < 0)
+ {
+ return -RT_EFULL;
+ }
+
+ rt_dm_dev_set_name(&gdev->parent, "fb%u", device_id);
+ dev_name = rt_dm_dev_get_name(&gdev->parent);
+
+ rt_list_init(&gdev->overlay_nodes);
+ rt_dm_ida_init(&gdev->plane_ida, CUSTOM);
+ rt_spin_lock_init(&gdev->lock);
+
+ graphic_ofw_init(gdev);
+
+ if (!gdev->primary_plane->width || !gdev->primary_plane->height)
+ {
+ rt_uint32_t mode, width, height;
+ struct rt_device_rect_info rect;
+
+ graphic_edid_res(gdev, &width, &height);
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = width;
+ rect.height = height;
+
+ if (gdev->primary_plane->mode == RTGRAPHIC_PIXEL_FORMAT_MONO)
+ {
+ mode = gdev->primary_plane->modes[0];
+ }
+ else
+ {
+ mode = gdev->primary_plane->mode;
+ }
+
+ err = plane_fb_remap(gdev->primary_plane, mode, &rect);
+
+ if (err)
+ {
+ LOG_E("%s: Set %s error = %s", rt_dm_dev_get_name(&gdev->parent),
+ "primary plane", rt_strerror(err));
+
+ goto _fail;
+ }
+ }
+
+ gdev->parent.type = RT_Device_Class_Graphic;
+#ifdef RT_USING_DEVICE_OPS
+ gdev->parent.ops = &_graphic_ops;
+#else
+ gdev->parent.open = _graphic_open;
+ gdev->parent.read = _graphic_read;
+ gdev->parent.write = _graphic_write;
+ gdev->parent.control = _graphic_control;
+#endif
+ gdev->parent.master_id = graphic_ida.master_id;
+ gdev->parent.device_id = device_id;
+
+ if ((err = rt_device_register(&gdev->parent, dev_name, RT_DEVICE_FLAG_RDWR)))
+ {
+ goto _fail;
+ }
+
+#ifdef RT_USING_PM
+ rt_pm_device_register(&gdev->parent, &_graphic_pm_ops);
+#endif
+
+ if ((err = rt_graphic_logo_render(gdev)))
+ {
+ LOG_D("Logo render error = %s", rt_strerror(err));
+ }
+
+ return RT_EOK;
+
+_fail:
+ rt_dm_ida_free(&graphic_ida, device_id);
+
+ return err;
+}
+
+rt_err_t rt_graphic_device_unregister(struct rt_graphic_device *gdev)
+{
+ const char *dev_name;
+ struct rt_graphic_plane *plane, *plane_next;
+
+ if (!gdev)
+ {
+ return -RT_EINVAL;
+ }
+
+ dev_name = rt_dm_dev_get_name(&gdev->parent);
+
+ if (gdev->parent.ref_count)
+ {
+ LOG_E("%s: there is %u user", dev_name, gdev->parent.ref_count);
+ return -RT_EINVAL;
+ }
+
+#ifdef RT_USING_PM
+ rt_pm_device_unregister(&gdev->parent);
+#endif
+
+ rt_graphic_device_update_auto(gdev, 0);
+
+ if (gdev->ops->dpms_switch)
+ {
+ graphic_dpms_switch(gdev, RT_GRAPHIC_DPMS_OFF);
+ }
+#ifdef RT_GRAPHIC_BACKLIGHT
+ if (gdev->backlight)
+ {
+ rt_backlight_set_power(gdev->backlight, RT_BACKLIGHT_POWER_POWERDOWN);
+ rt_device_close(&gdev->backlight->parent);
+ }
+#endif
+
+ rt_list_for_each_entry_safe(plane, plane_next, &gdev->overlay_nodes, list)
+ {
+ rt_graphic_device_del_plane(gdev, plane);
+ rt_graphic_device_free_plane(plane);
+ }
+
+ rt_graphic_device_del_plane(gdev, gdev->primary_plane);
+ rt_graphic_device_free_plane(gdev->primary_plane);
+
+ if (gdev->cursor_plane)
+ {
+ rt_graphic_device_del_plane(gdev, gdev->cursor_plane);
+ rt_graphic_device_free_plane(gdev->cursor_plane);
+ }
+
+ rt_dm_ida_free(&graphic_ida, gdev->parent.device_id);
+
+ rt_device_unregister(&gdev->parent);
+
+ return RT_EOK;
+}
+
+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)
+{
+ struct rt_graphic_plane *plane = RT_NULL;
+
+ if (!gdev || !ops || !modes || !modes_nr || type > RT_GRAPHIC_PLANE_TYPE_CURSOR)
+ {
+ return RT_NULL;
+ }
+
+ plane = rt_calloc(1, sizeof(*plane) + priv_size);
+
+ if (plane)
+ {
+ rt_list_init(&plane->list);
+ plane->type = type;
+ plane->modes_nr = modes_nr;
+ plane->modes = modes;
+ plane->mode = RTGRAPHIC_PIXEL_FORMAT_MONO;
+ plane->graphic = gdev;
+ plane->ops = ops;
+ }
+
+ return plane;
+}
+
+void rt_graphic_device_free_plane(struct rt_graphic_plane *plane)
+{
+ if (!plane)
+ {
+ return;
+ }
+
+ rt_free(plane);
+}
+
+rt_err_t rt_graphic_device_add_plane(struct rt_graphic_device *gdev,
+ struct rt_graphic_plane *plane)
+{
+ rt_err_t err = RT_EOK;
+
+ if (!gdev || !plane)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (!plane->ops)
+ {
+ LOG_E("%s: %s have no plane ops",
+ rt_dm_dev_get_name(&gdev->parent), plane->name);
+ return -RT_EINVAL;
+ }
+
+ plane->id = rt_dm_ida_alloc(&gdev->plane_ida);
+
+ if (plane->id == RT_DM_IDA_NUM)
+ {
+ LOG_E("%s: %s is out of plane max(%d)",
+ rt_dm_dev_get_name(&gdev->parent), plane->name, RT_DM_IDA_NUM - 1);
+ return -RT_EFULL;
+ }
+
+ if (plane->type == RT_GRAPHIC_PLANE_TYPE_PRIMARY)
+ {
+ if (gdev->primary_plane)
+ {
+ err = -RT_EINVAL;
+ goto _free_ida;
+ }
+ if (!plane->name[0])
+ {
+ rt_strncpy(plane->name, "primary", sizeof(plane->name));
+ }
+ gdev->primary_plane = plane;
+ }
+ else if (plane->type == RT_GRAPHIC_PLANE_TYPE_CURSOR)
+ {
+ if (gdev->cursor_plane)
+ {
+ err = -RT_EINVAL;
+ goto _free_ida;
+ }
+ if (!plane->name[0])
+ {
+ rt_strncpy(plane->name, "cursor", sizeof(plane->name));
+ }
+ gdev->cursor_plane = plane;
+ }
+ else if (plane->type == RT_GRAPHIC_PLANE_TYPE_OVERLAY)
+ {
+ if (!plane->name[0])
+ {
+ rt_snprintf(plane->name, sizeof(plane->name), "overlay-%u", plane->id);
+ }
+ spin_lock(&gdev->lock);
+ rt_list_insert_before(&gdev->overlay_nodes, &plane->list);
+ rt_graphic_device_leave(gdev);
+ }
+ else
+ {
+ LOG_E("What the fuck plane type(%u)", plane->type);
+ RT_ASSERT(0);
+ }
+
+_free_ida:
+ if (err)
+ {
+ rt_dm_ida_free(&gdev->plane_ida, plane->id);
+ }
+
+ return err;
+}
+
+rt_err_t rt_graphic_device_del_plane(struct rt_graphic_device *gdev,
+ struct rt_graphic_plane *plane)
+{
+ if (!gdev || !plane)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (plane->ops->fb_cleanup)
+ {
+ /* Ignore error */
+ plane->ops->fb_cleanup(plane);
+ }
+
+ if (plane->type == RT_GRAPHIC_PLANE_TYPE_PRIMARY)
+ {
+ gdev->primary_plane = RT_NULL;
+ }
+ else if (plane->type == RT_GRAPHIC_PLANE_TYPE_CURSOR)
+ {
+ gdev->cursor_plane = RT_NULL;
+ }
+ else if (plane->type == RT_GRAPHIC_PLANE_TYPE_OVERLAY)
+ {
+ spin_lock(&gdev->lock);
+ rt_list_remove(&plane->list);
+ spin_unlock(&gdev->lock);
+ }
+
+ rt_dm_ida_free(&gdev->plane_ida, plane->id);
+
+ return RT_EOK;
+}
+
+void rt_graphic_device_hotplug_event(struct rt_graphic_device *gdev)
+{
+ rt_err_t err;
+ rt_uint32_t width, height;
+ struct rt_device_rect_info rect;
+
+ RT_ASSERT(gdev != RT_NULL);
+
+ rt_graphic_device_enter(gdev);
+
+ graphic_edid_res(gdev, &width, &height);
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = width;
+ rect.height = height;
+
+ err = plane_fb_remap(gdev->primary_plane, gdev->primary_plane->mode, &rect);
+
+ if (err)
+ {
+ /* What the fuck? */
+ LOG_E("%s: hotplug event process error = %s",
+ rt_dm_dev_get_name(&gdev->parent), rt_strerror(err));
+ goto _out_lock;
+ }
+
+_out_lock:
+ rt_graphic_device_leave(gdev);
+
+ rt_atomic_store(&gdev->event_notifying, RT_TRUE);
+
+ if (gdev->event_notify.notify)
+ {
+ gdev->event_notify.notify(gdev->event_notify.dev);
+ }
+
+ rt_atomic_store(&gdev->event_notifying, RT_FALSE);
+}
+
+static void graphic_device_plane_update(struct rt_graphic_plane *plane,
+ struct rt_device_rect_info *rect)
+{
+ if (plane->ops->update)
+ {
+ plane->ops->update(plane, rect);
+ }
+}
+
+static void graphic_device_update(void *param)
+{
+ struct rt_device_rect_info rect;
+ struct rt_graphic_plane *plane;
+ struct rt_graphic_device *gdev = param;
+
+ rect.x = 0;
+ rect.y = 0;
+
+ spin_lock(&gdev->lock);
+
+ rect.width = gdev->primary_plane->width;
+ rect.height = gdev->primary_plane->height;
+ graphic_device_plane_update(gdev->primary_plane, &rect);
+
+ rt_list_for_each_entry(plane, &gdev->overlay_nodes, list)
+ {
+ rect.width = plane->width;
+ rect.height = plane->height;
+
+ graphic_device_plane_update(plane, &rect);
+ }
+
+ if ((plane = gdev->cursor_plane))
+ {
+ rect.x = plane->x;
+ rect.y = plane->y;
+ /* Ask driver to update position only */
+ rect.width = 0;
+ rect.height = 0;
+
+ graphic_device_plane_update(plane, &rect);
+ }
+
+ spin_unlock(&gdev->lock);
+}
+
+rt_err_t rt_graphic_device_update_auto(struct rt_graphic_device *gdev, rt_uint32_t update_ms)
+{
+ if (!gdev)
+ {
+ return -RT_EINVAL;
+ }
+
+ if (update_ms)
+ {
+ if (!gdev->update_timer)
+ {
+ char name[RT_NAME_MAX];
+
+ rt_snprintf(name, sizeof(name), "update-%s", rt_dm_dev_get_name(&gdev->parent));
+
+ gdev->update_timer = rt_timer_create(name, &graphic_device_update, gdev,
+ rt_tick_from_millisecond(update_ms),
+ RT_TIMER_FLAG_PERIODIC);
+
+ if (!gdev->update_timer)
+ {
+ return -RT_ENOMEM;
+ }
+ }
+
+ rt_timer_start(gdev->update_timer);
+ }
+ else if (gdev->update_timer)
+ {
+ rt_timer_stop(gdev->update_timer);
+ rt_timer_delete(gdev->update_timer);
+
+ gdev->update_timer = RT_NULL;
+ }
+
+ return RT_EOK;
+}
+
+void rt_graphic_device_enter(struct rt_graphic_device *gdev)
+{
+ RT_ASSERT(gdev != RT_NULL);
+
+ spin_lock(&gdev->lock);
+
+ if (gdev->update_timer)
+ {
+ rt_timer_stop(gdev->update_timer);
+ }
+}
+
+void rt_graphic_device_leave(struct rt_graphic_device *gdev)
+{
+ RT_ASSERT(gdev != RT_NULL);
+
+ if (gdev->update_timer)
+ {
+ rt_timer_start(gdev->update_timer);
+ }
+
+ spin_unlock(&gdev->lock);
+}
+
+rt_uint32_t rt_graphic_mode_bpp(rt_uint32_t mode)
+{
+ for (int i = 0; i < RT_ARRAY_SIZE(graphic_formats); ++i)
+ {
+ if (graphic_formats[i].mode == mode)
+ {
+ return graphic_formats[i].bits_per_pixel;
+ }
+ }
+
+ return 0;
+}
diff --git a/components/drivers/graphic/graphic_primary.c b/components/drivers/graphic/graphic_primary.c
new file mode 100644
index 0000000000..411931196f
--- /dev/null
+++ b/components/drivers/graphic/graphic_primary.c
@@ -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;
+}
diff --git a/components/drivers/graphic/graphic_simple.c b/components/drivers/graphic/graphic_simple.c
new file mode 100644
index 0000000000..141fc9d913
--- /dev/null
+++ b/components/drivers/graphic/graphic_simple.c
@@ -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);
+}
diff --git a/components/drivers/graphic/logo/.gitignore b/components/drivers/graphic/logo/.gitignore
new file mode 100644
index 0000000000..e524629a37
--- /dev/null
+++ b/components/drivers/graphic/logo/.gitignore
@@ -0,0 +1 @@
+logo.inc \ No newline at end of file
diff --git a/components/drivers/graphic/logo/Kconfig b/components/drivers/graphic/logo/Kconfig
new file mode 100644
index 0000000000..45bcfe3f59
--- /dev/null
+++ b/components/drivers/graphic/logo/Kconfig
@@ -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"
diff --git a/components/drivers/graphic/logo/SConscript b/components/drivers/graphic/logo/SConscript
new file mode 100644
index 0000000000..622ada0449
--- /dev/null
+++ b/components/drivers/graphic/logo/SConscript
@@ -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')
diff --git a/components/drivers/graphic/logo/logo.c b/components/drivers/graphic/logo/logo.c
new file mode 100644
index 0000000000..34597c8c03
--- /dev/null
+++ b/components/drivers/graphic/logo/logo.c
@@ -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;
+}
diff --git a/components/drivers/graphic/logo/logo.html b/components/drivers/graphic/logo/logo.html
new file mode 100644
index 0000000000..81e78c4aa1
--- /dev/null
+++ b/components/drivers/graphic/logo/logo.html
@@ -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>
diff --git a/components/drivers/include/drivers/graphic.h b/components/drivers/include/drivers/graphic.h
new file mode 100644
index 0000000000..df9b22729f
--- /dev/null
+++ b/components/drivers/include/drivers/graphic.h
@@ -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__ */
diff --git a/components/drivers/include/drivers/lcd.h b/components/drivers/include/drivers/lcd.h
index f2dcdce445..11fa8e6dcc 100644
--- a/components/drivers/include/drivers/lcd.h
+++ b/components/drivers/include/drivers/lcd.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