Files
rt-thread/components/drivers/graphic/graphic_simple.c
GuEe-GUI 5abecc1fd0 [dm][graphic] support dm mode
1. Add backlight framework for graphic.
2. Add framebuffer and plane, power, EDID for graphic framework
3. Add boot logo render for graphic
4. Update lcd.h

Signed-off-by: GuEe-GUI <2991707448@qq.com>
2025-12-15 16:54:23 +08:00

296 lines
8.5 KiB
C

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