18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2008 Intel Corporation
38c2ecf20Sopenharmony_ci * Copyright © 2016 Collabora Ltd
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
78c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
88c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
98c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
108c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
138c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
148c2ecf20Sopenharmony_ci * Software.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
198c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
208c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
218c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
228c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Based on code from the i915 driver.
258c2ecf20Sopenharmony_ci * Original author: Damien Lespiau <damien.lespiau@intel.com>
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/circ_buf.h>
308c2ecf20Sopenharmony_ci#include <linux/ctype.h>
318c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
328c2ecf20Sopenharmony_ci#include <linux/poll.h>
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
368c2ecf20Sopenharmony_ci#include <drm/drm_debugfs_crc.h>
378c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
388c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "drm_internal.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci * DOC: CRC ABI
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci * DRM device drivers can provide to userspace CRC information of each frame as
468c2ecf20Sopenharmony_ci * it reached a given hardware component (a CRC sampling "source").
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * Userspace can control generation of CRCs in a given CRTC by writing to the
498c2ecf20Sopenharmony_ci * file dri/0/crtc-N/crc/control in debugfs, with N being the index of the CRTC.
508c2ecf20Sopenharmony_ci * Accepted values are source names (which are driver-specific) and the "auto"
518c2ecf20Sopenharmony_ci * keyword, which will let the driver select a default source of frame CRCs
528c2ecf20Sopenharmony_ci * for this CRTC.
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Once frame CRC generation is enabled, userspace can capture them by reading
558c2ecf20Sopenharmony_ci * the dri/0/crtc-N/crc/data file. Each line in that file contains the frame
568c2ecf20Sopenharmony_ci * number in the first field and then a number of unsigned integer fields
578c2ecf20Sopenharmony_ci * containing the CRC data. Fields are separated by a single space and the number
588c2ecf20Sopenharmony_ci * of CRC fields is source-specific.
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * Note that though in some cases the CRC is computed in a specified way and on
618c2ecf20Sopenharmony_ci * the frame contents as supplied by userspace (eDP 1.3), in general the CRC
628c2ecf20Sopenharmony_ci * computation is performed in an unspecified way and on frame contents that have
638c2ecf20Sopenharmony_ci * been already processed in also an unspecified way and thus userspace cannot
648c2ecf20Sopenharmony_ci * rely on being able to generate matching CRC values for the frame contents that
658c2ecf20Sopenharmony_ci * it submits. In this general case, the maximum userspace can do is to compare
668c2ecf20Sopenharmony_ci * the reported CRCs of frames that should have the same contents.
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * On the driver side the implementation effort is minimal, drivers only need to
698c2ecf20Sopenharmony_ci * implement &drm_crtc_funcs.set_crc_source and &drm_crtc_funcs.verify_crc_source.
708c2ecf20Sopenharmony_ci * The debugfs files are automatically set up if those vfuncs are set. CRC samples
718c2ecf20Sopenharmony_ci * need to be captured in the driver by calling drm_crtc_add_crc_entry().
728c2ecf20Sopenharmony_ci * Depending on the driver and HW requirements, &drm_crtc_funcs.set_crc_source
738c2ecf20Sopenharmony_ci * may result in a commit (even a full modeset).
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * CRC results must be reliable across non-full-modeset atomic commits, so if a
768c2ecf20Sopenharmony_ci * commit via DRM_IOCTL_MODE_ATOMIC would disable or otherwise interfere with
778c2ecf20Sopenharmony_ci * CRC generation, then the driver must mark that commit as a full modeset
788c2ecf20Sopenharmony_ci * (drm_atomic_crtc_needs_modeset() should return true). As a result, to ensure
798c2ecf20Sopenharmony_ci * consistent results, generic userspace must re-setup CRC generation after a
808c2ecf20Sopenharmony_ci * legacy SETCRTC or an atomic commit with DRM_MODE_ATOMIC_ALLOW_MODESET.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int crc_control_show(struct seq_file *m, void *data)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = m->private;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (crtc->funcs->get_crc_sources) {
888c2ecf20Sopenharmony_ci		size_t count;
898c2ecf20Sopenharmony_ci		const char *const *sources = crtc->funcs->get_crc_sources(crtc,
908c2ecf20Sopenharmony_ci									&count);
918c2ecf20Sopenharmony_ci		size_t values_cnt;
928c2ecf20Sopenharmony_ci		int i;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		if (count == 0 || !sources)
958c2ecf20Sopenharmony_ci			goto out;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		for (i = 0; i < count; i++)
988c2ecf20Sopenharmony_ci			if (!crtc->funcs->verify_crc_source(crtc, sources[i],
998c2ecf20Sopenharmony_ci							    &values_cnt)) {
1008c2ecf20Sopenharmony_ci				if (strcmp(sources[i], crtc->crc.source))
1018c2ecf20Sopenharmony_ci					seq_printf(m, "%s\n", sources[i]);
1028c2ecf20Sopenharmony_ci				else
1038c2ecf20Sopenharmony_ci					seq_printf(m, "%s*\n", sources[i]);
1048c2ecf20Sopenharmony_ci			}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciout:
1098c2ecf20Sopenharmony_ci	seq_printf(m, "%s*\n", crtc->crc.source);
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int crc_control_open(struct inode *inode, struct file *file)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = inode->i_private;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return single_open(file, crc_control_show, crtc);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic ssize_t crc_control_write(struct file *file, const char __user *ubuf,
1218c2ecf20Sopenharmony_ci				 size_t len, loff_t *offp)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct seq_file *m = file->private_data;
1248c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = m->private;
1258c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
1268c2ecf20Sopenharmony_ci	char *source;
1278c2ecf20Sopenharmony_ci	size_t values_cnt;
1288c2ecf20Sopenharmony_ci	int ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (len == 0)
1318c2ecf20Sopenharmony_ci		return 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (len > PAGE_SIZE - 1) {
1348c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("Expected < %lu bytes into crtc crc control\n",
1358c2ecf20Sopenharmony_ci			      PAGE_SIZE);
1368c2ecf20Sopenharmony_ci		return -E2BIG;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	source = memdup_user_nul(ubuf, len);
1408c2ecf20Sopenharmony_ci	if (IS_ERR(source))
1418c2ecf20Sopenharmony_ci		return PTR_ERR(source);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (source[len - 1] == '\n')
1448c2ecf20Sopenharmony_ci		source[len - 1] = '\0';
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ret = crtc->funcs->verify_crc_source(crtc, source, &values_cnt);
1478c2ecf20Sopenharmony_ci	if (ret) {
1488c2ecf20Sopenharmony_ci		kfree(source);
1498c2ecf20Sopenharmony_ci		return ret;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (crc->opened) {
1558c2ecf20Sopenharmony_ci		spin_unlock_irq(&crc->lock);
1568c2ecf20Sopenharmony_ci		kfree(source);
1578c2ecf20Sopenharmony_ci		return -EBUSY;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	kfree(crc->source);
1618c2ecf20Sopenharmony_ci	crc->source = source;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	*offp += len;
1668c2ecf20Sopenharmony_ci	return len;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct file_operations drm_crtc_crc_control_fops = {
1708c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1718c2ecf20Sopenharmony_ci	.open = crc_control_open,
1728c2ecf20Sopenharmony_ci	.read = seq_read,
1738c2ecf20Sopenharmony_ci	.llseek = seq_lseek,
1748c2ecf20Sopenharmony_ci	.release = single_release,
1758c2ecf20Sopenharmony_ci	.write = crc_control_write
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int crtc_crc_data_count(struct drm_crtc_crc *crc)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	assert_spin_locked(&crc->lock);
1818c2ecf20Sopenharmony_ci	return CIRC_CNT(crc->head, crc->tail, DRM_CRC_ENTRIES_NR);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void crtc_crc_cleanup(struct drm_crtc_crc *crc)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	kfree(crc->entries);
1878c2ecf20Sopenharmony_ci	crc->overflow = false;
1888c2ecf20Sopenharmony_ci	crc->entries = NULL;
1898c2ecf20Sopenharmony_ci	crc->head = 0;
1908c2ecf20Sopenharmony_ci	crc->tail = 0;
1918c2ecf20Sopenharmony_ci	crc->values_cnt = 0;
1928c2ecf20Sopenharmony_ci	crc->opened = false;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int crtc_crc_open(struct inode *inode, struct file *filep)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = inode->i_private;
1988c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
1998c2ecf20Sopenharmony_ci	struct drm_crtc_crc_entry *entries = NULL;
2008c2ecf20Sopenharmony_ci	size_t values_cnt;
2018c2ecf20Sopenharmony_ci	int ret = 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (drm_drv_uses_atomic_modeset(crtc->dev)) {
2048c2ecf20Sopenharmony_ci		ret = drm_modeset_lock_single_interruptible(&crtc->mutex);
2058c2ecf20Sopenharmony_ci		if (ret)
2068c2ecf20Sopenharmony_ci			return ret;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci		if (!crtc->state->active)
2098c2ecf20Sopenharmony_ci			ret = -EIO;
2108c2ecf20Sopenharmony_ci		drm_modeset_unlock(&crtc->mutex);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (ret)
2138c2ecf20Sopenharmony_ci			return ret;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	ret = crtc->funcs->verify_crc_source(crtc, crc->source, &values_cnt);
2178c2ecf20Sopenharmony_ci	if (ret)
2188c2ecf20Sopenharmony_ci		return ret;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (WARN_ON(values_cnt > DRM_MAX_CRC_NR))
2218c2ecf20Sopenharmony_ci		return -EINVAL;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (WARN_ON(values_cnt == 0))
2248c2ecf20Sopenharmony_ci		return -EINVAL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	entries = kcalloc(DRM_CRC_ENTRIES_NR, sizeof(*entries), GFP_KERNEL);
2278c2ecf20Sopenharmony_ci	if (!entries)
2288c2ecf20Sopenharmony_ci		return -ENOMEM;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
2318c2ecf20Sopenharmony_ci	if (!crc->opened) {
2328c2ecf20Sopenharmony_ci		crc->opened = true;
2338c2ecf20Sopenharmony_ci		crc->entries = entries;
2348c2ecf20Sopenharmony_ci		crc->values_cnt = values_cnt;
2358c2ecf20Sopenharmony_ci	} else {
2368c2ecf20Sopenharmony_ci		ret = -EBUSY;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (ret) {
2418c2ecf20Sopenharmony_ci		kfree(entries);
2428c2ecf20Sopenharmony_ci		return ret;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ret = crtc->funcs->set_crc_source(crtc, crc->source);
2468c2ecf20Sopenharmony_ci	if (ret)
2478c2ecf20Sopenharmony_ci		goto err;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cierr:
2528c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
2538c2ecf20Sopenharmony_ci	crtc_crc_cleanup(crc);
2548c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
2558c2ecf20Sopenharmony_ci	return ret;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int crtc_crc_release(struct inode *inode, struct file *filep)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = filep->f_inode->i_private;
2618c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* terminate the infinite while loop if 'drm_dp_aux_crc_work' running */
2648c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
2658c2ecf20Sopenharmony_ci	crc->opened = false;
2668c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	crtc->funcs->set_crc_source(crtc, NULL);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
2718c2ecf20Sopenharmony_ci	crtc_crc_cleanup(crc);
2728c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	return 0;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci/*
2788c2ecf20Sopenharmony_ci * 1 frame field of 10 chars plus a number of CRC fields of 10 chars each, space
2798c2ecf20Sopenharmony_ci * separated, with a newline at the end and null-terminated.
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_ci#define LINE_LEN(values_cnt)	(10 + 11 * values_cnt + 1 + 1)
2828c2ecf20Sopenharmony_ci#define MAX_LINE_LEN		(LINE_LEN(DRM_MAX_CRC_NR))
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic ssize_t crtc_crc_read(struct file *filep, char __user *user_buf,
2858c2ecf20Sopenharmony_ci			     size_t count, loff_t *pos)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = filep->f_inode->i_private;
2888c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
2898c2ecf20Sopenharmony_ci	struct drm_crtc_crc_entry *entry;
2908c2ecf20Sopenharmony_ci	char buf[MAX_LINE_LEN];
2918c2ecf20Sopenharmony_ci	int ret, i;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (!crc->source) {
2968c2ecf20Sopenharmony_ci		spin_unlock_irq(&crc->lock);
2978c2ecf20Sopenharmony_ci		return 0;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Nothing to read? */
3018c2ecf20Sopenharmony_ci	while (crtc_crc_data_count(crc) == 0) {
3028c2ecf20Sopenharmony_ci		if (filep->f_flags & O_NONBLOCK) {
3038c2ecf20Sopenharmony_ci			spin_unlock_irq(&crc->lock);
3048c2ecf20Sopenharmony_ci			return -EAGAIN;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		ret = wait_event_interruptible_lock_irq(crc->wq,
3088c2ecf20Sopenharmony_ci							crtc_crc_data_count(crc),
3098c2ecf20Sopenharmony_ci							crc->lock);
3108c2ecf20Sopenharmony_ci		if (ret) {
3118c2ecf20Sopenharmony_ci			spin_unlock_irq(&crc->lock);
3128c2ecf20Sopenharmony_ci			return ret;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* We know we have an entry to be read */
3178c2ecf20Sopenharmony_ci	entry = &crc->entries[crc->tail];
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (count < LINE_LEN(crc->values_cnt)) {
3208c2ecf20Sopenharmony_ci		spin_unlock_irq(&crc->lock);
3218c2ecf20Sopenharmony_ci		return -EINVAL;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	BUILD_BUG_ON_NOT_POWER_OF_2(DRM_CRC_ENTRIES_NR);
3258c2ecf20Sopenharmony_ci	crc->tail = (crc->tail + 1) & (DRM_CRC_ENTRIES_NR - 1);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (entry->has_frame_counter)
3308c2ecf20Sopenharmony_ci		sprintf(buf, "0x%08x", entry->frame);
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		sprintf(buf, "XXXXXXXXXX");
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	for (i = 0; i < crc->values_cnt; i++)
3358c2ecf20Sopenharmony_ci		sprintf(buf + 10 + i * 11, " 0x%08x", entry->crcs[i]);
3368c2ecf20Sopenharmony_ci	sprintf(buf + 10 + crc->values_cnt * 11, "\n");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (copy_to_user(user_buf, buf, LINE_LEN(crc->values_cnt)))
3398c2ecf20Sopenharmony_ci		return -EFAULT;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return LINE_LEN(crc->values_cnt);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic __poll_t crtc_crc_poll(struct file *file, poll_table *wait)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = file->f_inode->i_private;
3478c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
3488c2ecf20Sopenharmony_ci	__poll_t ret = 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	poll_wait(file, &crc->wq, wait);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	spin_lock_irq(&crc->lock);
3538c2ecf20Sopenharmony_ci	if (crc->source && crtc_crc_data_count(crc))
3548c2ecf20Sopenharmony_ci		ret |= EPOLLIN | EPOLLRDNORM;
3558c2ecf20Sopenharmony_ci	spin_unlock_irq(&crc->lock);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return ret;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic const struct file_operations drm_crtc_crc_data_fops = {
3618c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
3628c2ecf20Sopenharmony_ci	.open = crtc_crc_open,
3638c2ecf20Sopenharmony_ci	.read = crtc_crc_read,
3648c2ecf20Sopenharmony_ci	.poll = crtc_crc_poll,
3658c2ecf20Sopenharmony_ci	.release = crtc_crc_release,
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_civoid drm_debugfs_crtc_crc_add(struct drm_crtc *crtc)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct dentry *crc_ent;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!crtc->funcs->set_crc_source || !crtc->funcs->verify_crc_source)
3738c2ecf20Sopenharmony_ci		return;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	crc_ent = debugfs_create_dir("crc", crtc->debugfs_entry);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	debugfs_create_file("control", S_IRUGO | S_IWUSR, crc_ent, crtc,
3788c2ecf20Sopenharmony_ci			    &drm_crtc_crc_control_fops);
3798c2ecf20Sopenharmony_ci	debugfs_create_file("data", S_IRUGO, crc_ent, crtc,
3808c2ecf20Sopenharmony_ci			    &drm_crtc_crc_data_fops);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/**
3848c2ecf20Sopenharmony_ci * drm_crtc_add_crc_entry - Add entry with CRC information for a frame
3858c2ecf20Sopenharmony_ci * @crtc: CRTC to which the frame belongs
3868c2ecf20Sopenharmony_ci * @has_frame: whether this entry has a frame number to go with
3878c2ecf20Sopenharmony_ci * @frame: number of the frame these CRCs are about
3888c2ecf20Sopenharmony_ci * @crcs: array of CRC values, with length matching #drm_crtc_crc.values_cnt
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * For each frame, the driver polls the source of CRCs for new data and calls
3918c2ecf20Sopenharmony_ci * this function to add them to the buffer from where userspace reads.
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_ciint drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame,
3948c2ecf20Sopenharmony_ci			   uint32_t frame, uint32_t *crcs)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct drm_crtc_crc *crc = &crtc->crc;
3978c2ecf20Sopenharmony_ci	struct drm_crtc_crc_entry *entry;
3988c2ecf20Sopenharmony_ci	int head, tail;
3998c2ecf20Sopenharmony_ci	unsigned long flags;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&crc->lock, flags);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Caller may not have noticed yet that userspace has stopped reading */
4048c2ecf20Sopenharmony_ci	if (!crc->entries) {
4058c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&crc->lock, flags);
4068c2ecf20Sopenharmony_ci		return -EINVAL;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	head = crc->head;
4108c2ecf20Sopenharmony_ci	tail = crc->tail;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) {
4138c2ecf20Sopenharmony_ci		bool was_overflow = crc->overflow;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		crc->overflow = true;
4168c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&crc->lock, flags);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		if (!was_overflow)
4198c2ecf20Sopenharmony_ci			DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n");
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		return -ENOBUFS;
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	entry = &crc->entries[head];
4258c2ecf20Sopenharmony_ci	entry->frame = frame;
4268c2ecf20Sopenharmony_ci	entry->has_frame_counter = has_frame;
4278c2ecf20Sopenharmony_ci	memcpy(&entry->crcs, crcs, sizeof(*crcs) * crc->values_cnt);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	head = (head + 1) & (DRM_CRC_ENTRIES_NR - 1);
4308c2ecf20Sopenharmony_ci	crc->head = head;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&crc->lock, flags);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	wake_up_interruptible(&crc->wq);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_crtc_add_crc_entry);
439