root/sound/soc/codecs/tas2781-comlib.c
// 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");