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 + +#define DBG_TAG "rtdm.backlight" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +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 +#include +#include + +#define DBG_TAG "rtdm.graphic" +#define DBG_LVL DBG_INFO +#include + +#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 +#include +#include + +#define DBG_TAG "graphic.primary" +#define DBG_LVL DBG_INFO +#include + +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 +#include +#include + +#define DBG_TAG "graphic.simple" +#define DBG_LVL DBG_INFO +#include + +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_ in Kconfig: +# +# config RT_GRAPHIC_LOGO_ +# bool " logo" +# +# RT_GRAPHIC_LOGO__PATH in Kconfig.path: +# +# if RT_GRAPHIC_LOGO_ +# config RT_GRAPHIC_LOGO__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 = data[0].decode('utf-8') + + # PPM: + offset = 1 + while True: + comment = str(data[offset].decode('utf-8')) + if comment[0] != '#': + break + offset += 1 + + # PPM: + logo_width, logo_height = map(int, data[offset].split()) + + # PPM: + logo_max_val = int(data[offset + 1]) + + # PPM: + 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 +#include +#include + +#define DBG_TAG "graphic.logo" +#define DBG_LVL DBG_INFO +#include + +#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 @@ + + + + + + + + + Logo PPM Previews + + + +
+
+
+
+
+ + + + +
+
+
+ +
+
+ +
+
+
+ +
+
+
+ + + 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 +#include +#include +#include +#include +#include + +#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 + /* 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 diff --git a/examples/test/dm_graphic_test.c b/examples/test/dm_graphic_test.c new file mode 100644 index 0000000000..a6894ddc6e --- /dev/null +++ b/examples/test/dm_graphic_test.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include + +#ifdef RT_USING_GRAPHIC +typedef rt_int32_t fixed; /* Q16.16 */ + +#define FIX_ONE 65536 +#define FIX_06 19661 /* 0.3 */ +#define FIX_35 22938 /* 0.35 */ +#define FIX_14 917504 /* 14.0 */ +#define FIX_286 187432 /* 2.86 */ +#define FIX_75 49152 /* 0.75 */ +#define FIX_1_3 21845 +#define FIX_2_3 43690 +#define FIX_6 393216 +#define FIX_3 196608 +#define FIX_255 16711680 /* 255 */ + +#define FMUL(a,b) ((fixed)(((rt_int64_t)(a) * (b)) >> 16)) +#define FDIV(a,b) ((fixed)(((rt_int64_t)(a) << 16) / (b))) + +typedef struct { fixed x, y, z; } vec3f; + +rt_inline fixed fix_floor(fixed x) +{ + return x & 0xffff0000; +} + +rt_inline fixed fix_fract(fixed x) +{ + return x & 0x0000ffff; +} + +rt_inline fixed fix_abs(fixed x) +{ + return rt_abs(x); +} + +rt_inline fixed fix_clamp(fixed x, fixed a, fixed b) +{ + return rt_clamp(x, a, b); +} + +static vec3f clamp3(vec3f v, fixed a, fixed b) +{ + vec3f r = + { + .x = fix_clamp(v.x, a, b), + .y = fix_clamp(v.y, a, b), + .z = fix_clamp(v.z, a, b), + }; + + return r; +} + +static vec3f mix3(vec3f A, vec3f B, fixed t) +{ + fixed omt = FIX_ONE - t; + vec3f r = + { + .x = FMUL(A.x, omt) + FMUL(B.x, t), + .y = FMUL(A.y, omt) + FMUL(B.y, t), + .z = FMUL(A.z, omt) + FMUL(B.z, t), + }; + + return r; +} + +static vec3f hsv2rgb(vec3f c) +{ + vec3f t, r, base, mixed; + fixed pX = FMUL(fix_fract(c.x + FIX_ONE), FIX_6); + fixed pY = FMUL(fix_fract(c.x + FIX_2_3), FIX_6); + fixed pZ = FMUL(fix_fract(c.x + FIX_1_3), FIX_6); + + pX = fix_abs(pX - FIX_3); + pY = fix_abs(pY - FIX_3); + pZ = fix_abs(pZ - FIX_3); + + t.x = pX - FIX_ONE; + t.y = pY - FIX_ONE; + t.z = pZ - FIX_ONE; + t = clamp3(t, 0, FIX_ONE); + + base.x = FIX_ONE; + base.y = FIX_ONE; + base.z = FIX_ONE; + + mixed = mix3(base, t, c.y); + + r.x = FMUL(c.z, mixed.x); + r.y = FMUL(c.z, mixed.y); + r.z = FMUL(c.z, mixed.z); + + return r; +} + +static void shader_frame(int px, int py, int width, int height, int frame, + rt_uint8_t *r, rt_uint8_t *g, rt_uint8_t *b) +{ + vec3f col; + fixed uv_x, uv_y, shift, size_computed, st, x; + + uv_x = FDIV((fixed)(px << 16), (fixed)(width << 16)); + uv_y = FDIV((fixed)(py << 16), (fixed)(width << 16)); + + shift = fix_fract(FDIV(FMUL((frame << 16), FIX_06), FIX_286)); + shift = FMUL(shift, FIX_286); + + size_computed = FMUL(uv_x + shift - uv_y, FIX_35); + + st = FMUL(size_computed, FIX_14); + x = FDIV(fix_floor(st), FIX_14); + + col = hsv2rgb((vec3f){ x, FIX_75, FIX_ONE }); + + *r = (rt_uint8_t)((FMUL(col.x, FIX_255) >> 16) & 255); + *g = (rt_uint8_t)((FMUL(col.y, FIX_255) >> 16) & 255); + *b = (rt_uint8_t)((FMUL(col.z, FIX_255) >> 16) & 255); +} + +static void conv_gray4(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + rt_uint8_t gray = (r * 30 + g * 59 + b * 11) / 100; + *(rt_uint8_t *)pixel = gray >> 4; +} + +static void conv_gray16(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + rt_uint16_t gray = (r * 30 + g * 59 + b * 11) / 100; + rt_uint16_t out = (gray << 8) | gray; + *(rt_uint16_t *)pixel = out; +} + +static void conv_rgb332(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint8_t*)pixel = ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); +} + +static void conv_rgb444(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint16_t *)pixel = ((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4); +} + +static void conv_rgb565(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint16_t *)pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static void conv_rgb565p(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint16_t *)pixel = ((g >> 2) << 10) | ((r >> 3) << 5) | (b >> 3); +} + +static void conv_bgr565(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint16_t *)pixel = ((b >> 3) << 11) | ((g >> 2) << 5) | (r >> 3); +} + +static void conv_rgb666(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + rt_uint8_t *p = (rt_uint8_t *)pixel; + + p[0] = r & 0xfc; + p[1] = g & 0xfc; + p[2] = b & 0xfc; +} + +static void conv_rgb888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + rt_uint8_t *p = (rt_uint8_t *)pixel; + + p[0] = r; + p[1] = g; + p[2] = b; +} + +static void conv_bgr888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + rt_uint8_t *p = (rt_uint8_t*)pixel; + p[0] = b; + p[1] = g; + p[2] = r; +} + +static void conv_argb888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint32_t *)pixel = (0xffU << 24) | (r << 16) | (g << 8) | b; +} + +static void conv_abgr888(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) +{ + *(rt_uint32_t *)pixel = (0xffU << 24) | (b << 16) | (g << 8) | r; +} + +static void (*conv_funcs[])(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel) = +{ + [RTGRAPHIC_PIXEL_FORMAT_GRAY4] = conv_gray4, + [RTGRAPHIC_PIXEL_FORMAT_GRAY16] = conv_gray16, + [RTGRAPHIC_PIXEL_FORMAT_RGB332] = conv_rgb332, + [RTGRAPHIC_PIXEL_FORMAT_RGB444] = conv_rgb444, + [RTGRAPHIC_PIXEL_FORMAT_RGB565] = conv_rgb565, + [RTGRAPHIC_PIXEL_FORMAT_RGB565P] = conv_rgb565p, + [RTGRAPHIC_PIXEL_FORMAT_BGR565] = conv_bgr565, + [RTGRAPHIC_PIXEL_FORMAT_RGB666] = conv_rgb666, + [RTGRAPHIC_PIXEL_FORMAT_RGB888] = conv_rgb888, + [RTGRAPHIC_PIXEL_FORMAT_BGR888] = conv_bgr888, + [RTGRAPHIC_PIXEL_FORMAT_ARGB888] = conv_argb888, + [RTGRAPHIC_PIXEL_FORMAT_ABGR888] = conv_abgr888, +}; + +rt_err_t graphic_start(const char *gdev, int count) +{ + rt_err_t err; + rt_uint8_t *vfb, *fb, *pixel, bpp; + struct rt_device_graphic_info info; + struct rt_device *dev = rt_device_find(gdev); + void (*conv_func)(rt_uint8_t r, rt_uint8_t g, rt_uint8_t b, void *pixel); + + if (!dev) + { + return -RT_EINVAL; + } + + if ((err = rt_device_open(dev, 0))) + { + return err; + } + + if ((err = rt_device_control(dev, RTGRAPHIC_CTRL_GET_INFO, &info))) + { + goto _end; + } + + if (!(vfb = rt_malloc(info.smem_len))) + { + err = -RT_ENOMEM; + goto _end; + } + + bpp = info.bits_per_pixel / 8; + conv_func = conv_funcs[info.pixel_format]; + + for (int frame = 0; frame < count; ++frame) + { + fb = vfb; + + for (int y = 0; y < info.height; ++y) + { + pixel = fb; + + for (int x = 0; x < info.width; ++x) + { + rt_uint8_t r, g, b; + + shader_frame(x, y, info.width, info.height, frame, &r, &g, &b); + + conv_func(r, g, b, pixel); + + pixel += bpp; + } + + fb += info.pitch; + } + + rt_memcpy(info.framebuffer, vfb, info.smem_len); + } + + rt_free(vfb); + +_end: + rt_device_close(dev); + + return err; +} + +#ifdef RT_USING_FINSH +#include + +static int _graphic_start(int argc, char**argv) +{ + int count = 10; + const char *gdev = "fb0"; + + if (argc > 1) + { + gdev = argv[1]; + } + if (argc > 2) + { + count = atoi(argv[2]); + } + + return (int)graphic_start(gdev, count); +} +MSH_CMD_EXPORT_ALIAS(_graphic_start, graphic_start, fixed resolution only e.g: graphic_start("fb0", 10)); +#endif /* RT_USING_FINSH */ +#endif /* RT_USING_GRAPHIC */ diff --git a/examples/test/dm_hmi_test.c b/examples/test/dm_hmi_test.c new file mode 100644 index 0000000000..b8fea78ad6 --- /dev/null +++ b/examples/test/dm_hmi_test.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-02-25 GuEe-GUI the first version + */ + +#include +#include + +#include +#include + +#if defined(RT_USING_GRAPHIC) && defined(RT_USING_INPUT) + +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +struct hmi_info +{ + struct rt_device *gdev; + struct rt_device *idev; + + struct rt_device_graphic_info info; + struct rt_device_notify event_notify; + struct rt_input_handler handler; + + rt_bool_t event; + rt_bool_t vsync; + rt_bool_t keydown; + + rt_uint32_t x, y; + rt_uint32_t dx, dy; + rt_uint32_t bytes_per_pixel; + rt_ubase_t line[2]; + rt_ubase_t colors[4]; + + void *backend_framebuffer; +}; + +static rt_bool_t hmi_working; + +static struct rt_input_device *to_input_device(struct rt_device *idev) +{ + return rt_container_of(idev, struct rt_input_device, parent); +} + +static rt_ubase_t to_color(rt_uint8_t color255, rt_ubase_t color_max) +{ + return (rt_ubase_t)color255 * color_max / 255; +} + +static void hmi_reset(struct hmi_info *hmi) +{ + void *cursor; + rt_ubase_t none_alpha; + rt_ubase_t red_off, green_off, blue_off, alpha_off; + rt_ubase_t red_mask, green_mask, blue_mask, alpha_mask; + struct fb_var_screeninfo var; + + if (hmi->backend_framebuffer) + { + rt_free(hmi->backend_framebuffer); + } + + rt_device_control(hmi->gdev, FBIOGET_VSCREENINFO, &var); + rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_GET_INFO, &hmi->info); + + hmi->backend_framebuffer = rt_malloc(hmi->info.smem_len); + + hmi->bytes_per_pixel = hmi->info.bits_per_pixel / 8; + red_off = var.red.offset; + red_mask = RT_GENMASK(var.red.length - 1, 0); + green_off = var.green.offset; + green_mask = RT_GENMASK(var.green.length - 1, 0); + blue_off = var.blue.offset; + blue_mask = RT_GENMASK(var.blue.length - 1, 0); + + if (var.transp.length) + { + alpha_off = var.transp.offset; + alpha_mask = RT_GENMASK(var.transp.length - 1, 0); + } + else + { + alpha_off = 0; + alpha_mask = 0; + } + + if ((cursor = rt_malloc(CURSOR_WIDTH * CURSOR_HEIGHT * hmi->bytes_per_pixel))) + { + rt_uint8_t *stream = cursor; + rt_ubase_t color = ((to_color(0x82, red_mask)) << red_off) | + (to_color(0x50, green_mask) << green_off) | + (to_color(0xdf, blue_mask) << blue_off) | + (to_color(0xcc, alpha_mask) << alpha_off); + + for (int y = 0; y < CURSOR_HEIGHT; ++y) + { + for (int x = 0; x < CURSOR_WIDTH; ++x) + { + rt_memcpy(stream, &color, hmi->bytes_per_pixel); + stream += hmi->bytes_per_pixel; + } + } + + rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_TYPE, cursor); + rt_free(cursor); + } + + none_alpha = alpha_mask << alpha_off; + + hmi->line[0] = ~0UL; + hmi->line[1] = none_alpha; + + hmi->colors[0] = ((to_color(0xff, red_mask)) << red_off) | + (to_color(0x4b, green_mask) << green_off) | + (to_color(0x00, blue_mask) << blue_off) | none_alpha; + hmi->colors[1] = ((to_color(0x7f, red_mask)) << red_off) | + (to_color(0xdb, green_mask) << green_off) | + (to_color(0x3b, blue_mask) << blue_off) | none_alpha; + hmi->colors[2] = ((to_color(0x00, red_mask)) << red_off) | + (to_color(0xa4, green_mask) << green_off) | + (to_color(0xef, blue_mask) << blue_off) | none_alpha; + hmi->colors[3] = ((to_color(0xff, red_mask)) << red_off) | + (to_color(0xb8, green_mask) << green_off) | + (to_color(0x1c, blue_mask) << blue_off) | none_alpha; + + hmi->event = RT_FALSE; +} + +static void hmi_graphic_notify(rt_device_t dev) +{ + struct hmi_info *hmi = (void *)dev; + + hmi->event = RT_TRUE; +} + +static rt_bool_t hmi_input_callback(struct rt_input_handler *handler, + struct rt_input_event *ev) +{ + struct hmi_info *hmi = handler->priv; + + if (ev->type == EV_ABS) + { + if (ev->code == 0) + { + hmi->dx = (ev->value * hmi->info.width) / + (handler->idev->absinfo->maximum - handler->idev->absinfo->minimum); + } + else if (ev->code == 1) + { + hmi->dy = (ev->value * hmi->info.height) / + (handler->idev->absinfo->maximum - handler->idev->absinfo->minimum); + } + } + else if (ev->type == EV_KEY) + { + if (ev->code == BTN_LEFT) + { + if (hmi->keydown && ev->value == 0) + { + /* Swap lines color */ + hmi->line[0] ^= hmi->line[1]; + hmi->line[1] ^= hmi->line[0]; + hmi->line[0] ^= hmi->line[1]; + + hmi->keydown = RT_FALSE; + hmi->vsync = RT_FALSE; + } + else + { + hmi->keydown = RT_TRUE; + } + } + } + else if (ev->type == EV_SYN) + { + hmi->vsync = RT_FALSE; + } + + return RT_TRUE; +} + +static void hmi_loop(void *param) +{ + struct hmi_info *hmi = param; + struct rt_device_rect_info rect; + struct rt_device_graphic_ops *gops; + + /* Graphic device event */ + hmi->event_notify.notify = &hmi_graphic_notify; + hmi->event_notify.dev = (void *)hmi; + rt_device_control(hmi->gdev, RT_DEVICE_CTRL_NOTIFY_SET, &hmi->event_notify); + + /* Input device event */ + hmi->handler.idev = to_input_device(hmi->idev); + hmi->handler.identify = RT_NULL; + hmi->handler.callback = &hmi_input_callback; + hmi->handler.priv = hmi; + rt_input_add_handler(&hmi->handler); + + hmi->backend_framebuffer = RT_NULL; + hmi_reset(hmi); + + hmi->dx = hmi->info.width >> 1; + hmi->dy = hmi->info.height >> 1; + + rect.x = 0; + rect.y = 0; + + gops = rt_graphix_ops(hmi->gdev); + rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_POWERON, RT_NULL); + + while (hmi_working) + { + rt_ubase_t pos; + + /* Wait graphic change */ + if (hmi->event) + { + hmi_reset(hmi); + } + + hmi->x = hmi->dx; + hmi->y = hmi->dy; + + rect.width = hmi->info.width; + rect.height = hmi->info.height; + pos = RTGRAPHIC_PIXEL_POSITION(hmi->x, hmi->y); + + for (int i = 0; i < RT_ARRAY_SIZE(hmi->colors); ++i) + { + rt_uint32_t x1, y1, x2, y2; + void *fb = hmi->backend_framebuffer ? : hmi->info.framebuffer; + + switch (i) + { + case 0: + x1 = 0; + y1 = 0; + x2 = hmi->x; + y2 = hmi->y; + break; + + case 1: + x1 = hmi->x; + y1 = 0; + x2 = hmi->info.width; + y2 = hmi->y; + break; + + case 2: + x1 = 0; + y1 = hmi->y; + x2 = hmi->x; + y2 = hmi->info.height; + break; + + case 3: + x1 = hmi->x; + y1 = hmi->y; + x2 = hmi->info.width; + y2 = hmi->info.height; + break; + } + + fb += x1 * hmi->bytes_per_pixel + y1 * hmi->info.pitch; + + for (int y = y1; y < y2; ++y) + { + void *fb_entry = fb; + + for (int x = x1; x < x2; ++x) + { + rt_memcpy(fb, &hmi->colors[i], hmi->bytes_per_pixel); + fb += hmi->bytes_per_pixel; + } + + fb = fb_entry + hmi->info.pitch; + } + } + + if (hmi->backend_framebuffer) + { + rt_memcpy(hmi->info.framebuffer, hmi->backend_framebuffer, hmi->info.smem_len); + } + + gops->draw_hline((void *)&hmi->line[0], 0, rect.width, hmi->y); + gops->draw_vline((void *)&hmi->line[1], hmi->x, 0, rect.height); + + rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_RECT_UPDATE, &rect); + rt_device_control(hmi->gdev, RT_DEVICE_CTRL_CURSOR_SET_POSITION, (void *)pos); + + /* Next position */ + hmi->vsync = RT_TRUE; + rt_hw_wmb(); + + while (hmi_working && hmi->vsync) + { + rt_thread_mdelay(1); + } + } + + rt_device_control(hmi->gdev, RTGRAPHIC_CTRL_POWEROFF, RT_NULL); + + rt_memset(&hmi->event_notify, 0, sizeof(hmi->event_notify)); + rt_device_control(hmi->gdev, RT_DEVICE_CTRL_NOTIFY_SET, &hmi->event_notify); + + rt_input_del_handler(&hmi->handler); + + rt_device_close(hmi->gdev); + rt_device_close(hmi->idev); + + if (hmi->backend_framebuffer) + { + rt_free(hmi->backend_framebuffer); + } + rt_free(hmi); + + rt_thread_delete(rt_thread_self()); +} + +rt_err_t hmi_start(const char *gdev, const char *idev) +{ + rt_err_t err; + struct hmi_info *hmi; + struct rt_thread *loop; + + if (hmi_working) + { + rt_kprintf("HMI is running\n"); + return -RT_EBUSY; + } + + hmi = rt_malloc(sizeof(*hmi)); + + if (!hmi) + { + return -RT_ENOMEM; + } + + hmi->gdev = rt_device_find(gdev); + hmi->idev = rt_device_find(idev); + + if (!hmi->gdev || !hmi->idev) + { + rt_free(hmi); + return -RT_EINVAL; + } + + if (!rt_bitmap_test_bit(to_input_device(hmi->idev)->cap, EV_ABS)) + { + rt_kprintf("%s is not a ABS input\n", idev); + rt_free(hmi); + return -RT_EINVAL; + } + + if ((err = rt_device_open(hmi->gdev, 0))) + { + rt_free(hmi); + return err; + } + + if ((err = rt_device_open(hmi->idev, 0))) + { + rt_device_close(hmi->gdev); + rt_free(hmi); + return err; + } + + loop = rt_thread_create("HMI", hmi_loop, hmi, + DM_THREAD_STACK_SIZE, + RT_THREAD_PRIORITY_MAX / 3, + rt_tick_from_millisecond(RT_GRAPHIC_UPDATE_MS)); + + if (!loop) + { + rt_device_close(hmi->gdev); + rt_device_close(hmi->idev); + rt_free(hmi); + return -RT_ENOMEM; + } + + hmi_working = RT_TRUE; + rt_thread_startup(loop); + + return RT_EOK; +} + +rt_err_t hmi_stop(void) +{ + hmi_working = RT_FALSE; + return RT_EOK; +} + +#ifdef RT_USING_FINSH +static int _hmi_start(int argc, char**argv) +{ + const char *gdev = "fb0", *idev = "input0"; + + if (argc == 3) + { + gdev = argv[1]; + idev = argv[2]; + } + + return (int)hmi_start(gdev, idev); +} +MSH_CMD_EXPORT_ALIAS(_hmi_start, hmi_start, e.g: hmi_start("fb0", "input0")); + +static int _hmi_stop(void) +{ + return (int)hmi_stop(); +} +MSH_CMD_EXPORT_ALIAS(_hmi_stop, hmi_stop, e.g: hmi_exit()); +#endif /* RT_USING_FINSH */ +#endif /* RT_USING_GRAPHIC && RT_USING_INPUT */