dm: virtio-gpu: EDID emulation of virtual monitor

Extended display identification data(EDID) is a data to store
display timings which are supported by ACRN virtual monitor.
Virtio-gpu FE driver will request it to config crtc for display
resolutions.

Tracked-On: #7210
Signed-off-by: Sun, Peng <peng.p.sun@linux.intel.com>
Reviewed-by: Zhao, yakui <yakui.zhao@intel.com>
Acked-by: Wang, Yu1 <yu1.wang@intel.com>
This commit is contained in:
Sun, Peng 2022-02-28 21:27:37 +08:00 committed by acrnsi-robot
parent bca6464e9f
commit eba0820bc7
3 changed files with 506 additions and 1 deletions

View File

@ -113,6 +113,22 @@ struct virtio_gpu_ctrl_hdr {
uint8_t padding[3];
};
/*
* Command: VIRTIO_GPU_CMD_GET_EDID
*/
struct virtio_gpu_get_edid {
struct virtio_gpu_ctrl_hdr hdr;
uint32_t scanout;
uint32_t padding;
};
struct virtio_gpu_resp_edid {
struct virtio_gpu_ctrl_hdr hdr;
uint32_t size;
uint32_t padding;
uint8_t edid[1024];
};
/*
* Per-device struct
*/
@ -243,14 +259,49 @@ virtio_gpu_cmd_unspec(struct virtio_gpu_command *cmd)
}
static void
virtio_gpu_notify_controlq(void *vdev, struct virtio_vq_info *vq)
virtio_gpu_update_resp_fence(struct virtio_gpu_ctrl_hdr *hdr,
struct virtio_gpu_ctrl_hdr *resp)
{
if ((hdr == NULL ) || (resp == NULL))
return;
if(hdr->flags & VIRTIO_GPU_FLAG_FENCE) {
resp->flags |= VIRTIO_GPU_FLAG_FENCE;
resp->fence_id = hdr->fence_id;
}
}
static void
virtio_gpu_cmd_get_edid(struct virtio_gpu_command *cmd)
{
struct virtio_gpu_get_edid req;
struct virtio_gpu_resp_edid resp;
struct virtio_gpu *gpu;
gpu = cmd->gpu;
memcpy(&req, cmd->iov[0].iov_base, sizeof(req));
cmd->iolen = sizeof(resp);
memset(&resp, 0, sizeof(resp));
/* Only one EDID block is enough */
resp.size = 128;
virtio_gpu_update_resp_fence(&cmd->hdr, &resp.hdr);
vdpy_get_edid(gpu->vdpy_handle, resp.edid, resp.size);
memcpy(cmd->iov[1].iov_base, &resp, sizeof(resp));
}
static void
virtio_gpu_ctrl_bh(void *data)
{
struct virtio_gpu *vdev;
struct virtio_vq_info *vq;
struct virtio_gpu_command cmd;
struct iovec iov[VIRTIO_GPU_MAXSEGS];
uint16_t flags[VIRTIO_GPU_MAXSEGS];
int n;
uint16_t idx;
vq = (struct virtio_vq_info *)data;
vdev = (struct virtio_gpu *)(vq->base);
cmd.gpu = vdev;
cmd.iolen = 0;
@ -271,6 +322,8 @@ virtio_gpu_notify_controlq(void *vdev, struct virtio_vq_info *vq)
sizeof(struct virtio_gpu_ctrl_hdr));
switch (cmd.hdr.type) {
case VIRTIO_GPU_CMD_GET_EDID:
virtio_gpu_cmd_get_edid(&cmd);
default:
virtio_gpu_cmd_unspec(&cmd);
break;
@ -281,6 +334,12 @@ virtio_gpu_notify_controlq(void *vdev, struct virtio_vq_info *vq)
vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
}
static void
virtio_gpu_notify_controlq(void *vdev, struct virtio_vq_info *vq)
{
virtio_gpu_ctrl_bh(vq);
}
static void
virtio_gpu_notify_cursorq(void *vdev, struct virtio_vq_info *vq)
{

View File

@ -74,6 +74,451 @@ static struct display {
.s.n_connect = 0
};
typedef enum {
ESTT = 1, // Established Timings I & II
STDT, // Standard Timings
ESTT3, // Established Timings III
} TIMING_MODE;
static const struct timing_entry {
uint32_t hpixel;// Horizontal pixels
uint32_t vpixel;// Vertical pixels
uint32_t byte; // byte idx in the Established Timings I & II
uint32_t byte_t3;// byte idx in the Established Timings III Descriptor
uint32_t bit; // bit idx
uint8_t hz; // frequency
} timings[] = {
/* Established Timings I & II (all @ 60Hz) */
{ .hpixel = 1024, .vpixel = 768, .byte = 36, .bit = 3, .hz = 60},
{ .hpixel = 800, .vpixel = 600, .byte = 35, .bit = 0, .hz = 60 },
{ .hpixel = 640, .vpixel = 480, .byte = 35, .bit = 5, .hz = 60 },
/* Standard Timings */
{ .hpixel = 1920, .vpixel = 1080, .hz = 60 },
{ .hpixel = 1280, .vpixel = 720, .hz = 60 },
};
typedef struct frame_param{
uint32_t hav_pixel; // Horizontal Addressable Video in pixels
uint32_t hb_pixel; // Horizontal Blanking in pixels
uint32_t hfp_pixel; // Horizontal Front Porch in pixels
uint32_t hsp_pixel; // Horizontal Sync Pulse Width in pixels
uint32_t lhb_pixel; // Left Horizontal Border or Right Horizontal
// Border in pixels
uint32_t vav_line; // Vertical Addressable Video in lines
uint32_t vb_line; // Vertical Blanking in lines
uint32_t vfp_line; // Vertical Front Porch in Lines
uint32_t vsp_line; // Vertical Sync Pulse Width in Lines
uint32_t tvb_line; // Top Vertical Border or Bottom Vertical
// Border in Lines
uint64_t pixel_clock; // Hz
uint32_t width; // mm
uint32_t height; // mm
}frame_param;
typedef struct base_param{
uint32_t h_pixel; // pixels
uint32_t v_pixel; // lines
uint32_t h_pixelmax;
uint32_t v_pixelmax;
uint32_t rate; // Hz
uint32_t width; // mm
uint32_t height; // mm
const char *id_manuf; // ID Manufacturer Name, ISA 3-character ID Code
uint16_t id_product; // ID Product Code
uint32_t id_sn; // ID Serial Number and it is a number only.
const char *sn; // Serial number.
const char *product_name;// Product name.
}base_param;
static void
vdpy_edid_set_baseparam(base_param *b_param, uint32_t width, uint32_t height)
{
b_param->h_pixel = width;
b_param->v_pixel = height;
b_param->h_pixelmax = 0;
b_param->v_pixelmax = 0;
b_param->rate = 60;
b_param->width = width;
b_param->height = height;
b_param->id_manuf = "ACRN";
b_param->id_product = 4321;
b_param->id_sn = 12345678;
b_param->sn = "A0123456789";
b_param->product_name = "ACRN_Monitor";
}
static void
vdpy_edid_set_frame(frame_param *frame, const base_param *b_param)
{
frame->hav_pixel = b_param->h_pixel;
frame->hb_pixel = b_param->h_pixel * 35 / 100;
frame->hfp_pixel = b_param->h_pixel * 25 / 100;
frame->hsp_pixel = b_param->h_pixel * 3 / 100;
frame->lhb_pixel = 0;
frame->vav_line = b_param->v_pixel;
frame->vb_line = b_param->v_pixel * 35 / 1000;
frame->vfp_line = b_param->v_pixel * 5 / 1000;
frame->vsp_line = b_param->v_pixel * 5 / 1000;
frame->tvb_line = 0;
frame->pixel_clock = b_param->rate *
(frame->hav_pixel + frame->hb_pixel + frame->lhb_pixel * 2) *
(frame->vav_line + frame->vb_line + frame->tvb_line * 2);
frame->width = b_param->width;
frame->height = b_param->height;
}
static void
vdpy_edid_set_color(uint8_t *edid, float red_x, float red_y,
float green_x, float green_y,
float blue_x, float blue_y,
float white_x, float white_y)
{
uint8_t *color;
uint16_t rx, ry, gx, gy, bx, by, wx, wy;
rx = transto_10bits(red_x);
ry = transto_10bits(red_y);
gx = transto_10bits(green_x);
gy = transto_10bits(green_y);
bx = transto_10bits(blue_x);
by = transto_10bits(blue_y);
wx = transto_10bits(white_x);
wy = transto_10bits(white_y);
color = edid + 25;
color[0] = ((rx & 0x03) << 6) |
((ry & 0x03) << 4) |
((gx & 0x03) << 2) |
(gy & 0x03);
color[1] = ((bx & 0x03) << 6) |
((by & 0x03) << 4) |
((wx & 0x03) << 2) |
(wy & 0x03);
color[2] = rx >> 2;
color[3] = ry >> 2;
color[4] = gx >> 2;
color[5] = gy >> 2;
color[6] = bx >> 2;
color[7] = by >> 2;
color[8] = wx >> 2;
color[9] = wy >> 2;
}
static void
vdpy_edid_set_timing(uint8_t *addr, const base_param *b_param, TIMING_MODE mode)
{
static uint16_t idx;
static uint16_t size;
const struct timing_entry *timing;
uint8_t stdcnt;
uint16_t hpixel;
int16_t AR;
stdcnt = 0;
if(mode == STDT) {
addr += 38;
}
idx = 0;
size = sizeof(timings) / sizeof(timings[0]);
for(; idx < size; idx++){
timing = timings + idx;
if ((b_param->h_pixelmax && b_param->h_pixelmax < timing->hpixel) ||
(b_param->v_pixelmax && b_param->v_pixelmax < timing->vpixel)) {
continue;
}
switch(mode){
case ESTT: // Established Timings I & II
if(timing->byte) {
addr[timing->byte] |= (1 << timing->bit);
break;
} else {
return;
}
case ESTT3: // Established Timings III
if(timing->byte_t3){
addr[timing->byte_t3] |= (1 << timing->bit);
break;
} else {
return;
}
case STDT: // Standard Timings
if(stdcnt < 8 && (timing->hpixel == b_param->h_pixel)) {
hpixel = (timing->hpixel >> 3) - 31;
if (timing->hpixel == 0 ||
timing->vpixel == 0) {
AR = -1;
} else if (hpixel & 0xff00) {
AR = -2;
} else if (timing->hpixel * 10 ==
timing->vpixel * 16) {
AR = 0;
} else if (timing->hpixel * 3 ==
timing->vpixel * 4) {
AR = 1;
} else if (timing->hpixel * 4 ==
timing->vpixel * 5) {
AR = 2;
} else if (timing->hpixel * 9 ==
timing->vpixel * 16) {
AR = 3;
} else {
AR = -2;
}
if (AR >= 0) {
addr[0] = hpixel & 0xff;
addr[1] = (AR << 6) | ((timing->hz - 60) & 0x3f);
addr += 2;
stdcnt++;
} else if (AR == -1){
addr[0] = 0x01;
addr[1] = 0x01;
addr += 2;
stdcnt++;
}
break;
} else {
return;
}
default:
return;
}
}
while(mode == STDT && stdcnt < 8){
addr[0] = 0x01;
addr[1] = 0x01;
addr += 2;
stdcnt++;
}
}
static void
vdpy_edid_set_dtd(uint8_t *dtd, const frame_param *frame)
{
uint16_t pixel_clk;
// Range: 10 kHz to 655.35 MHz in 10 kHz steps
pixel_clk = frame->pixel_clock / 10000;
memcpy(dtd, &pixel_clk, sizeof(pixel_clk));
dtd[2] = frame->hav_pixel & 0xff;
dtd[3] = frame->hb_pixel & 0xff;
dtd[4] = ((frame->hav_pixel & 0xf00) >> 4) |
((frame->hb_pixel & 0xf00) >> 8);
dtd[5] = frame->vav_line & 0xff;
dtd[6] = frame->vb_line & 0xff;
dtd[7] = ((frame->vav_line & 0xf00) >> 4) |
((frame->vb_line & 0xf00) >> 8);
dtd[8] = frame->hfp_pixel & 0xff;
dtd[9] = frame->hsp_pixel & 0xff;
dtd[10] = ((frame->vfp_line & 0xf) << 4) |
(frame->vsp_line & 0xf);
dtd[11] = ((frame->hfp_pixel & 0x300) >> 2) |
((frame->hsp_pixel & 0x300) >> 4) |
((frame->vfp_line & 0x030) >> 6) |
((frame->vsp_line & 0x030) >> 8);
dtd[12] = frame->width & 0xff;
dtd[13] = frame->height & 0xff;
dtd[14] = ((frame->width & 0xf00) >> 4) |
((frame->height & 0xf00) >> 8);
dtd[15] = frame->lhb_pixel & 0xff;
dtd[16] = frame->tvb_line & 0xff;
dtd[17] = 0x18;
}
static void
vdpy_edid_set_descripter(uint8_t *edid, uint8_t is_dtd,
uint8_t tag, const base_param *b_param)
{
static uint8_t offset;
uint8_t *desc;
frame_param frame;
const char* text;
uint16_t len;
offset = 54;
desc = edid + offset;
if (is_dtd) {
vdpy_edid_set_frame(&frame, b_param);
vdpy_edid_set_dtd(desc, &frame);
offset += 18;
return;
}
desc[3] = tag;
text = NULL;
switch(tag){
// Established Timings III Descriptor (tag #F7h)
case 0xf7:
desc[5] = 0x0a; // Revision Number
vdpy_edid_set_timing(desc, b_param, ESTT3);
break;
// Display Range Limits & Additional Timing Descriptor (tag #FDh)
case 0xfd:
desc[5] = 50; // Minimum Vertical Rate. (50 -> 125 Hz)
desc[6] = 125; // Maximum Vertical Rate.
desc[7] = 30; // Minimum Horizontal Rate.(30 -> 160 kHz)
desc[8] = 160; // Maximum Horizontal Rate.
desc[9] = 2550 / 10; // Max Pixel Clock. (2550 MHz)
desc[10] = 0x01; // no extended timing information
desc[11] = '\n'; // padding
break;
// Display Product Name (ASCII) String Descriptor (tag #FCh)
case 0xfc:
// Display Product Serial Number Descriptor (tag #FFh)
case 0xff:
text = (tag == 0xff) ? b_param->sn : b_param->product_name;
memset(desc + 5, ' ', 13);
if (text == NULL)
break;
len = strlen(text);
if (len > 12)
len = 12;
memcpy(desc + 5, text, len);
desc[len + 5] = '\n';
break;
// Dummy Descriptor (Tag #10h)
case 0x10:
default:
break;
}
offset += 18;
}
static uint8_t
vdpy_edid_get_checksum(uint8_t *edid)
{
uint8_t sum;
int i;
sum = 0;
for (i = 0; i < 127; i++) {
sum += edid[i];
}
return 0x100 - sum;
}
static void
vdpy_edid_generate(uint8_t *edid, size_t size, struct edid_info *info)
{
uint16_t id_manuf;
uint16_t id_product;
uint32_t serial;
base_param b_param, c_param;
vdpy_edid_set_baseparam(&b_param, info->prefx, info->prefy);
memset(edid, 0, size);
/* edid[7:0], fixed header information, (00 FF FF FF FF FF FF 00)h */
memset(edid + 1, 0xff, 6);
/* edid[17:8], Vendor & Product Identification */
// Manufacturer ID is a big-endian 16-bit value.
id_manuf = ((((b_param.id_manuf[0] - '@') & 0x1f) << 10) |
(((b_param.id_manuf[1] - '@') & 0x1f) << 5) |
(((b_param.id_manuf[2] - '@') & 0x1f) << 0));
edid[8] = id_manuf >> 8;
edid[9] = id_manuf & 0xff;
// Manufacturer product code is a little-endian 16-bit number.
id_product = b_param.id_product;
memcpy(edid+10, &id_product, sizeof(id_product));
// Serial number is a little-endian 32-bit value.
serial = b_param.id_sn;
memcpy(edid+12, &serial, sizeof(serial));
edid[16] = 0; // Week of Manufacture
edid[17] = 2018 - 1990; // Year of Manufacture or Model Year.
// Acrn is released in 2018.
edid[18] = 1; // Version Number
edid[19] = 4; // Revision Number
/* edid[24:20], Basic Display Parameters & Features */
// Video Input Definition: 1 Byte
edid[20] = 0xa5; // Digital input;
// 8 Bits per Primary Color;
// DisplayPort is supported
// Horizontal and Vertical Screen Size or Aspect Ratio: 2 Bytes
// screen size, in centimetres
edid[21] = info->prefx / 10;
edid[22] = info->prefy / 10;
// Display Transfer Characteristics (GAMMA): 1 Byte
// Stored Value = (GAMMA x 100) - 100
edid[23] = 120; // display gamma: 2.2
// Feature Support: 1 Byte
edid[24] = 0x06; // sRGB Standard is the default color space;
// Preferred Timing Mode includes the native
// pixel format and preferred.
/* edid[34:25], Display x, y Chromaticity Coordinates */
vdpy_edid_set_color(edid, 0.6400, 0.3300,
0.3000, 0.6000,
0.1500, 0.0600,
0.3127, 0.3290);
/* edid[37:35], Established Timings */
vdpy_edid_set_timing(edid, &b_param, ESTT);
/* edid[53:38], Standard Timings: Identification 1 -> 8 */
vdpy_edid_set_timing(edid, &b_param, STDT);
/* edid[125:54], Detailed Timing Descriptor - 18 bytes x 4 */
// Detailed Timing Descriptor 1
vdpy_edid_set_baseparam(&c_param, VDPY_MAX_WIDTH, VDPY_MAX_HEIGHT);
vdpy_edid_set_descripter(edid, 0x1, 0, &c_param);
// Detailed Timing Descriptor 2
vdpy_edid_set_baseparam(&c_param, VDPY_DEFAULT_WIDTH, VDPY_DEFAULT_HEIGHT);
vdpy_edid_set_descripter(edid, 0x1, 0, &c_param);
// Display Product Name (ASCII) String Descriptor (tag #FCh)
vdpy_edid_set_descripter(edid, 0, 0xfc, &b_param);
// Display Product Serial Number Descriptor (tag #FFh)
vdpy_edid_set_descripter(edid, 0, 0xff, &b_param);
/* EDID[126], Extension Block Count */
edid[126] = 0; // no Extension Block
/* Checksum */
edid[127] = vdpy_edid_get_checksum(edid);
}
void
vdpy_get_edid(int handle, uint8_t *edid, size_t size)
{
struct edid_info edid_info;
if (handle == vdpy.s.n_connect) {
edid_info.prefx = vdpy.info.width;
edid_info.prefy = vdpy.info.height;
edid_info.maxx = VDPY_MAX_WIDTH;
edid_info.maxy = VDPY_MAX_HEIGHT;
} else {
edid_info.prefx = 0;
edid_info.prefy = 0;
edid_info.maxx = 0;
edid_info.maxy = 0;
}
edid_info.refresh_rate = 0;
edid_info.vendor = NULL;
edid_info.name = NULL;
edid_info.sn = NULL;
vdpy_edid_generate(edid, size, &edid_info);
}
void
vdpy_get_display_info(int handle, struct display_info *info)
{

View File

@ -73,6 +73,7 @@ void vdpy_get_display_info(int handle, struct display_info *info);
void vdpy_surface_set(int handle, struct surface *surf);
void vdpy_surface_update(int handle, struct surface *surf);
bool vdpy_submit_bh(int handle, struct vdpy_display_bh *bh);
void vdpy_get_edid(int handle, uint8_t *edid, size_t size);
int vdpy_deinit(int handle);
void gfx_ui_deinit();