18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * An example software sink buffer for Intel TH MSU.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Intel Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/intel_th.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define MAX_SGTS 16
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct msu_sink_private {
178c2ecf20Sopenharmony_ci	struct device	*dev;
188c2ecf20Sopenharmony_ci	struct sg_table **sgts;
198c2ecf20Sopenharmony_ci	unsigned int	nr_sgts;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic void *msu_sink_assign(struct device *dev, int *mode)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct msu_sink_private *priv;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
278c2ecf20Sopenharmony_ci	if (!priv)
288c2ecf20Sopenharmony_ci		return NULL;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	priv->sgts = kcalloc(MAX_SGTS, sizeof(void *), GFP_KERNEL);
318c2ecf20Sopenharmony_ci	if (!priv->sgts) {
328c2ecf20Sopenharmony_ci		kfree(priv);
338c2ecf20Sopenharmony_ci		return NULL;
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	priv->dev = dev;
378c2ecf20Sopenharmony_ci	*mode = MSC_MODE_MULTI;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	return priv;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void msu_sink_unassign(void *data)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct msu_sink_private *priv = data;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	kfree(priv->sgts);
478c2ecf20Sopenharmony_ci	kfree(priv);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* See also: msc.c: __msc_buffer_win_alloc() */
518c2ecf20Sopenharmony_cistatic int msu_sink_alloc_window(void *data, struct sg_table **sgt, size_t size)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct msu_sink_private *priv = data;
548c2ecf20Sopenharmony_ci	unsigned int nents;
558c2ecf20Sopenharmony_ci	struct scatterlist *sg_ptr;
568c2ecf20Sopenharmony_ci	void *block;
578c2ecf20Sopenharmony_ci	int ret, i;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (priv->nr_sgts == MAX_SGTS)
608c2ecf20Sopenharmony_ci		return -ENOMEM;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	nents = DIV_ROUND_UP(size, PAGE_SIZE);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = sg_alloc_table(*sgt, nents, GFP_KERNEL);
658c2ecf20Sopenharmony_ci	if (ret)
668c2ecf20Sopenharmony_ci		return -ENOMEM;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	priv->sgts[priv->nr_sgts++] = *sgt;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	for_each_sg((*sgt)->sgl, sg_ptr, nents, i) {
718c2ecf20Sopenharmony_ci		block = dma_alloc_coherent(priv->dev->parent->parent,
728c2ecf20Sopenharmony_ci					   PAGE_SIZE, &sg_dma_address(sg_ptr),
738c2ecf20Sopenharmony_ci					   GFP_KERNEL);
748c2ecf20Sopenharmony_ci		if (!block)
758c2ecf20Sopenharmony_ci			return -ENOMEM;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		sg_set_buf(sg_ptr, block, PAGE_SIZE);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return nents;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* See also: msc.c: __msc_buffer_win_free() */
848c2ecf20Sopenharmony_cistatic void msu_sink_free_window(void *data, struct sg_table *sgt)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct msu_sink_private *priv = data;
878c2ecf20Sopenharmony_ci	struct scatterlist *sg_ptr;
888c2ecf20Sopenharmony_ci	int i;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	for_each_sg(sgt->sgl, sg_ptr, sgt->nents, i) {
918c2ecf20Sopenharmony_ci		dma_free_coherent(priv->dev->parent->parent, PAGE_SIZE,
928c2ecf20Sopenharmony_ci				  sg_virt(sg_ptr), sg_dma_address(sg_ptr));
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	sg_free_table(sgt);
968c2ecf20Sopenharmony_ci	priv->nr_sgts--;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int msu_sink_ready(void *data, struct sg_table *sgt, size_t bytes)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct msu_sink_private *priv = data;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	intel_th_msc_window_unlock(priv->dev, sgt);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic const struct msu_buffer sink_mbuf = {
1098c2ecf20Sopenharmony_ci	.name		= "sink",
1108c2ecf20Sopenharmony_ci	.assign		= msu_sink_assign,
1118c2ecf20Sopenharmony_ci	.unassign	= msu_sink_unassign,
1128c2ecf20Sopenharmony_ci	.alloc_window	= msu_sink_alloc_window,
1138c2ecf20Sopenharmony_ci	.free_window	= msu_sink_free_window,
1148c2ecf20Sopenharmony_ci	.ready		= msu_sink_ready,
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cimodule_intel_th_msu_buffer(sink_mbuf);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
120