2023-06-18 20:28:18 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//
|
2024-07-16 14:41:17 +08:00
|
|
|
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
|
2023-06-18 20:28:18 +08:00
|
|
|
//
|
2025-04-29 19:10:54 +08:00
|
|
|
// Copyright 2023 - 2025 Texas Instruments, Inc.
|
2023-06-18 20:28:18 +08:00
|
|
|
//
|
|
|
|
// Author: Shenghao Ding <shenghao-ding@ti.com>
|
|
|
|
|
|
|
|
#include <linux/crc8.h>
|
2025-04-29 19:10:54 +08:00
|
|
|
#include <linux/dev_printk.h>
|
2023-06-18 20:28:18 +08:00
|
|
|
#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>
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
|
|
|
|
unsigned short chn, unsigned int reg, unsigned int *val)
|
2023-06-18 20:28:18 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (chn < tas_priv->ndev) {
|
|
|
|
struct regmap *map = tas_priv->regmap;
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = tas_priv->change_chn_book(tas_priv, chn,
|
|
|
|
TASDEVICE_BOOK_ID(reg));
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
2023-06-18 20:28:18 +08:00
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
|
|
|
|
if (ret < 0)
|
|
|
|
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
2023-06-18 20:28:18 +08:00
|
|
|
} else {
|
|
|
|
ret = -EINVAL;
|
|
|
|
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
|
|
|
chn);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2025-04-29 19:10:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tasdevice_dev_read);
|
2023-06-18 20:28:18 +08:00
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
|
|
|
unsigned short chn, unsigned int reg, unsigned char *data,
|
|
|
|
unsigned int len)
|
2023-06-18 20:28:18 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (chn < tas_priv->ndev) {
|
|
|
|
struct regmap *map = tas_priv->regmap;
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = tas_priv->change_chn_book(tas_priv, chn,
|
2023-06-18 20:28:18 +08:00
|
|
|
TASDEVICE_BOOK_ID(reg));
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
|
2023-06-18 20:28:18 +08:00
|
|
|
if (ret < 0)
|
|
|
|
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
|
2025-04-29 19:10:54 +08:00
|
|
|
} else
|
2023-06-18 20:28:18 +08:00
|
|
|
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
|
|
|
|
chn);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
2025-04-29 19:10:54 +08:00
|
|
|
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
|
2023-06-18 20:28:18 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = tas_priv->change_chn_book(tas_priv, chn,
|
2023-06-18 20:28:18 +08:00
|
|
|
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;
|
|
|
|
|
2025-04-29 19:10:54 +08:00
|
|
|
ret = tas_priv->change_chn_book(tas_priv, chn,
|
2023-06-18 20:28:18 +08:00
|
|
|
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);
|
|
|
|
|
2025-05-13 16:59:47 +08:00
|
|
|
void tasdevice_remove(struct tasdevice_priv *tas_priv)
|
|
|
|
{
|
|
|
|
mutex_destroy(&tas_priv->codec_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(tasdevice_remove);
|
|
|
|
|
2023-06-18 20:28:18 +08:00
|
|
|
MODULE_DESCRIPTION("TAS2781 common library");
|
|
|
|
MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
|
|
|
|
MODULE_LICENSE("GPL");
|