mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

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>
185 lines
5.2 KiB
C
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));
|
|
}
|