150472Speter// SPDX-License-Identifier: GPL-2.0
21664Sphk/*
369040Sben * An example software sink buffer for Intel TH MSU.
469040Sben *
569040Sben * Copyright (C) 2019 Intel Corporation.
669040Sben */
782604Salex
882604Salex#include <linux/intel_th.h>
982604Salex#include <linux/module.h>
1082604Salex#include <linux/slab.h>
111664Sphk#include <linux/device.h>
123023Srgrimes#include <linux/dma-mapping.h>
133023Srgrimes
1482604Salex#define MAX_SGTS 16
1582604Salex
1682604Salexstruct msu_sink_private {
171664Sphk	struct device	*dev;
1872679Skris	struct sg_table **sgts;
1999260Sjohan	unsigned int	nr_sgts;
2072878Skris};
2172878Skris
2272878Skrisstatic void *msu_sink_assign(struct device *dev, int *mode)
2372878Skris{
2472878Skris	struct msu_sink_private *priv;
2599260Sjohan
2673145Skris	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
2773145Skris	if (!priv)
2873145Skris		return NULL;
2972878Skris
3072878Skris	priv->sgts = kcalloc(MAX_SGTS, sizeof(void *), GFP_KERNEL);
3172679Skris	if (!priv->sgts) {
32101232Sru		kfree(priv);
33101232Sru		return NULL;
34101232Sru	}
3572878Skris
3674146Skris	priv->dev = dev;
3772878Skris	*mode = MSC_MODE_MULTI;
3858648Skris
3968917Sdougb	return priv;
4058648Skris}
4168917Sdougb
4258648Skrisstatic void msu_sink_unassign(void *data)
4358648Skris{
4458648Skris	struct msu_sink_private *priv = data;
451664Sphk
4629281Sjkh	kfree(priv->sgts);
471664Sphk	kfree(priv);
4859006Sobrien}
4959006Sobrien
5059006Sobrien/* See also: msc.c: __msc_buffer_win_alloc() */
5159006Sobrienstatic int msu_sink_alloc_window(void *data, struct sg_table **sgt, size_t size)
521664Sphk{
5359006Sobrien	struct msu_sink_private *priv = data;
5459006Sobrien	unsigned int nents;
5562136Sobrien	struct scatterlist *sg_ptr;
5662136Sobrien	void *block;
5780452Speter	int ret, i;
5899260Sjohan
5962136Sobrien	if (priv->nr_sgts == MAX_SGTS)
6082604Salex		return -ENOMEM;
6182604Salex
6282604Salex	nents = DIV_ROUND_UP(size, PAGE_SIZE);
6382604Salex
6462136Sobrien	ret = sg_alloc_table(*sgt, nents, GFP_KERNEL);
6568917Sdougb	if (ret)
6668263Sobrien		return -ENOMEM;
6768263Sobrien
6868263Sobrien	priv->sgts[priv->nr_sgts++] = *sgt;
6965380Sobrien
7065380Sobrien	for_each_sg((*sgt)->sgl, sg_ptr, nents, i) {
7165380Sobrien		block = dma_alloc_coherent(priv->dev->parent->parent,
7281749Sobrien					   PAGE_SIZE, &sg_dma_address(sg_ptr),
7381749Sobrien					   GFP_KERNEL);
7481749Sobrien		if (!block)
7581749Sobrien			return -ENOMEM;
7681749Sobrien
7781749Sobrien		sg_set_buf(sg_ptr, block, PAGE_SIZE);
7842325Sobrien	}
79100870Sru
8035222Sache	return nents;
8165884Sache}
8265957Sache
8365884Sache/* See also: msc.c: __msc_buffer_win_free() */
8464803Sbrianstatic void msu_sink_free_window(void *data, struct sg_table *sgt)
8564803Sbrian{
8664803Sbrian	struct msu_sink_private *priv = data;
8768705Sgreen	struct scatterlist *sg_ptr;
8868705Sgreen	int i;
8968705Sgreen
9097387Stjr	for_each_sg(sgt->sgl, sg_ptr, sgt->nents, i) {
9197387Stjr		dma_free_coherent(priv->dev->parent->parent, PAGE_SIZE,
9297387Stjr				  sg_virt(sg_ptr), sg_dma_address(sg_ptr));
9397387Stjr	}
9451299Speter
9557542Skris	sg_free_table(sgt);
9690522Sobrien	priv->nr_sgts--;
9759124Sasmodai}
9857542Skris
9990522Sobrienstatic int msu_sink_ready(void *data, struct sg_table *sgt, size_t bytes)
10077041Sru{
10192868Sru	struct msu_sink_private *priv = data;
10261139Shoek
10358859Ssheldonh	intel_th_msc_window_unlock(priv->dev, sgt);
10459884Schuckr
10557764Skris	return 0;
10657542Skris}
10757542Skris
10898064Sdougbstatic const struct msu_buffer sink_mbuf = {
10957542Skris	.name		= "sink",
11058418Sobrien	.assign		= msu_sink_assign,
11159338Sobrien	.unassign	= msu_sink_unassign,
11258280Skris	.alloc_window	= msu_sink_alloc_window,
11357553Skris	.free_window	= msu_sink_free_window,
11457542Skris	.ready		= msu_sink_ready,
11557542Skris};
11657542Skris
11765381Sobrienmodule_intel_th_msu_buffer(sink_mbuf);
11857553Skris
11957542SkrisMODULE_LICENSE("GPL v2");
12035206Sphk