Contributors: 2
Author Tokens Token Proportion Commits Commit Proportion
Shenghao Ding 985 99.60% 4 80.00%
Gergo Koteles 4 0.40% 1 20.00%
Total 989 5


// SPDX-License-Identifier: GPL-2.0
//
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
//
// Copyright 2023 - 2025 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>

#include <linux/crc8.h>
#include <linux/dev_printk.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/tas2781.h>

int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
	unsigned short chn, unsigned int reg, unsigned int *val)
{
	int ret = 0;

	if (chn < tas_priv->ndev) {
		struct regmap *map = tas_priv->regmap;

		ret = tas_priv->change_chn_book(tas_priv, chn,
			TASDEVICE_BOOK_ID(reg));
		if (ret < 0)
			goto out;

		ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
		if (ret < 0)
			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
	} else {
		ret = -EINVAL;
		dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
			chn);
	}

out:
	return ret;
}
EXPORT_SYMBOL_GPL(tasdevice_dev_read);

int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
	unsigned short chn, unsigned int reg, unsigned char *data,
	unsigned int len)
{
	int ret = 0;

	if (chn < tas_priv->ndev) {
		struct regmap *map = tas_priv->regmap;

		ret = tas_priv->change_chn_book(tas_priv, chn,
			TASDEVICE_BOOK_ID(reg));
		if (ret < 0)
			goto out;

		ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
		if (ret < 0)
			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
	} else
		dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
			chn);

out:
	return ret;
}
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);

int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
	unsigned short chn, unsigned int reg, unsigned int value)
{
	int ret = 0;

	if (chn < tas_priv->ndev) {
		struct regmap *map = tas_priv->regmap;

		ret = tas_priv->change_chn_book(tas_priv, chn,
			TASDEVICE_BOOK_ID(reg));
		if (ret < 0)
			goto out;

		ret = regmap_write(map, TASDEVICE_PGRG(reg),
			value);
		if (ret < 0)
			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
	} else {
		ret = -EINVAL;
		dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
			chn);
	}

out:
	return ret;
}
EXPORT_SYMBOL_GPL(tasdevice_dev_write);

int tasdevice_dev_bulk_write(
	struct tasdevice_priv *tas_priv, unsigned short chn,
	unsigned int reg, unsigned char *data,
	unsigned int len)
{
	int ret = 0;

	if (chn < tas_priv->ndev) {
		struct regmap *map = tas_priv->regmap;

		ret = tas_priv->change_chn_book(tas_priv, chn,
			TASDEVICE_BOOK_ID(reg));
		if (ret < 0)
			goto out;

		ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg),
			data, len);
		if (ret < 0)
			dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
	} else {
		ret = -EINVAL;
		dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
			chn);
	}

out:
	return ret;
}
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);

static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
{
	struct tasdevice_data *tas_dt;
	struct tasdev_blk *blk;
	unsigned int i;

	if (!prog)
		return;

	tas_dt = &(prog->dev_data);

	if (!tas_dt->dev_blks)
		return;

	for (i = 0; i < tas_dt->nr_blk; i++) {
		blk = &(tas_dt->dev_blks[i]);
		kfree(blk->data);
	}
	kfree(tas_dt->dev_blks);
}

static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog,
	unsigned short nr)
{
	int i;

	for (i = 0; i < nr; i++)
		tasdev_dsp_prog_blk_remove(&prog[i]);
	kfree(prog);
}

static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg)
{
	struct tasdevice_data *tas_dt;
	struct tasdev_blk *blk;
	unsigned int i;

	if (cfg) {
		tas_dt = &(cfg->dev_data);

		if (!tas_dt->dev_blks)
			return;

		for (i = 0; i < tas_dt->nr_blk; i++) {
			blk = &(tas_dt->dev_blks[i]);
			kfree(blk->data);
		}
		kfree(tas_dt->dev_blks);
	}
}

static void tasdev_dsp_cfg_remove(struct tasdevice_config *config,
	unsigned short nr)
{
	int i;

	for (i = 0; i < nr; i++)
		tasdev_dsp_cfg_blk_remove(&config[i]);
	kfree(config);
}

void tasdevice_dsp_remove(void *context)
{
	struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
	struct tasdevice_fw *tas_fmw = tas_dev->fmw;

	if (!tas_dev->fmw)
		return;

	if (tas_fmw->programs)
		tasdev_dsp_prog_remove(tas_fmw->programs,
			tas_fmw->nr_programs);
	if (tas_fmw->configs)
		tasdev_dsp_cfg_remove(tas_fmw->configs,
			tas_fmw->nr_configurations);
	kfree(tas_fmw);
	tas_dev->fmw = NULL;
}
EXPORT_SYMBOL_GPL(tasdevice_dsp_remove);

void tasdevice_remove(struct tasdevice_priv *tas_priv)
{
	mutex_destroy(&tas_priv->codec_lock);
}
EXPORT_SYMBOL_GPL(tasdevice_remove);

MODULE_DESCRIPTION("TAS2781 common library");
MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
MODULE_LICENSE("GPL");