diff options
| author | GuEe-GUI <[email protected]> | 2025-12-12 15:58:12 +0800 |
|---|---|---|
| committer | R b b666 <[email protected]> | 2025-12-15 16:54:23 +0800 |
| commit | 5abecc1fd0c3ee9d71ce6b3ff3ee95eda4825065 (patch) | |
| tree | 752168c49b2d3e043e5c264cc0e23fa5376516e5 /components | |
| parent | 35bae6824b61910600cb47a3f1c345671609748d (diff) | |
[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 <[email protected]>
Diffstat (limited to 'components')
| -rw-r--r-- | components/drivers/graphic/Kconfig | 13 | ||||
| -rw-r--r-- | components/drivers/graphic/SConscript | 23 | ||||
| -rw-r--r-- | components/drivers/graphic/backlight/Kconfig | 7 | ||||
| -rw-r--r-- | components/drivers/graphic/backlight/SConscript | 14 | ||||
| -rw-r--r-- | components/drivers/graphic/backlight/backlight.c | 231 | ||||
| -rw-r--r-- | components/drivers/graphic/framebuffer/Kconfig | 8 | ||||
| -rw-r--r-- | components/drivers/graphic/framebuffer/SConscript | 16 | ||||
| -rw-r--r-- | components/drivers/graphic/graphic.c | 1495 | ||||
| -rw-r--r-- | components/drivers/graphic/graphic_primary.c | 381 | ||||
| -rw-r--r-- | components/drivers/graphic/graphic_simple.c | 295 | ||||
| -rw-r--r-- | components/drivers/graphic/logo/.gitignore | 1 | ||||
| -rw-r--r-- | components/drivers/graphic/logo/Kconfig | 31 | ||||
| -rw-r--r-- | components/drivers/graphic/logo/SConscript | 84 | ||||
| -rw-r--r-- | components/drivers/graphic/logo/logo.c | 216 | ||||
| -rw-r--r-- | components/drivers/graphic/logo/logo.html | 243 | ||||
| -rw-r--r-- | components/drivers/include/drivers/graphic.h | 367 | ||||
| -rw-r--r-- | components/drivers/include/drivers/lcd.h | 33 |
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">×</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 |
