linux/drivers/gpu/drm/vkms/vkms_writeback.c
Paz Zcharya 8cca475b80
drm/vkms: Add support for ABGR8888 pixel format
Add support for pixel format ABGR8888, which is the default format
on Android devices. This will allow us to use VKMS as the default
display driver in Android Emulator (Cuttlefish) and increase VKMS
adoption.

Signed-off-by: Paz Zcharya <pazz@chromium.org>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250129142238.562999-1-pazz@google.com
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
2025-02-04 14:44:13 +01:00

185 lines
5.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
#include <linux/iosys-map.h>
#include <drm/drm_atomic.h>
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_writeback.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include "vkms_drv.h"
#include "vkms_formats.h"
static const u32 vkms_wb_formats[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XRGB16161616,
DRM_FORMAT_ARGB16161616,
DRM_FORMAT_RGB565
};
static const struct drm_connector_funcs vkms_wb_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int vkms_wb_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector);
struct drm_crtc_state *crtc_state;
struct drm_framebuffer *fb;
const struct drm_display_mode *mode;
int ret;
if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
return 0;
if (!conn_state->crtc)
return 0;
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
mode = &crtc_state->mode;
fb = conn_state->writeback_job->fb;
if (fb->width != mode->hdisplay || fb->height != mode->vdisplay) {
DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
fb->width, fb->height);
return -EINVAL;
}
ret = drm_atomic_helper_check_wb_connector_state(connector, state);
if (ret < 0)
return ret;
return 0;
}
static int vkms_wb_connector_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
return drm_add_modes_noedid(connector, dev->mode_config.max_width,
dev->mode_config.max_height);
}
static int vkms_wb_prepare_job(struct drm_writeback_connector *wb_connector,
struct drm_writeback_job *job)
{
struct vkms_writeback_job *vkmsjob;
int ret;
if (!job->fb)
return 0;
vkmsjob = kzalloc(sizeof(*vkmsjob), GFP_KERNEL);
if (!vkmsjob)
return -ENOMEM;
ret = drm_gem_fb_vmap(job->fb, vkmsjob->wb_frame_info.map, vkmsjob->data);
if (ret) {
DRM_ERROR("vmap failed: %d\n", ret);
goto err_kfree;
}
vkmsjob->wb_frame_info.fb = job->fb;
drm_framebuffer_get(vkmsjob->wb_frame_info.fb);
job->priv = vkmsjob;
return 0;
err_kfree:
kfree(vkmsjob);
return ret;
}
static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
struct drm_writeback_job *job)
{
struct vkms_writeback_job *vkmsjob = job->priv;
struct vkms_output *vkms_output = container_of(connector,
struct vkms_output,
wb_connector);
if (!job->fb)
return;
drm_gem_fb_vunmap(job->fb, vkmsjob->wb_frame_info.map);
drm_framebuffer_put(vkmsjob->wb_frame_info.fb);
vkms_set_composer(vkms_output, false);
kfree(vkmsjob);
}
static void vkms_wb_atomic_commit(struct drm_connector *conn,
struct drm_atomic_state *state)
{
struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
conn);
struct vkms_output *output = drm_crtc_to_vkms_output(connector_state->crtc);
struct drm_writeback_connector *wb_conn = &output->wb_connector;
struct drm_connector_state *conn_state = wb_conn->base.state;
struct vkms_crtc_state *crtc_state = output->composer_state;
struct drm_framebuffer *fb = connector_state->writeback_job->fb;
u16 crtc_height = crtc_state->base.mode.vdisplay;
u16 crtc_width = crtc_state->base.mode.hdisplay;
struct vkms_writeback_job *active_wb;
struct vkms_frame_info *wb_frame_info;
u32 wb_format = fb->format->format;
if (!conn_state)
return;
vkms_set_composer(output, true);
active_wb = conn_state->writeback_job->priv;
wb_frame_info = &active_wb->wb_frame_info;
spin_lock_irq(&output->composer_lock);
crtc_state->active_writeback = active_wb;
crtc_state->wb_pending = true;
spin_unlock_irq(&output->composer_lock);
drm_writeback_queue_job(wb_conn, connector_state);
active_wb->pixel_write = get_pixel_write_function(wb_format);
drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height);
drm_rect_init(&wb_frame_info->dst, 0, 0, crtc_width, crtc_height);
}
static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
.get_modes = vkms_wb_connector_get_modes,
.prepare_writeback_job = vkms_wb_prepare_job,
.cleanup_writeback_job = vkms_wb_cleanup_job,
.atomic_commit = vkms_wb_atomic_commit,
.atomic_check = vkms_wb_atomic_check,
};
int vkms_enable_writeback_connector(struct vkms_device *vkmsdev,
struct vkms_output *vkms_output)
{
struct drm_writeback_connector *wb = &vkms_output->wb_connector;
int ret;
ret = drmm_encoder_init(&vkmsdev->drm, &vkms_output->wb_encoder,
NULL, DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret)
return ret;
vkms_output->wb_encoder.possible_crtcs |= drm_crtc_mask(&vkms_output->crtc);
drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);
return drmm_writeback_connector_init(&vkmsdev->drm, wb,
&vkms_wb_connector_funcs,
&vkms_output->wb_encoder,
vkms_wb_formats,
ARRAY_SIZE(vkms_wb_formats));
}