aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vkms/vkms_crc.c
blob: 68421d3d809ac7dcaaeefa94c8e486e8f82e9773 (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
// SPDX-License-Identifier: GPL-2.0
#include "vkms_drv.h"
#include <linux/crc32.h>
#include <drm/drm_gem_framebuffer_helper.h>

static uint32_t _vkms_get_crc(struct vkms_crc_data *crc_data)
{
	struct drm_framebuffer *fb = &crc_data->fb;
	struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0);
	struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(gem_obj);
	u32 crc = 0;
	int i = 0;
	unsigned int x = crc_data->src.x1 >> 16;
	unsigned int y = crc_data->src.y1 >> 16;
	unsigned int height = drm_rect_height(&crc_data->src) >> 16;
	unsigned int width = drm_rect_width(&crc_data->src) >> 16;
	unsigned int cpp = fb->format->cpp[0];
	unsigned int src_offset;
	unsigned int size_byte = width * cpp;
	void *vaddr;

	mutex_lock(&vkms_obj->pages_lock);
	vaddr = vkms_obj->vaddr;
	if (WARN_ON(!vaddr))
		goto out;

	for (i = y; i < y + height; i++) {
		src_offset = fb->offsets[0] + (i * fb->pitches[0]) + (x * cpp);
		crc = crc32_le(crc, vaddr + src_offset, size_byte);
	}

out:
	mutex_unlock(&vkms_obj->pages_lock);
	return crc;
}

void vkms_crc_work_handle(struct work_struct *work)
{
	struct vkms_crtc_state *crtc_state = container_of(work,
						struct vkms_crtc_state,
						crc_work);
	struct drm_crtc *crtc = crtc_state->base.crtc;
	struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
	struct vkms_device *vdev = container_of(out, struct vkms_device,
						output);
	struct vkms_crc_data *primary_crc = NULL;
	struct drm_plane *plane;

	u32 crc32 = 0;

	drm_for_each_plane(plane, &vdev->drm) {
		struct vkms_plane_state *vplane_state;
		struct vkms_crc_data *crc_data;

		vplane_state = to_vkms_plane_state(plane->state);
		crc_data = vplane_state->crc_data;

		if (drm_framebuffer_read_refcount(&crc_data->fb) == 0)
			continue;

		if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
			primary_crc = crc_data;
			break;
		}
	}

	if (primary_crc)
		crc32 = _vkms_get_crc(primary_crc);

	drm_crtc_add_crc_entry(crtc, true, crtc_state->n_frame, &crc32);
}

static int vkms_crc_parse_source(const char *src_name, bool *enabled)
{
	int ret = 0;

	if (!src_name) {
		*enabled = false;
	} else if (strcmp(src_name, "auto") == 0) {
		*enabled = true;
	} else {
		*enabled = false;
		ret = -EINVAL;
	}

	return ret;
}

int vkms_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
			   size_t *values_cnt)
{
	bool enabled;

	if (vkms_crc_parse_source(src_name, &enabled) < 0) {
		DRM_DEBUG_DRIVER("unknown source %s\n", src_name);
		return -EINVAL;
	}

	*values_cnt = 1;

	return 0;
}

int vkms_set_crc_source(struct drm_crtc *crtc, const char *src_name,
			size_t *values_cnt)
{
	struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
	bool enabled = false;
	unsigned long flags;
	int ret = 0;

	ret = vkms_crc_parse_source(src_name, &enabled);

	*values_cnt = 1;

	/* make sure nothing is scheduled on crtc workq */
	flush_workqueue(out->crc_workq);

	spin_lock_irqsave(&out->lock, flags);
	out->crc_enabled = enabled;
	spin_unlock_irqrestore(&out->lock, flags);

	return ret;
}