hw/display/apple-gfx: Adds configurable mode list

This change adds a property 'display_modes' on the graphics device
which permits specifying a list of display modes. (screen resolution
and refresh rate)

The property is an array of a custom type to make the syntax slightly
less awkward to use, for example:

-device '{"driver":"apple-gfx-pci", "display-modes":["1920x1080@60", "3840x2160@60"]}'

Signed-off-by: Phil Dennis-Jordan <phil@philjordan.eu>
Reviewed-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Tested-by: Akihiko Odaki <akihiko.odaki@daynix.com>
Message-ID: <20241223221645.29911-5-phil@philjordan.eu>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
This commit is contained in:
Phil Dennis-Jordan 2024-07-15 23:07:03 +02:00 committed by Philippe Mathieu-Daudé
parent b21f17cce5
commit bb43a2342d
5 changed files with 139 additions and 18 deletions

View file

@ -255,6 +255,11 @@ static void apple_gfx_mmio_reset(Object *obj, ResetType type)
[s->common.pgdev reset];
}
static const Property apple_gfx_mmio_properties[] = {
DEFINE_PROP_ARRAY("display-modes", AppleGFXMMIOState,
common.num_display_modes, common.display_modes,
qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
};
static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
{
@ -264,6 +269,8 @@ static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data)
rc->phases.hold = apple_gfx_mmio_reset;
dc->hotpluggable = false;
dc->realize = apple_gfx_mmio_realize;
device_class_set_props(dc, apple_gfx_mmio_properties);
}
static const TypeInfo apple_gfx_mmio_types[] = {

View file

@ -115,6 +115,12 @@ static void apple_gfx_pci_reset(Object *obj, ResetType type)
[s->common.pgdev reset];
}
static const Property apple_gfx_pci_properties[] = {
DEFINE_PROP_ARRAY("display-modes", AppleGFXPCIState,
common.num_display_modes, common.display_modes,
qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode),
};
static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@ -131,7 +137,7 @@ static void apple_gfx_pci_class_init(ObjectClass *klass, void *data)
pci->class_id = PCI_CLASS_DISPLAY_OTHER;
pci->realize = apple_gfx_pci_realize;
/* TODO: Property for setting mode list */
device_class_set_props(dc, apple_gfx_pci_properties);
}
static const TypeInfo apple_gfx_pci_types[] = {

View file

@ -25,6 +25,12 @@
typedef QTAILQ_HEAD(, PGTask_s) PGTaskList;
typedef struct AppleGFXDisplayMode {
uint16_t width_px;
uint16_t height_px;
uint16_t refresh_rate_hz;
} AppleGFXDisplayMode;
typedef struct AppleGFXState {
/* Initialised on init/realize() */
MemoryRegion iomem_gfx;
@ -33,6 +39,8 @@ typedef struct AppleGFXState {
QemuConsole *con;
id<MTLDevice> mtl;
id<MTLCommandQueue> mtl_queue;
AppleGFXDisplayMode *display_modes;
uint32_t num_display_modes;
/* List `tasks` is protected by task_mutex */
QemuMutex task_mutex;
@ -60,5 +68,7 @@ void *apple_gfx_host_ptr_for_gpa_range(uint64_t guest_physical,
uint64_t length, bool read_only,
MemoryRegion **mapping_in_region);
extern const PropertyInfo qdev_prop_apple_gfx_display_mode;
#endif

View file

@ -31,9 +31,10 @@
#import <ParavirtualizedGraphics/ParavirtualizedGraphics.h>
static const PGDisplayCoord_t apple_gfx_modes[] = {
{ .x = 1440, .y = 1080 },
{ .x = 1280, .y = 1024 },
static const AppleGFXDisplayMode apple_gfx_default_modes[] = {
{ 1920, 1080, 60 },
{ 1440, 1080, 60 },
{ 1280, 1024, 60 },
};
static Error *apple_gfx_mig_blocker;
@ -690,22 +691,23 @@ static PGDisplayDescriptor *apple_gfx_prepare_display_descriptor(AppleGFXState *
return disp_desc;
}
static NSArray<PGDisplayMode*>* apple_gfx_prepare_display_mode_array(void)
static NSArray<PGDisplayMode *> *apple_gfx_create_display_mode_array(
const AppleGFXDisplayMode display_modes[], uint32_t display_mode_count)
{
PGDisplayMode *modes[ARRAY_SIZE(apple_gfx_modes)];
NSArray<PGDisplayMode*>* mode_array;
int i;
PGDisplayMode *mode_obj;
NSMutableArray<PGDisplayMode *> *mode_array =
[[NSMutableArray alloc] initWithCapacity:display_mode_count];
for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
modes[i] =
[[PGDisplayMode alloc] initWithSizeInPixels:apple_gfx_modes[i] refreshRateInHz:60.];
}
for (unsigned i = 0; i < display_mode_count; i++) {
const AppleGFXDisplayMode *mode = &display_modes[i];
trace_apple_gfx_display_mode(i, mode->width_px, mode->height_px);
PGDisplayCoord_t mode_size = { mode->width_px, mode->height_px };
mode_array = [NSArray arrayWithObjects:modes count:ARRAY_SIZE(apple_gfx_modes)];
for (i = 0; i < ARRAY_SIZE(apple_gfx_modes); i++) {
[modes[i] release];
modes[i] = nil;
mode_obj =
[[PGDisplayMode alloc] initWithSizeInPixels:mode_size
refreshRateInHz:mode->refresh_rate_hz];
[mode_array addObject:mode_obj];
[mode_obj release];
}
return mode_array;
@ -741,6 +743,9 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
PGDeviceDescriptor *desc, Error **errp)
{
PGDisplayDescriptor *disp_desc;
const AppleGFXDisplayMode *display_modes = apple_gfx_default_modes;
uint32_t num_display_modes = ARRAY_SIZE(apple_gfx_default_modes);
NSArray<PGDisplayMode *> *mode_array;
if (apple_gfx_mig_blocker == NULL) {
error_setg(&apple_gfx_mig_blocker,
@ -776,8 +781,99 @@ bool apple_gfx_common_realize(AppleGFXState *s, DeviceState *dev,
port:0
serialNum:next_pgdisplay_serial_num++];
[disp_desc release];
s->pgdisp.modeList = apple_gfx_prepare_display_mode_array();
if (s->display_modes != NULL && s->num_display_modes > 0) {
trace_apple_gfx_common_realize_modes_property(s->num_display_modes);
display_modes = s->display_modes;
num_display_modes = s->num_display_modes;
}
s->pgdisp.modeList = mode_array =
apple_gfx_create_display_mode_array(display_modes, num_display_modes);
[mode_array release];
s->con = graphic_console_init(dev, 0, &apple_gfx_fb_ops, s);
return true;
}
/* ------ Display mode list device property ------ */
static void apple_gfx_get_display_mode(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
Property *prop = opaque;
AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
/* 3 uint16s (max 5 digits) + 2 separator characters + nul. */
char buffer[5 * 3 + 2 + 1];
char *pos = buffer;
int rc = snprintf(buffer, sizeof(buffer),
"%"PRIu16"x%"PRIu16"@%"PRIu16,
mode->width_px, mode->height_px,
mode->refresh_rate_hz);
assert(rc < sizeof(buffer));
visit_type_str(v, name, &pos, errp);
}
static void apple_gfx_set_display_mode(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
Property *prop = opaque;
AppleGFXDisplayMode *mode = object_field_prop_ptr(obj, prop);
const char *endptr;
g_autofree char *str = NULL;
int ret;
int val;
if (!visit_type_str(v, name, &str, errp)) {
return;
}
endptr = str;
ret = qemu_strtoi(endptr, &endptr, 10, &val);
if (ret || val > UINT16_MAX || val <= 0) {
error_setg(errp, "width in '%s' must be a decimal integer number"
" of pixels in the range 1..65535", name);
return;
}
mode->width_px = val;
if (*endptr != 'x') {
goto separator_error;
}
ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
if (ret || val > UINT16_MAX || val <= 0) {
error_setg(errp, "height in '%s' must be a decimal integer number"
" of pixels in the range 1..65535", name);
return;
}
mode->height_px = val;
if (*endptr != '@') {
goto separator_error;
}
ret = qemu_strtoi(endptr + 1, &endptr, 10, &val);
if (ret || val > UINT16_MAX || val <= 0) {
error_setg(errp, "refresh rate in '%s'"
" must be a positive decimal integer (Hertz)", name);
return;
}
mode->refresh_rate_hz = val;
return;
separator_error:
error_setg(errp,
"Each display mode takes the format '<width>x<height>@<rate>'");
}
const PropertyInfo qdev_prop_apple_gfx_display_mode = {
.name = "display_mode",
.description =
"Display mode in pixels and Hertz, as <width>x<height>@<refresh-rate> "
"Example: 3840x2160@60",
.get = apple_gfx_get_display_mode,
.set = apple_gfx_set_display_mode,
};

View file

@ -212,6 +212,8 @@ apple_gfx_cursor_set(uint32_t bpp, uint64_t width, uint64_t height) "bpp=%d widt
apple_gfx_cursor_show(uint32_t show) "show=%d"
apple_gfx_cursor_move(void) ""
apple_gfx_common_init(const char *device_name, size_t mmio_size) "device: %s; MMIO size: %zu bytes"
apple_gfx_common_realize_modes_property(uint32_t num_modes) "using %u modes supplied by 'display-modes' device property"
apple_gfx_display_mode(uint32_t mode_idx, uint16_t width_px, uint16_t height_px) "mode %2"PRIu32": %4"PRIu16"x%4"PRIu16
# apple-gfx-mmio.m
apple_gfx_mmio_iosfc_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64