aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vkms/vkms_writeback.c
blob: 67f80ab1e85f86392f5da0e57260a5d2dcb57e34 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// SPDX-License-Identifier: GPL-2.0+

#include <linux/dma-buf-map.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"

static const u32 vkms_wb_formats[] = {
	DRM_FORMAT_XRGB8888,
};

static const struct drm_connector_funcs vkms_wb_connector_funcs = {
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = drm_connector_cleanup,
	.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_encoder_atomic_check(struct drm_encoder *encoder,
					struct drm_crtc_state *crtc_state,
					struct drm_connector_state *conn_state)
{
	struct drm_framebuffer *fb;
	const struct drm_display_mode *mode = &crtc_state->mode;

	if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
		return 0;

	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;
	}

	if (fb->format->format != vkms_wb_formats[0]) {
		struct drm_format_name_buf format_name;

		DRM_DEBUG_KMS("Invalid pixel format %s\n",
			      drm_get_format_name(fb->format->format,
						  &format_name));
		return -EINVAL;
	}

	return 0;
}

static const struct drm_encoder_helper_funcs vkms_wb_encoder_helper_funcs = {
	.atomic_check = vkms_wb_encoder_atomic_check,
};

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 drm_gem_object *gem_obj;
	struct dma_buf_map map;
	int ret;

	if (!job->fb)
		return 0;

	gem_obj = drm_gem_fb_get_obj(job->fb, 0);
	ret = drm_gem_shmem_vmap(gem_obj, &map);
	if (ret) {
		DRM_ERROR("vmap failed: %d\n", ret);
		return ret;
	}

	job->priv = map.vaddr;

	return 0;
}

static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
				struct drm_writeback_job *job)
{
	struct drm_gem_object *gem_obj;
	struct vkms_device *vkmsdev;
	struct dma_buf_map map;

	if (!job->fb)
		return;

	gem_obj = drm_gem_fb_get_obj(job->fb, 0);
	dma_buf_map_set_vaddr(&map, job->priv);
	drm_gem_shmem_vunmap(gem_obj, &map);

	vkmsdev = drm_device_to_vkms_device(gem_obj->dev);
	vkms_set_composer(&vkmsdev->output, false);
}

static void vkms_wb_atomic_commit(struct drm_connector *conn,
				  struct drm_connector_state *state)
{
	struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
	struct vkms_output *output = &vkmsdev->output;
	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;

	if (!conn_state)
		return;

	vkms_set_composer(&vkmsdev->output, true);

	spin_lock_irq(&output->composer_lock);
	crtc_state->active_writeback = conn_state->writeback_job->priv;
	crtc_state->wb_pending = true;
	spin_unlock_irq(&output->composer_lock);
	drm_writeback_queue_job(wb_conn, state);
}

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,
};

int vkms_enable_writeback_connector(struct vkms_device *vkmsdev)
{
	struct drm_writeback_connector *wb = &vkmsdev->output.wb_connector;

	vkmsdev->output.wb_connector.encoder.possible_crtcs = 1;
	drm_connector_helper_add(&wb->base, &vkms_wb_conn_helper_funcs);

	return drm_writeback_connector_init(&vkmsdev->drm, wb,
					    &vkms_wb_connector_funcs,
					    &vkms_wb_encoder_helper_funcs,
					    vkms_wb_formats,
					    ARRAY_SIZE(vkms_wb_formats));
}