一、简介
本文讲述的多功放兼容驱动框架是一套基于 Linux ALSA/ASoC 的音频功放驱动方案,当前已兼容ad82088、ad82120、tas5731、acm8625; 平台为 RK3588 Android16(6.1内核)。该框架通过芯片操作集抽象层(amp_chip_ops),将不同功放芯片的硬件差异封装在各自的操作函数中,对上层提供统一的 ALSA 控件接口和用户空间 API。
二、工作原理及流程
整体工作原理
框架采用三层分离设计:设备树描述硬件连接,内核驱动负责芯片控制,上层应用通过 ALSA 标准接口与驱动交互。核心思想是将各芯片的硬件差异封装在芯片操作集(amp_chip_ops)中,驱动框架本身不关心具体芯片的寄存器细节,只通过操作集的函数指针调用对应芯片的实现。
┌────────────────────────────────────────────────────────────┐
│ 上层应用 │
│ 通过 ALSA Mixer 控件读写数据 │
├────────────────────────────────────────────────────────────┤
│ ALSA 控件层 │
│ Skg Spk Mute / Skg Spk DSP Config / Skg Spk DSP RAM │
│ Skg Sub Mute / Skg Sub DSP Config / Skg Sub DSP RAM │
├────────────────────────────────────────────────────────────┤
│ 内核驱动 (skg_amp.c) │
│ 统一的控件回调 → ops->set_mute / write_bulk / write_ram │
├────────────────────────────────────────────────────────────┤
│ 芯片操作集 (amp_chip_ops) │
│ ACM8625_ops / AD82120_ops / TAS5731_ops / AD82088_ops │
├────────────────────────────────────────────────────────────┤
│ regmap → I2C 总线 → 功放芯片 │
└────────────────────────────────────────────────────────────┘
启动阶段流程
系统启动时,内核为每个设备树中配置的功放节点执行一次完整的 probe 流程
内核加载驱动
│
├── 设备树节点 1 (如 skg_amp_spk@6a)
│ │
│ └── skg_amp_i2c_probe()
│ ├── ① 解析设备树
│ │ ├── skg,amp-role → 确定角色 (spk/sub)
│ │ ├── acme,chip-type → 芯片类型 (可选)
│ │ ├── mute-gpios → 静音 GPIO
│ │ └── no_mclk → 是否需要时钟
│ │
│ ├── ② 初始化 regmap
│ │ └── 绑定到具体 I2C 总线和地址
│ │
│ ├── ③ 芯片检测 (detect_chip_type)
│ │ ├── 设备树指定 → 直接使用
│ │ ├── I2C 读 ID 寄存器 → 自动匹配
│ │ └── 都不匹配 → 默认 ACM8625
│ │
│ ├── ④ 注册 ALSA 组件
│ │ └── component_probe()
│ │ ├── 根据角色生成控件名
│ │ │ SPK → "Skg Spk Mute" / "Skg Spk DSP Config" / "Skg Spk DSP RAM"
│ │ │ SUB → "Skg Sub Mute" / "Skg Sub DSP Config" / "Skg Sub DSP RAM"
│ │ ├── 动态注册 3 个 ALSA 控件
│ │ └── 调用 ops->init()
│ │ ├── 硬件复位
│ │ └── 加载默认配置表 (兜底)
│ │ ├── REG 数据 → snd_soc_component_write
│ │ └── RAM 数据 → 芯片专用 RAM 写入流程
│ │
│ └── ⑤ 驱动就绪,功放可正常工作
│
└── 设备树节点 2 (如 skg_amp_sub@6b)
└── 重复上述流程(独立实例,独立 regmap,独立控件名)
三、代码实现
skg_amp.c
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/gpio/consumer.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/pm.h>
#include "skg_amp.h"
#include "skg_amp_registers.h"
#define DRV_NAME "skg-audio-amplifier"
/* 角色自动分配:记录已使用的角色位图 */
static unsigned long role_assigned;
/* 调试开关 */
static bool debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Enable debug logging (default: 0)");
#define skg_dbg(dev, fmt, ...) \
do { if (debug) dev_info(dev, fmt, ##__VA_ARGS__); } while (0)
/* 支持的音频格式 */
#define SKG_AMP_RATES (SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_64000 | \
SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 | \
SNDRV_PCM_RATE_176400 | \
SNDRV_PCM_RATE_192000)
#define SKG_AMP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
/*============================================================================
* ACM8625芯片操作集实现
============================================================================*/
/* 前向声明:RAM 写入函数(被 init 调用) */
static int ad82120_ram_write_one(struct snd_soc_component *component,
u8 addr, u8 top, u8 mid, u8 bot, u8 bot2, u8 block);
static int ad82088_ram_write_one(struct snd_soc_component *component,
u8 addr, u8 top, u8 mid, u8 bot, u8 block);
static int acm8625_init(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
const u8 (*dsp_cfg)[2];
int cfg_count, i, ret;
dev_info(component->dev, "Initializing ACM8625 (I2C 0x%02x, role=%s)\n",
skg_amp->i2c_addr, skg_amp->role_name);
if (skg_amp->mute_gpio) {
gpiod_set_value_cansleep(skg_amp->mute_gpio, 1);
mdelay(200);
gpiod_set_value_cansleep(skg_amp->mute_gpio, 0);
mdelay(50);
}
mdelay(500);
/* ACM8625硬件复位序列 */
snd_soc_component_write(component, 0x00, 0x00);
snd_soc_component_write(component, 0x04, 0x00);
snd_soc_component_write(component, 0xfc, 0x86);
snd_soc_component_write(component, 0xfe, 0x15);
snd_soc_component_write(component, 0x00, 0x01);
snd_soc_component_write(component, 0x02, 0x20);
snd_soc_component_write(component, 0x00, 0x00);
mdelay(50);
/* 根据角色选择默认配置表 */
if (skg_amp->role == AMP_ROLE_SUB) {
dsp_cfg = acm8625_sub_dsp_cfg;
cfg_count = ACM8625_SUB_DSP_CFG_COUNT;
} else {
dsp_cfg = acm8625_spk_dsp_cfg;
cfg_count = ACM8625_SPK_DSP_CFG_COUNT;
}
dev_info(component->dev, "Loading ACM8625 default DSP config (%s), count: %d\n",
skg_amp->role_name, cfg_count);
for (i = 0; i < cfg_count; i++) {
ret = snd_soc_component_write(component, dsp_cfg[i][0], dsp_cfg[i][1]);
if (ret < 0) {
dev_err(component->dev, "DSP config failed at index %d, reg 0x%02x\n",
i, dsp_cfg[i][0]);
return ret;
}
}
snd_soc_component_write(component, 0x04, 0x03);
dev_info(component->dev, "ACM8625 initialization completed (%s)\n", skg_amp->role_name);
return 0;
}
static int acm8625_shutdown(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
dev_info(component->dev, "Shutting down ACM8625\n");
if (skg_amp->mute_gpio)
gpiod_set_value_cansleep(skg_amp->mute_gpio, 0);
return 0;
}
static int acm8625_set_mute(struct snd_soc_component *component, bool mute)
{
u8 reg_val;
/* 切换到 page 0 */
snd_soc_component_write(component, 0x00, 0x00);
reg_val = snd_soc_component_read(component, 0x04);
dev_info(component->dev, "acm8625_set_mute: mute=%d, reg04=0x%02x\n", mute, reg_val);
if (mute)
reg_val &= ~0x03;
else
reg_val |= 0x03;
snd_soc_component_write(component, 0x04, reg_val);
return 0;
}
static int acm8625_get_mute(struct snd_soc_component *component)
{
u8 reg_val;
/* 切换到 page 0 */
snd_soc_component_write(component, 0x00, 0x00);
reg_val = snd_soc_component_read(component, 0x04);
return (reg_val & 0x03) == 0 ? 1 : 0;
}
static int acm8625_write_bulk(struct snd_soc_component *component,
const u8 *data, int count)
{
int i, ret = 0;
int reg_count;
if (count < 2 || (count % 2) != 0) {
dev_err(component->dev, "ACM8625: Invalid bulk data count: %d\n", count);
return -EINVAL;
}
reg_count = count / 2;
skg_dbg(component->dev, "ACM8625: Writing %d register pairs\n", reg_count);
for (i = 0; i < reg_count; i++) {
u8 reg = data[i * 2];
u8 value = data[i * 2 + 1];
ret = snd_soc_component_write(component, reg, value);
if (ret < 0) {
dev_err(component->dev, "ACM8625: Bulk write failed at index %d, reg 0x%02x\n", i, reg);
break;
}
}
skg_dbg(component->dev, "ACM8625: Bulk write completed, wrote %d registers\n", i);
return ret;
}
/*============================================================================
* AD82120芯片操作集实现
============================================================================*/
static int ad82120_init(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
struct REG_TAB1 *reg_cfg;
struct AD82120_RAM_TAB *ram_cfg;
int reg_count, ram_count, i, ret;
u8 block = 0;
dev_info(component->dev, "Initializing AD82120 (I2C 0x%02x, role=%s)\n",
skg_amp->i2c_addr, skg_amp->role_name);
/* 软件复位 */
snd_soc_component_write(component, 0x1A, 0x10);
mdelay(50);
snd_soc_component_write(component, 0x1A, 0x30);
mdelay(50);
/* 静音 */
snd_soc_component_write(component, 0x02, 0x7F);
/* 根据角色选择默认配置表 */
if (skg_amp->role == AMP_ROLE_SUB) {
reg_cfg = ad82120_sub_reg_cfg;
reg_count = AD82120_SUB_REG_CFG_COUNT;
ram_cfg = ad82120_sub_ram_cfg;
ram_count = AD82120_SUB_RAM_CFG_COUNT;
} else {
reg_cfg = ad82120_spk_reg_cfg;
reg_count = AD82120_SPK_REG_CFG_COUNT;
ram_cfg = ad82120_spk_ram_cfg;
ram_count = AD82120_SPK_RAM_CFG_COUNT;
}
/* 写 REG */
dev_info(component->dev, "Loading AD82120 REG config (%s), count: %d\n",
skg_amp->role_name, reg_count);
for (i = 0; i < reg_count; i++) {
if (reg_cfg[i].Address == 0x02)
continue;
ret = snd_soc_component_write(component, reg_cfg[i].Address, reg_cfg[i].Data);
if (ret < 0) {
dev_err(component->dev, "AD82120: REG init failed at %d\n", i);
return ret;
}
}
/* 写 RAM */
dev_info(component->dev, "Loading AD82120 RAM config (%s), count: %d\n",
skg_amp->role_name, ram_count);
block = 0;
for (i = 0; i < ram_count; i++) {
if (ram_cfg[i].Address == 0x00 && i > 0)
block++;
ret = ad82120_ram_write_one(component,
ram_cfg[i].Address,
ram_cfg[i].Top_data,
ram_cfg[i].Middle_data,
ram_cfg[i].Botton_data,
ram_cfg[i].Botton_data2,
block);
if (ret < 0) {
dev_err(component->dev, "AD82120: RAM init failed at %d\n", i);
return ret;
}
}
dev_info(component->dev, "AD82120 initialization completed (%s)\n", skg_amp->role_name);
return 0;
}
static int ad82120_shutdown(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
dev_info(component->dev, "Shutting down AD82120\n");
if (skg_amp->mute_gpio)
gpiod_set_value_cansleep(skg_amp->mute_gpio, 0);
return 0;
}
static int ad82120_set_mute(struct snd_soc_component *component, bool mute)
{
/* reg 0x02: 0x7F=静音, 0x00=解静音 */
return snd_soc_component_write(component, 0x02, mute ? 0x7F : 0x00);
}
static int ad82120_get_mute(struct snd_soc_component *component)
{
u8 reg_val = snd_soc_component_read(component, 0x02);
return (reg_val == 0x7F) ? 1 : 0;
}
/**
* AD82120 RAM 单条写入
*/
static int ad82120_ram_write_one(struct snd_soc_component *component,
u8 addr, u8 top, u8 mid, u8 bot, u8 bot2, u8 block)
{
u8 cfrw_val;
int retry = 250;
int ret;
do {
cfrw_val = snd_soc_component_read(component, AD82120_CFRW);
if (!(cfrw_val & 0x01))
break;
udelay(10);
} while (--retry > 0);
if (retry <= 0) {
dev_err(component->dev, "AD82120: RAM write timeout\n");
return -ETIMEDOUT;
}
snd_soc_component_write(component, AD82120_CFADDR, addr);
snd_soc_component_write(component, AD82120_A1CF1, top);
snd_soc_component_write(component, AD82120_A1CF2, mid);
snd_soc_component_write(component, AD82120_A1CF3, bot);
snd_soc_component_write(component, AD82120_A1CF4, bot2);
ret = snd_soc_component_write(component, AD82120_CFRW, (block << 6) | 0x01);
return ret;
}
/* AD82120 批量写入:REG 数据,格式 [addr, data] 对 */
static int ad82120_write_bulk(struct snd_soc_component *component,
const u8 *data, int count)
{
int i, ret = 0;
if (count < 2 || (count % 2) != 0) {
dev_err(component->dev, "AD82120: Invalid REG data count: %d\n", count);
return -EINVAL;
}
skg_dbg(component->dev, "AD82120: Writing %d REG entries\n", count / 2);
for (i = 0; i < count; i += 2) {
if (data[i] == 0x02)
continue;
ret = snd_soc_component_write(component, data[i], data[i + 1]);
if (ret < 0) {
dev_err(component->dev, "AD82120: REG write failed at offset %d\n", i);
return ret;
}
}
return 0;
}
/* AD82120 RAM 写入:格式 [addr, top, mid, bot, bot2] 每条 5 字节 */
static int ad82120_write_ram(struct snd_soc_component *component,
const u8 *data, int count)
{
int i, ret = 0;
int ram_count;
u8 block = 0;
if (count < 5 || (count % 5) != 0) {
dev_err(component->dev, "AD82120: Invalid RAM data count: %d\n", count);
return -EINVAL;
}
ram_count = count / 5;
skg_dbg(component->dev, "AD82120: Writing %d RAM entries\n", ram_count);
for (i = 0; i < ram_count; i++) {
const u8 *entry = &data[i * 5];
if (entry[0] == 0x00 && i > 0)
block++;
ret = ad82120_ram_write_one(component,
entry[0], entry[1], entry[2],
entry[3], entry[4], block);
if (ret < 0) {
dev_err(component->dev, "AD82120: RAM write failed at entry %d\n", i);
return ret;
}
}
skg_dbg(component->dev, "AD82120: RAM write completed, %d entries, %d blocks\n",
ram_count, block + 1);
return 0;
}
/*============================================================================
* TAS5731芯片操作集实现
============================================================================*/
static int tas5731_init(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
unsigned char *dsp_cfg;
int cfg_size, offset, ret;
dev_info(component->dev, "Initializing TAS5731 (I2C 0x%02x, role=%s)\n",
skg_amp->i2c_addr, skg_amp->role_name);
/* 根据角色选择默认配置表 */
if (skg_amp->role == AMP_ROLE_SUB) {
dsp_cfg = tas5731_sub_dsp_cfg;
cfg_size = TAS5731_SUB_DSP_CFG_SIZE;
} else {
dsp_cfg = tas5731_spk_dsp_cfg;
cfg_size = TAS5731_SPK_DSP_CFG_SIZE;
}
dev_info(component->dev, "Loading TAS5731 DSP config (%s), size: %d bytes\n",
skg_amp->role_name, cfg_size);
offset = 0;
while (offset < cfg_size) {
u8 len = dsp_cfg[offset];
u8 reg = dsp_cfg[offset + 1];
if (offset + 1 + len >= cfg_size) {
dev_err(component->dev, "TAS5731: DSP config truncated at offset %d\n", offset);
break;
}
if (len == 1) {
ret = snd_soc_component_write(component, reg, dsp_cfg[offset + 2]);
} else {
ret = regmap_raw_write(skg_amp->regmap, reg, &dsp_cfg[offset + 2], len);
}
if (ret < 0) {
dev_err(component->dev, "TAS5731: DSP config failed at offset %d, reg 0x%02x\n",
offset, reg);
return ret;
}
offset += 1 + 1 + len;
}
dev_info(component->dev, "TAS5731 initialization completed (%s)\n", skg_amp->role_name);
return 0;
}
static int tas5731_shutdown(struct snd_soc_component *component)
{
dev_info(component->dev, "Shutting down TAS5731\n");
return 0;
}
static int tas5731_set_mute(struct snd_soc_component *component, bool mute)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
int ret;
skg_dbg(component->dev, "TAS5731: set_mute=%d\n", mute);
if (mute) {
/* 静音:先设音量为最大衰减,再使能软件静音 */
u8 mute_vol[] = { 0xFF };
u8 mute_sw[] = { 0x07 };
ret = regmap_raw_write(skg_amp->regmap, 0x07, mute_vol, 1);
if (ret < 0) return ret;
usleep_range(2000, 3000);
ret = regmap_raw_write(skg_amp->regmap, 0x06, mute_sw, 1);
} else {
/* 解静音:分步恢复音量(防爆音),再关闭软件静音 */
u8 vol_step1[] = { 0xF0 };
u8 vol_step2[] = { 0x7C };
u8 vol_step3[] = { 0x30 };
u8 unmute_sw[] = { 0x00 };
ret = regmap_raw_write(skg_amp->regmap, 0x07, vol_step1, 1);
if (ret < 0) return ret;
usleep_range(2000, 3000);
ret = regmap_raw_write(skg_amp->regmap, 0x07, vol_step2, 1);
if (ret < 0) return ret;
usleep_range(2000, 3000);
ret = regmap_raw_write(skg_amp->regmap, 0x07, vol_step3, 1);
if (ret < 0) return ret;
usleep_range(2000, 3000);
ret = regmap_raw_write(skg_amp->regmap, 0x06, unmute_sw, 1);
}
return ret;
}
static int tas5731_get_mute(struct snd_soc_component *component)
{
/* reg 0x06: 0x07=静音, 0x00=非静音 */
u8 reg_val = snd_soc_component_read(component, 0x06);
return (reg_val == 0x07) ? 1 : 0;
}
/* TAS5731 批量写入:格式 [len, addr, data1, data2, ...] */
static int tas5731_write_bulk(struct snd_soc_component *component,
const u8 *data, int count)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
int offset = 0;
int ret = 0;
int entry_count = 0;
while (offset < count) {
u8 len = data[offset];
u8 reg;
/* 检查数据完整性 */
if (len == 0 || offset + 1 + len >= count) {
/* 遇到填充的0或数据不完整,停止解析 */
break;
}
reg = data[offset + 1];
if (len == 1) {
/* 单字节写入 */
ret = snd_soc_component_write(component, reg, data[offset + 2]);
} else {
/* 多字节写入 */
ret = regmap_raw_write(skg_amp->regmap, reg,
&data[offset + 2], len);
}
if (ret < 0) {
dev_err(component->dev, "TAS5731: Bulk write failed at entry %d, reg 0x%02x\n",
entry_count, reg);
return ret;
}
offset += 1 + 1 + len; /* len字节 + addr字节 + data字节 */
entry_count++;
}
skg_dbg(component->dev, "TAS5731: Bulk write completed, %d entries\n", entry_count);
return 0;
}
/*============================================================================
* AD82088芯片操作集实现
============================================================================*/
static int ad82088_init(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
struct REG_TAB *reg_cfg;
struct AD82088_RAM_TAB *ram_cfg;
int reg_count, ram_count, i, ret;
u8 block = 0;
dev_info(component->dev, "Initializing AD82088 (I2C 0x%02x, role=%s)\n",
skg_amp->i2c_addr, skg_amp->role_name);
/* 软件复位 */
snd_soc_component_write(component, 0x1A, 0x10);
mdelay(50);
snd_soc_component_write(component, 0x1A, 0x30);
mdelay(50);
/* 静音 */
snd_soc_component_write(component, 0x02, 0x7F);
/* 根据角色选择默认配置表 */
if (skg_amp->role == AMP_ROLE_SUB) {
reg_cfg = ad82088_sub_reg_cfg;
reg_count = AD82088_SUB_REG_CFG_COUNT;
ram_cfg = ad82088_sub_ram_cfg;
ram_count = AD82088_SUB_RAM_CFG_COUNT;
} else {
reg_cfg = ad82088_spk_reg_cfg;
reg_count = AD82088_SPK_REG_CFG_COUNT;
ram_cfg = ad82088_spk_ram_cfg;
ram_count = AD82088_SPK_RAM_CFG_COUNT;
}
/* 写 REG */
dev_info(component->dev, "Loading AD82088 REG config (%s), count: %d\n",
skg_amp->role_name, reg_count);
for (i = 0; i < reg_count; i++) {
if (reg_cfg[i].Address == 0x02)
continue;
ret = snd_soc_component_write(component, reg_cfg[i].Address, reg_cfg[i].Data);
if (ret < 0) {
dev_err(component->dev, "AD82088: REG init failed at %d\n", i);
return ret;
}
}
/* 写 RAM */
dev_info(component->dev, "Loading AD82088 RAM config (%s), count: %d\n",
skg_amp->role_name, ram_count);
block = 0;
for (i = 0; i < ram_count; i++) {
if (ram_cfg[i].Address == 0x00 && i > 0)
block++;
ret = ad82088_ram_write_one(component,
ram_cfg[i].Address,
ram_cfg[i].Top_data,
ram_cfg[i].Middle_data,
ram_cfg[i].Botton_data,
block);
if (ret < 0) {
dev_err(component->dev, "AD82088: RAM init failed at %d\n", i);
return ret;
}
}
dev_info(component->dev, "AD82088 initialization completed (%s)\n", skg_amp->role_name);
return 0;
}
static int ad82088_shutdown(struct snd_soc_component *component)
{
dev_info(component->dev, "Shutting down AD82088\n");
return 0;
}
static int ad82088_set_mute(struct snd_soc_component *component, bool mute)
{
/* reg 0x02: 0x7F=静音, 0x00=解静音 */
return snd_soc_component_write(component, 0x02, mute ? 0x7F : 0x00);
}
static int ad82088_get_mute(struct snd_soc_component *component)
{
u8 reg_val = snd_soc_component_read(component, 0x02);
return (reg_val == 0x7F) ? 1 : 0;
}
/**
* AD82088 RAM 单条写入
*/
static int ad82088_ram_write_one(struct snd_soc_component *component,
u8 addr, u8 top, u8 mid, u8 bot, u8 block)
{
u8 cfrw_val;
int retry = 250;
int ret;
do {
cfrw_val = snd_soc_component_read(component, AD82088_CFRW);
if (!(cfrw_val & 0x01))
break;
udelay(10);
} while (--retry > 0);
if (retry <= 0) {
dev_err(component->dev, "AD82088: RAM write timeout\n");
return -ETIMEDOUT;
}
snd_soc_component_write(component, AD82088_CFADDR, addr);
snd_soc_component_write(component, AD82088_A1CF1, top);
snd_soc_component_write(component, AD82088_A1CF2, mid);
snd_soc_component_write(component, AD82088_A1CF3, bot);
ret = snd_soc_component_write(component, AD82088_CFRW, (block << 6) | 0x01);
return ret;
}
/* AD82088 批量写入:REG 数据,格式 [addr, data] 对 */
static int ad82088_write_bulk(struct snd_soc_component *component,
const u8 *data, int count)
{
int i, ret = 0;
if (count < 2 || (count % 2) != 0) {
dev_err(component->dev, "AD82088: Invalid REG data count: %d\n", count);
return -EINVAL;
}
skg_dbg(component->dev, "AD82088: Writing %d REG entries\n", count / 2);
for (i = 0; i < count; i += 2) {
if (data[i] == 0x02)
continue;
ret = snd_soc_component_write(component, data[i], data[i + 1]);
if (ret < 0) {
dev_err(component->dev, "AD82088: REG write failed at offset %d\n", i);
return ret;
}
}
return 0;
}
/* AD82088 RAM 写入:格式 [addr, top, mid, bot] 每条 4 字节 */
static int ad82088_write_ram(struct snd_soc_component *component,
const u8 *data, int count)
{
int i, ret = 0;
int ram_count;
u8 block = 0;
if (count < 4 || (count % 4) != 0) {
dev_err(component->dev, "AD82088: Invalid RAM data count: %d\n", count);
return -EINVAL;
}
ram_count = count / 4;
skg_dbg(component->dev, "AD82088: Writing %d RAM entries\n", ram_count);
for (i = 0; i < ram_count; i++) {
const u8 *entry = &data[i * 4];
if (entry[0] == 0x00 && i > 0)
block++;
ret = ad82088_ram_write_one(component,
entry[0], entry[1], entry[2],
entry[3], block);
if (ret < 0) {
dev_err(component->dev, "AD82088: RAM write failed at entry %d\n", i);
return ret;
}
}
skg_dbg(component->dev, "AD82088: RAM write completed, %d entries, %d blocks\n",
ram_count, block + 1);
return 0;
}
/*============================================================================
* 芯片操作集定义
============================================================================*/
static const struct amp_chip_ops acm8625_ops = {
.name = "ACM8625",
.chip_id = ACM8625_CHIP_ID,
.chip_type = CHIP_ACM8625,
.init = acm8625_init,
.shutdown = acm8625_shutdown,
.set_mute = acm8625_set_mute,
.get_mute = acm8625_get_mute,
.write_bulk = acm8625_write_bulk,
};
static const struct amp_chip_ops ad82120_ops = {
.name = "AD82120",
.chip_id = AD82120_CHIP_ID,
.chip_type = CHIP_AD82120,
.init = ad82120_init,
.shutdown = ad82120_shutdown,
.set_mute = ad82120_set_mute,
.get_mute = ad82120_get_mute,
.write_bulk = ad82120_write_bulk,
.write_ram = ad82120_write_ram,
};
static const struct amp_chip_ops tas5731_ops = {
.name = "TAS5731",
.chip_id = TAS5731_CHIP_ID,
.chip_type = CHIP_TAS5731,
.init = tas5731_init,
.shutdown = tas5731_shutdown,
.set_mute = tas5731_set_mute,
.get_mute = tas5731_get_mute,
.write_bulk = tas5731_write_bulk,
};
static const struct amp_chip_ops ad82088_ops = {
.name = "AD82088",
.chip_id = AD82088_CHIP_ID,
.chip_type = CHIP_AD82088,
.init = ad82088_init,
.shutdown = ad82088_shutdown,
.set_mute = ad82088_set_mute,
.get_mute = ad82088_get_mute,
.write_bulk = ad82088_write_bulk,
.write_ram = ad82088_write_ram,
};
/* 芯片操作集列表 */
static const struct amp_chip_ops *chip_ops_list[] = {
&acm8625_ops,
&ad82120_ops,
&tas5731_ops,
&ad82088_ops,
NULL
};
/* 辅助函数:根据芯片类型查找操作集 */
static const struct amp_chip_ops *find_ops_by_type(enum amp_chip_type type)
{
int i;
for (i = 0; chip_ops_list[i]; i++) {
if (chip_ops_list[i]->chip_type == type)
return chip_ops_list[i];
}
return NULL;
}
/*============================================================================
* 寄存器静音控件回调函数
============================================================================*/
static int get_unified_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
int mute = 0;
if (skg_amp->ops && skg_amp->ops->get_mute)
mute = skg_amp->ops->get_mute(component);
ucontrol->value.integer.value[0] = mute;
return 0;
}
static int set_unified_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
bool mute = ucontrol->value.integer.value[0];
int ret;
skg_dbg(component->dev, "set_unified_mute: mute=%d\n", mute);
if (skg_amp->ops && skg_amp->ops->set_mute) {
ret = skg_amp->ops->set_mute(component, mute);
if (ret < 0)
return ret;
return 1; /* 通知 ALSA 值已变化 */
}
return 0;
}
/*============================================================================
* 统一寄存器读写控件回调函数
============================================================================*/
static int get_amp_dsp_config(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
unsigned char *data = (unsigned char *)ucontrol->value.bytes.data;
int offset = 0;
if (!skg_amp || !skg_amp->ops) {
dev_err(component->dev, "Invalid amp or chip ops\n");
return -ENODEV;
}
if (skg_amp->chip_type == CHIP_ACM8625) {
int i, ret;
int read_count = 0;
u8 current_page = 0xFF;
u8 reg_addr, expected_value;
skg_dbg(component->dev, "Reading DSP config for ACM8625, total entries: %ld\n",
ACM8625_SPK_DSP_CFG_COUNT);
/* 返回芯片类型和配置数量 */
data[offset++] = 1;
data[offset++] = ACM8625_SPK_DSP_CFG_COUNT & 0xFF;
data[offset++] = (ACM8625_SPK_DSP_CFG_COUNT >> 8) & 0xFF;
data[offset++] = 0;
for (i = 0; i < ACM8625_SPK_DSP_CFG_COUNT && offset < 124; i++) {
reg_addr = acm8625_spk_dsp_cfg[i][0];
expected_value = acm8625_spk_dsp_cfg[i][1];
/* 如果是页面寄存器(0x00),先切换页面 */
if (reg_addr == 0x00) {
current_page = expected_value;
data[offset++] = reg_addr;
data[offset++] = expected_value;
read_count++;
continue;
}
/* 尝试读取当前页面的寄存器 */
if (current_page != 0xFF) {
snd_soc_component_write(component, 0x00, current_page);
usleep_range(100, 200);
}
/* 读取寄存器 */
ret = snd_soc_component_read(component, reg_addr);
if (ret >= 0) {
data[offset++] = reg_addr;
data[offset++] = (u8)ret;
read_count++;
} else {
data[offset++] = reg_addr;
data[offset++] = 0xFF;
}
}
skg_dbg(component->dev, "Read %d/%ld registers from DSP config table\n",
read_count, ACM8625_SPK_DSP_CFG_COUNT);
} else if (skg_amp->chip_type == CHIP_TAS5731) {
int read_count = 0;
int src_offset = 0;
skg_dbg(component->dev, "Reading DSP config for TAS5731, size: %ld bytes\n",
TAS5731_SPK_DSP_CFG_SIZE);
/* header: chip_type + cfg_size */
data[offset++] = CHIP_TAS5731;
data[offset++] = TAS5731_SPK_DSP_CFG_SIZE & 0xFF;
data[offset++] = (TAS5731_SPK_DSP_CFG_SIZE >> 8) & 0xFF;
data[offset++] = 0;
/* 遍历配置表,读取每个寄存器的当前值 */
while (src_offset < TAS5731_SPK_DSP_CFG_SIZE && offset < 252) {
u8 len = tas5731_spk_dsp_cfg[src_offset];
u8 reg;
if (len == 0 || src_offset + 1 + len > TAS5731_SPK_DSP_CFG_SIZE)
break;
reg = tas5731_spk_dsp_cfg[src_offset + 1];
/* 检查输出缓冲区空间: 需要 2(len+reg) + len(data) */
if (offset + 2 + len > 252)
break;
data[offset++] = len;
data[offset++] = reg;
if (len == 1) {
int ret = snd_soc_component_read(component, reg);
data[offset++] = (ret >= 0) ? (u8)ret : 0xFF;
} else {
struct skg_amp_priv *priv = snd_soc_component_get_drvdata(component);
int ret = regmap_raw_read(priv->regmap, reg, &data[offset], len);
if (ret < 0)
memset(&data[offset], 0xFF, len);
offset += len;
}
read_count++;
src_offset += 1 + 1 + len;
}
skg_dbg(component->dev, "TAS5731: Read %d entries from DSP config\n", read_count);
} else {
skg_dbg(component->dev, "Chip %s does not have DSP config table\n",
skg_amp->ops->name);
data[0] = 0xFF;
}
return 0;
}
static int put_amp_dsp_config(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
unsigned char *data = (unsigned char *)ucontrol->value.bytes.data;
struct soc_bytes *params = (struct soc_bytes *)kcontrol->private_value;
int count = params->num_regs;
int ret = 0;
if (!skg_amp || !skg_amp->ops) {
dev_err(component->dev, "No chip driver loaded\n");
return -ENODEV;
}
skg_dbg(component->dev, "DSP config write %d bytes to %s\n", count, skg_amp->ops->name);
if (skg_amp->ops->write_bulk) {
ret = skg_amp->ops->write_bulk(component, data, count);
if (ret < 0) {
dev_err(component->dev, "DSP config write failed for %s: %d\n",
skg_amp->ops->name, ret);
return ret;
}
} else {
/* 回退:使用通用写入,假设 [reg, value] 格式 */
int i;
if (count % 2 != 0) {
dev_err(component->dev, "Invalid data count: %d\n", count);
return -EINVAL;
}
for (i = 0; i < count; i += 2) {
ret = snd_soc_component_write(component, data[i], data[i + 1]);
if (ret < 0) {
dev_err(component->dev, "Write failed at offset %d\n", i);
return ret;
}
}
}
return 0;
}
/*============================================================================
* ALSA控件模板(probe时动态生成带I2C地址的控件名)
============================================================================*/
static struct soc_bytes dsp_config_params = {
.base = 0,
.num_regs = 256,
.mask = 0xffffffff,
};
static struct soc_bytes dsp_ram_params = {
.base = 0,
.num_regs = 256,
.mask = 0xffffffff,
};
/*============================================================================
* DSP RAM 写入控件回调
============================================================================*/
static int get_amp_dsp_ram(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
/* RAM 控件为只写,get 返回 0 */
return 0;
}
static int put_amp_dsp_ram(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
unsigned char *data = (unsigned char *)ucontrol->value.bytes.data;
struct soc_bytes *params = (struct soc_bytes *)kcontrol->private_value;
int count = params->num_regs;
int ret;
if (!skg_amp || !skg_amp->ops) {
dev_err(component->dev, "No chip driver loaded\n");
return -ENODEV;
}
if (!skg_amp->ops->write_ram) {
dev_err(component->dev, "Chip %s does not support RAM write\n",
skg_amp->ops->name);
return -ENOSYS;
}
skg_dbg(component->dev, "DSP RAM write %d bytes to %s\n", count, skg_amp->ops->name);
ret = skg_amp->ops->write_ram(component, data, count);
if (ret < 0) {
dev_err(component->dev, "DSP RAM write failed for %s: %d\n",
skg_amp->ops->name, ret);
return ret;
}
return 0;
}
/*============================================================================
* 芯片检测函数
============================================================================*/
static int detect_chip_type(struct i2c_client *i2c, struct skg_amp_priv *skg_amp)
{
int ret, i;
u8 reg_value = 0;
dev_info(&i2c->dev, "Chip detection at I2C 0x%02x\n", i2c->addr);
/* 设备树优先 */
if (skg_amp->pdata && skg_amp->pdata->chip_type != CHIP_UNKNOWN) {
skg_amp->ops = find_ops_by_type(skg_amp->pdata->chip_type);
if (skg_amp->ops) {
skg_amp->chip_type = skg_amp->pdata->chip_type;
dev_info(&i2c->dev, "DT: %s\n", skg_amp->ops->name);
return 0;
}
}
/* 芯片检测表 */
struct {
u8 reg;
u8 id;
enum amp_chip_type type;
const char *name;
} chips[] = {
{ACM8625_CHIP_ID_REG, ACM8625_CHIP_ID, CHIP_ACM8625, "ACM8625"},
{AD82120_CHIP_ID_REG, AD82120_CHIP_ID, CHIP_AD82120, "AD82120"},
{TAS5731_CHIP_ID_REG, TAS5731_CHIP_ID, CHIP_TAS5731, "TAS5731"},
{AD82088_CHIP_ID_REG, AD82088_CHIP_ID, CHIP_AD82088, "AD82088"},
};
for (i = 0; i < ARRAY_SIZE(chips); i++) {
ret = i2c_smbus_read_byte_data(i2c, chips[i].reg);
if (ret >= 0) {
reg_value = ret;
skg_amp->detected_id = reg_value;
dev_info(&i2c->dev, "Reg 0x%02x for %s: 0x%02x\n",
chips[i].reg, chips[i].name, reg_value);
if (reg_value == chips[i].id) {
skg_amp->ops = find_ops_by_type(chips[i].type);
if (skg_amp->ops) {
skg_amp->chip_type = chips[i].type;
dev_info(&i2c->dev, "Detected %s\n", chips[i].name);
return 0;
}
}
}
}
/* 默认芯片 */
dev_warn(&i2c->dev, "Using default ACM8625\n");
skg_amp->ops = find_ops_by_type(CHIP_ACM8625);
if (skg_amp->ops) {
skg_amp->chip_type = CHIP_ACM8625;
return 0;
}
return -ENODEV;
}
/*============================================================================
* ALSA DAI操作
============================================================================*/
static int skg_amp_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
return 0;
}
static int skg_amp_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
break;
default:
return 0;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
break;
default:
return 0;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
break;
default:
return 0;
}
return 0;
}
static int skg_amp_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
case SND_SOC_BIAS_OFF:
break;
}
return 0;
}
static int skg_amp_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
skg_dbg(component->dev, "%s - %s\n", __func__,
skg_amp->ops ? skg_amp->ops->name : "unknown");
return 0;
}
static int skg_amp_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
skg_dbg(component->dev, "%s - %s, rate: %d\n", __func__,
skg_amp->ops ? skg_amp->ops->name : "unknown",
params_rate(params));
return 0;
}
static const struct snd_soc_dai_ops skg_amp_dai_ops = {
.startup = skg_amp_pcm_startup,
.hw_params = skg_amp_pcm_hw_params,
.set_sysclk = skg_amp_set_dai_sysclk,
.set_fmt = skg_amp_set_dai_fmt,
};
static struct snd_soc_dai_driver skg_amp_dai = {
.name = DRV_NAME,
.playback = {
.stream_name = "HIFI Playback",
.channels_min = 2,
.channels_max = 16,
.rates = SKG_AMP_RATES,
.formats = SKG_AMP_FORMATS,
},
.capture = {
.stream_name = "PDM-dlp Capture",
.channels_min = 2,
.channels_max = 16,
.rates = SKG_AMP_RATES,
.formats = SKG_AMP_FORMATS,
},
.ops = &skg_amp_dai_ops,
};
/*============================================================================
* ALSA组件操作
============================================================================*/
static int skg_amp_component_probe(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
int ret = 0;
skg_dbg(component->dev, "Initializing %s at 0x%02x, role=%s\n",
skg_amp->ops ? skg_amp->ops->name : "unknown",
skg_amp->i2c_addr, skg_amp->role_name);
/* 根据角色生成控件名 */
if (skg_amp->role == AMP_ROLE_SUB) {
snprintf(skg_amp->ctl_name_mute, SKG_CTL_NAME_LEN, "Skg Sub Mute");
snprintf(skg_amp->ctl_name_dsp, SKG_CTL_NAME_LEN, "Skg Sub DSP Config");
snprintf(skg_amp->ctl_name_ram, SKG_CTL_NAME_LEN, "Skg Sub DSP RAM");
} else {
snprintf(skg_amp->ctl_name_mute, SKG_CTL_NAME_LEN, "Skg Spk Mute");
snprintf(skg_amp->ctl_name_dsp, SKG_CTL_NAME_LEN, "Skg Spk DSP Config");
snprintf(skg_amp->ctl_name_ram, SKG_CTL_NAME_LEN, "Skg Spk DSP RAM");
}
/* 构建动态控件 */
skg_amp->controls[0] = (struct snd_kcontrol_new)
SOC_SINGLE_BOOL_EXT(skg_amp->ctl_name_mute, 0,
get_unified_mute, set_unified_mute);
skg_amp->controls[1] = (struct snd_kcontrol_new) {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = skg_amp->ctl_name_dsp,
.info = snd_soc_bytes_info,
.get = get_amp_dsp_config,
.put = put_amp_dsp_config,
.private_value = (unsigned long)&dsp_config_params,
};
skg_amp->controls[2] = (struct snd_kcontrol_new) {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = skg_amp->ctl_name_ram,
.info = snd_soc_bytes_info,
.get = get_amp_dsp_ram,
.put = put_amp_dsp_ram,
.private_value = (unsigned long)&dsp_ram_params,
};
/* 注册动态控件 */
ret = snd_soc_add_component_controls(component, skg_amp->controls, 3);
if (ret) {
dev_err(component->dev, "Failed to add controls: %d\n", ret);
return ret;
}
dev_info(component->dev, "Registered controls: '%s', '%s'\n",
skg_amp->ctl_name_mute, skg_amp->ctl_name_dsp);
/* 获取MCLK时钟(如果支持) */
if (!skg_amp->pdata->no_mclk) {
skg_amp->mclk = devm_clk_get(component->dev, "mclk");
if (IS_ERR(skg_amp->mclk)) {
dev_err(component->dev, "mclk is missing or invalid\n");
} else {
ret = clk_prepare_enable(skg_amp->mclk);
if (ret)
dev_err(component->dev, "mclk enable failed: %d\n", ret);
}
}
/* 调用芯片特定的初始化 */
if (skg_amp->ops && skg_amp->ops->init)
ret = skg_amp->ops->init(component);
return ret;
}
static void skg_amp_component_remove(struct snd_soc_component *component)
{
struct skg_amp_priv *skg_amp = snd_soc_component_get_drvdata(component);
if (skg_amp->ops && skg_amp->ops->shutdown)
skg_amp->ops->shutdown(component);
if (!skg_amp->pdata->no_mclk && skg_amp->mclk)
clk_disable_unprepare(skg_amp->mclk);
}
static const struct snd_soc_dapm_widget skg_amp_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC", "HIFI Playback", SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_component_driver soc_codec_dev_skg_amp = {
.name = DRV_NAME,
.probe = skg_amp_component_probe,
.remove = skg_amp_component_remove,
.set_bias_level = skg_amp_set_bias_level,
.dapm_widgets = skg_amp_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(skg_amp_dapm_widgets),
};
/*============================================================================
* 设备树解析
============================================================================*/
static int skg_amp_parse_dt(struct skg_amp_priv *skg_amp,
struct device_node *np)
{
int ret = 0;
const char *chip_type_str = NULL;
const char *role_str = NULL;
skg_amp->pdata->no_mclk = of_property_read_bool(np, "no_mclk");
/* 解析芯片类型 */
ret = of_property_read_string(np, "acme,chip-type", &chip_type_str);
if (!ret) {
if (!strcmp(chip_type_str, "acm8625"))
skg_amp->pdata->chip_type = CHIP_ACM8625;
else if (!strcmp(chip_type_str, "ad82120"))
skg_amp->pdata->chip_type = CHIP_AD82120;
else if (!strcmp(chip_type_str, "tas5731"))
skg_amp->pdata->chip_type = CHIP_TAS5731;
else if (!strcmp(chip_type_str, "ad82088"))
skg_amp->pdata->chip_type = CHIP_AD82088;
else
skg_amp->pdata->chip_type = CHIP_UNKNOWN;
} else {
skg_amp->pdata->chip_type = CHIP_UNKNOWN;
}
/* 解析功放角色:spk(立体音) 或 sub(低音炮) */
ret = of_property_read_string(np, "skg,amp-role", &role_str);
if (!ret && role_str) {
/* 设备树明确指定了角色 */
if (!strcmp(role_str, "sub")) {
skg_amp->role = AMP_ROLE_SUB;
strscpy(skg_amp->role_name, "sub", SKG_ROLE_NAME_LEN);
} else {
skg_amp->role = AMP_ROLE_SPK;
strscpy(skg_amp->role_name, "spk", SKG_ROLE_NAME_LEN);
}
} else {
/* 未指定角色,自动分配:第一个 spk,第二个 sub */
if (!test_and_set_bit(AMP_ROLE_SPK, &role_assigned)) {
skg_amp->role = AMP_ROLE_SPK;
strscpy(skg_amp->role_name, "spk", SKG_ROLE_NAME_LEN);
} else if (!test_and_set_bit(AMP_ROLE_SUB, &role_assigned)) {
skg_amp->role = AMP_ROLE_SUB;
strscpy(skg_amp->role_name, "sub", SKG_ROLE_NAME_LEN);
} else {
pr_warn("skg_amp: All roles assigned, please configure 'skg,amp-role' in DT\n");
skg_amp->role = AMP_ROLE_SPK;
strscpy(skg_amp->role_name, "spk", SKG_ROLE_NAME_LEN);
}
}
pr_debug("skg_amp: no_mclk=%d, chip_type=%d, role=%s\n",
skg_amp->pdata->no_mclk,
skg_amp->pdata->chip_type,
skg_amp->role_name);
return 0;
}
/*============================================================================
* I2C驱动核心
============================================================================*/
static const struct regmap_config skg_amp_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xFF,
};
static int skg_amp_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct skg_amp_priv *skg_amp;
struct skg_amp_platform_data *pdata;
int ret = 0;
dev_info(&i2c->dev, "Probing audio amplifier\n");
/* 分配私有数据结构 */
skg_amp = devm_kzalloc(&i2c->dev, sizeof(struct skg_amp_priv), GFP_KERNEL);
if (!skg_amp)
return -ENOMEM;
/* 分配平台数据 */
pdata = devm_kzalloc(&i2c->dev, sizeof(struct skg_amp_platform_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
skg_amp->pdata = pdata;
/* 解析设备树 */
skg_amp_parse_dt(skg_amp, i2c->dev.of_node);
/* 获取MUTE GPIO */
skg_amp->mute_gpio = devm_gpiod_get_optional(&i2c->dev, "mute",
GPIOD_OUT_LOW);
if (IS_ERR(skg_amp->mute_gpio)) {
ret = PTR_ERR(skg_amp->mute_gpio);
dev_warn(&i2c->dev, "Failed to get mute GPIO: %d\n", ret);
skg_amp->mute_gpio = NULL;
} else if (skg_amp->mute_gpio) {
dev_info(&i2c->dev, "Got mute GPIO\n");
gpiod_set_value_cansleep(skg_amp->mute_gpio, 0);
}
/* 初始化regmap */
skg_amp->regmap = devm_regmap_init_i2c(i2c, &skg_amp_regmap);
if (IS_ERR(skg_amp->regmap)) {
ret = PTR_ERR(skg_amp->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret);
return ret;
}
i2c_set_clientdata(i2c, skg_amp);
/* 记录I2C地址,用于多芯片区分 */
skg_amp->i2c_addr = i2c->addr;
/* 检测芯片类型 */
ret = detect_chip_type(i2c, skg_amp);
if (ret) {
dev_err(&i2c->dev, "Failed to detect chip type: %d\n", ret);
return ret;
}
/* 注册ALSA组件 */
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_codec_dev_skg_amp,
&skg_amp_dai, 1);
if (ret) {
dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
dev_info(&i2c->dev, "%s registered successfully\n",
skg_amp->ops ? skg_amp->ops->name : "unknown");
return 0;
}
static void skg_amp_i2c_remove(struct i2c_client *client)
{
dev_info(&client->dev, "Removing audio amplifier\n");
}
static void skg_amp_i2c_shutdown(struct i2c_client *client)
{
struct skg_amp_priv *skg_amp = i2c_get_clientdata(client);
if (!skg_amp)
return;
dev_info(&client->dev, "Shutting down %s\n",
skg_amp->ops ? skg_amp->ops->name : "unknown");
if (skg_amp->mute_gpio)
gpiod_set_value_cansleep(skg_amp->mute_gpio, 1);
}
/*============================================================================
* 电源管理
============================================================================*/
#ifdef CONFIG_PM_SLEEP
static int skg_amp_suspend(struct device *dev)
{
struct skg_amp_priv *skg_amp = dev_get_drvdata(dev);
if (skg_amp && skg_amp->mute_gpio)
gpiod_set_value_cansleep(skg_amp->mute_gpio, 1);
return 0;
}
static int skg_amp_resume(struct device *dev)
{
struct skg_amp_priv *skg_amp = dev_get_drvdata(dev);
if (skg_amp && skg_amp->mute_gpio)
gpiod_set_value_cansleep(skg_amp->mute_gpio, 0);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(skg_amp_pm_ops, skg_amp_suspend, skg_amp_resume);
static const struct i2c_device_id skg_amp_i2c_id[] = {
{ "skg-audio-amplifier", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, skg_amp_i2c_id);
static const struct of_device_id skg_amp_of_id[] = {
{ .compatible = "ebsw, skg_amp", },
{}
};
MODULE_DEVICE_TABLE(of, skg_amp_of_id);
static struct i2c_driver skg_amp_i2c_driver = {
.driver = {
.name = "skg-audio-amplifier",
.owner = THIS_MODULE,
.of_match_table = skg_amp_of_id,
.pm = &skg_amp_pm_ops,
},
.probe = skg_amp_i2c_probe,
.remove = skg_amp_i2c_remove,
.shutdown = skg_amp_i2c_shutdown,
.id_table = skg_amp_i2c_id,
};
module_i2c_driver(skg_amp_i2c_driver);
MODULE_DESCRIPTION("ASoC SKG Multi-Amplifier Driver (ACM8625/AD82120/TAS5731/AD82088)");
MODULE_AUTHOR("rongyf <rongyf@tc.cn>");
MODULE_LICENSE("GPL");
skg_amp.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
* skg_amp.h -- SKG Multi-Amplifier Driver Header
*
* 支持芯片: ACM8625, AD82120, TAS5731, AD82088
* 支持角色: SPK(立体音), SUB(低音炮)
*/
#ifndef _SKG_AMP_HEADER
#define _SKG_AMP_HEADER
#include <linux/i2c.h>
#include <sound/soc.h>
/* 芯片类型枚举 */
enum amp_chip_type {
CHIP_UNKNOWN = 0,
CHIP_ACM8625, /* 分页寄存器架构,DSP 格式: [reg, val] 对 */
CHIP_AD82120, /* REG + RAM 架构,RAM 格式: [addr, top, mid, bot, bot2] */
CHIP_TAS5731, /* 变长格式: [len, addr, data...] */
CHIP_AD82088, /* REG + RAM 架构,RAM 格式: [addr, top, mid, bot] */
CHIP_MAX
};
/* 芯片操作集 - 每款芯片实现自己的操作函数 */
struct amp_chip_ops {
const char *name; /* 芯片名称 */
u8 chip_id; /* 芯片 ID 寄存器期望值 */
enum amp_chip_type chip_type; /* 芯片类型 */
/* 生命周期 */
int (*init)(struct snd_soc_component *component); /* 硬件复位 + 加载默认配置 */
int (*shutdown)(struct snd_soc_component *component); /* 关闭芯片 */
/* 静音控制 */
int (*set_mute)(struct snd_soc_component *component, bool mute);
int (*get_mute)(struct snd_soc_component *component); /* 返回 1=静音, 0=非静音 */
/* 数据写入 - 由用户空间通过 ALSA 控件触发 */
int (*write_bulk)(struct snd_soc_component *component, /* 写 REG 数据 */
const u8 *data, int count);
int (*write_ram)(struct snd_soc_component *component, /* 写 RAM 数据 (AD82120/AD82088) */
const u8 *data, int count); /* 无 RAM 的芯片设为 NULL */
};
/* 平台数据 - 从设备树解析 */
struct skg_amp_platform_data {
bool no_mclk; /* 是否不需要 MCLK 时钟 */
enum amp_chip_type chip_type; /* 设备树指定的芯片类型(可选) */
};
/* 功放角色 - 区分立体音和低音炮 */
enum amp_role {
AMP_ROLE_SPK = 0, /* 立体音 (Stereo Speaker) */
AMP_ROLE_SUB, /* 低音炮 (Subwoofer) */
AMP_ROLE_MAX
};
#define SKG_CTL_NAME_LEN 32 /* 控件名最大长度 */
#define SKG_ROLE_NAME_LEN 8 /* 角色名最大长度 */
/* 驱动私有数据 - 每个 I2C 设备一个实例 */
struct skg_amp_priv {
struct regmap *regmap; /* I2C regmap,绑定具体总线/地址 */
struct snd_soc_component *component; /* ALSA 组件 */
struct skg_amp_platform_data *pdata; /* 平台数据 */
struct clk *mclk; /* MCLK 时钟 */
/* 芯片信息 */
enum amp_chip_type chip_type; /* 实际检测到的芯片类型 */
const struct amp_chip_ops *ops; /* 芯片操作集指针 */
u8 detected_id; /* 实际检测到的芯片 ID */
u8 i2c_addr; /* I2C 从设备地址 */
/* 角色信息 */
enum amp_role role; /* 功放角色 (spk/sub) */
char role_name[SKG_ROLE_NAME_LEN]; /* 角色名字符串 */
struct gpio_desc *mute_gpio; /* 静音 GPIO */
/* 动态 ALSA 控件(根据角色生成不同名字) */
char ctl_name_mute[SKG_CTL_NAME_LEN]; /* "Skg Spk Mute" 或 "Skg Sub Mute" */
char ctl_name_dsp[SKG_CTL_NAME_LEN]; /* "Skg Spk DSP Config" 或 "Skg Sub DSP Config" */
char ctl_name_ram[SKG_CTL_NAME_LEN]; /* "Skg Spk DSP RAM" 或 "Skg Sub DSP RAM" */
struct snd_kcontrol_new controls[3]; /* Mute + DSP Config + DSP RAM */
};
#endif /* _SKG_AMP_HEADER */
skg_amp_registers.h
存放默认数据的头文件(已删减部分数据, 具体格式自行阅读芯片手册)
#ifndef __SKG_AMP_REGISTERS_H__
#define __SKG_AMP_REGISTERS_H__
#define ACM8625_CHIP_ID_REG 0x15
#define ACM8625_CHIP_ID 0x26
#define ACM8625_SPK_DSP_CFG_COUNT (sizeof(acm8625_spk_dsp_cfg) / sizeof(acm8625_spk_dsp_cfg[0]))
#define AD82120_CHIP_ID_REG 0x37
#define AD82120_CHIP_ID 0xA0
#define AD82120_SPK_REG_CFG_COUNT (sizeof(ad82120_spk_reg_cfg) / sizeof(ad82120_spk_reg_cfg[0]))
#define AD82120_SPK_RAM_CFG_COUNT (sizeof(ad82120_spk_ram_cfg) / sizeof(ad82120_spk_ram_cfg[0]))
/* AD82120 RAM 写入相关寄存器 */
#define AD82120_CFADDR 0x1D /* RAM 地址寄存器 */
#define AD82120_A1CF1 0x1E /* RAM 数据 byte1 */
#define AD82120_A1CF2 0x1F /* RAM 数据 byte2 */
#define AD82120_A1CF3 0x20 /* RAM 数据 byte3 */
#define AD82120_A1CF4 0x21 /* RAM 数据 byte4 */
#define AD82120_CFRW 0x32 /* RAM 读写控制寄存器 */
#define TAS5731_CHIP_ID_REG 0x01
#define TAS5731_CHIP_ID 0x00
#define TAS5731_SPK_DSP_CFG_SIZE (sizeof(tas5731_spk_dsp_cfg))
#define AD82088_CHIP_ID_REG 0x37
#define AD82088_CHIP_ID 0x52
#define AD82088_SPK_REG_CFG_COUNT (sizeof(ad82088_spk_reg_cfg) / sizeof(ad82088_spk_reg_cfg[0]))
#define AD82088_SPK_RAM_CFG_COUNT (sizeof(ad82088_spk_ram_cfg) / sizeof(ad82088_spk_ram_cfg[0]))
/* AD82088 RAM 写入相关寄存器 */
#define AD82088_CFADDR 0x1D /* RAM 地址寄存器 */
#define AD82088_A1CF1 0x1E /* RAM 数据 byte1 */
#define AD82088_A1CF2 0x1F /* RAM 数据 byte2 */
#define AD82088_A1CF3 0x20 /* RAM 数据 byte3 */
#define AD82088_CFRW 0x2D /* RAM 读写控制寄存器 */
struct REG_TAB {
unsigned char Address;
unsigned char Data;
};
struct AD82120_RAM_TAB {
unsigned char Address;
unsigned char Top_data;
unsigned char Middle_data;
unsigned char Botton_data;
unsigned char Botton_data2;
};
struct AD82088_RAM_TAB {
unsigned char Address;
unsigned char Top_data;
unsigned char Middle_data;
unsigned char Botton_data;
};
/* Power-up register defaults */
static const u8 acm8625_spk_dsp_cfg[][2] = {
//Digital Off during DSP configuration
//Digital Off during DSP configuration
{ 0x00, 0x00 },
{ 0x04, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
//EQ
//EQ1 - left; Setting: Type - High Pass; Fc - 100; Gain - 0.0; Q - 1.0; Bandwidth - 1000
{ 0x00, 0x06 },
{ 0xb0, 0x07 },
{ 0xb1, 0xf2 },
{ 0xb2, 0x98 },
{ 0xb3, 0xa4 },
{ 0xb4, 0xf0 },
{ 0xb5, 0x1a },
{ 0xb6, 0xce },
{ 0xb7, 0xb8 },
{ 0xb8, 0x07 },
{ 0xb9, 0xf2 },
{ 0xba, 0x98 },
{ 0xbb, 0xa4 },
{ 0xbc, 0x0f },
{ 0xbd, 0xe5 },
{ 0xbe, 0x04 },
{ 0xbf, 0xa8 },
{ 0xc0, 0xf8 },
{ 0xc1, 0x1a },
{ 0xc2, 0xa2 },
{ 0xc3, 0x18 },
//EQ2 - left; Setting: Type - High Pass; Fc - 100; Gain - 0.0; Q - 0.7; Bandwidth - 1000
{ 0xc4, 0x07 },
{ 0xc5, 0xec },
{ 0xc6, 0xf1 },
{ 0xc7, 0x37 },
{ 0xc8, 0xf0 },
{ 0xc9, 0x26 },
{ 0xca, 0x1d },
{ 0xcb, 0x92 },
{ 0xcc, 0x07 },
{ 0xcd, 0xec },
{ 0xce, 0xf1 },
{ 0xcf, 0x37 },
{ 0xd0, 0x0f },
{ 0xd1, 0xd9 },
{ 0xd2, 0xb5 },
{ 0xd3, 0xee },
{ 0xd4, 0xf8 },
{ 0xd5, 0x25 },
{ 0xd6, 0xf1 },
{ 0xd7, 0x11 },
......................
//ClassD Control Registers
{ 0x00, 0x01 },
{ 0x01, 0x00 },
{ 0x00, 0x00 },
{ 0x11, 0x03 },
{ 0x02, 0x07 },
{ 0x06, 0xf0 },
{ 0x28, 0x00 },
{ 0x05, 0x46 },
{ 0x03, 0x02 },
{ 0x01, 0x84 },
{ 0x00, 0x00 },
{ 0x04, 0x02 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x00 },
{ 0x00, 0x03 },
{ 0x13, 0x1d },
{ 0x00, 0x00 },
{ 0x04, 0x03 },
};
unsigned char tas5731_spk_dsp_cfg[] =
{
1, 0x05, 0x00,
1, 0x06, 0x00,
1, 0x07, 0x20, // Main Volume ( 0xFF : Mute)
1, 0x08, 0x2E, // Ch1 Volume ( 0xFF : Mute)
1, 0x09, 0x2E, // Ch2 Volume ( 0xFF : Mute)
1, 0x0A, 0x30,
1, 0x0B, 0x00,
1, 0x0E, 0x91,
1, 0x10, 0x02,
1, 0x11, 0xAC,
1, 0x12, 0x54,
1, 0x13, 0xAC,
1, 0x14, 0x54,
1, 0x10, 0x02,
1, 0x19, 0x30,
1, 0x1B, 0x00,
1, 0x1C, 0x02,
4, 0x20, 0x00, 0x01, 0x77, 0x72,
4, 0x25, 0x01, 0x02, 0x13, 0x45,
// Biquads
4, 0x50, 0x00, 0x00, 0x00, 0x00,
20, 0x29, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
........................
};
struct REG_TAB ad82120_spk_reg_cfg[] = {
//Register_Table
{0x00,0x00}, //##State_Control_1
{0x01,0xA2}, //##State_Control_2
{0x02,0x00}, //##State_Control_3
{0x03,0x0C}, //##Master_volume_control
{0x04,0x18}, //##Channel_1_volume_control
{0x05,0x18}, //##Channel_2_volume_control
{0x06,0x18}, //##Channel_3_volume_control
......................
{0x65,0x00}, //##Channel2_EQ_bypass_3
};
struct AD82120_RAM_TAB ad82120_spk_ram_cfg[] = {
//RAM1 TABLE
{0x00,0x0C,0x05,0xBA,0x6D},//##Channel_1_A1_EQ1
{0x01,0x01,0xFD,0x22,0xCA},//##Channel_1_A2_EQ1
{0x02,0x03,0xFA,0x41,0x8E},//##Channel_1_B1_EQ1
{0x03,0x0E,0x05,0xB6,0x68},//##Channel_1_B2_EQ1
{0x04,0x01,0xFD,0x22,0xCA},//##Channel_1_A0_EQ1
.....................
{0x9E,0x00,0x04,0x00,0x00},//##AGC ALPHA
};
struct REG_TAB ad82088_spk_reg_cfg[] = {
//Register_Table
{0x00,0x04}, //State_Control_1
{0x01,0x81}, //State_Control_2
{0x02,0x00}, //State_Control_3
{0x03,0x09}, //Master_volume_control
{0x04,0x18}, //Channel_1_volume_control
{0x05,0x18}, //Channel_2_volume_control
{0x06,0x18}, //Channel_3_volume_control
.......................
{0x82,0x0c}, //Minimum_duty_test
//0x83 ~ 0x85 : Reserved
};
struct AD82088_RAM_TAB ad82088_spk_ram_cfg[] = {
//Ram1_Table
{0x00,0xc0,0x4c,0x6a}, //Channel_1_EQ1_A1
{0x01,0x1f,0xd9,0xcb}, //Channel_1_EQ1_A2
{0x02,0x3f,0xb3,0x6a}, //Channel_1_EQ1_B1
{0x03,0xe0,0x4c,0x3d}, //Channel_1_EQ1_B2
{0x04,0x1f,0xd9,0xcb}, //Channel_1_EQ1_A0
{0x05,0xc0,0xba,0xc5}, //Channel_1_EQ2_A1
{0x06,0x1f,0x5f,0x51}, //Channel_1_EQ2_A2
.................
{0x7b,0x20,0x00,0x00}, //Channel_1_DEQ4_A0
//0x7c ~ 0x7f : Reserved
//Ram2_Mode
{0x00,0xc0,0x4c,0x6a}, //Channel_2_EQ1_A1
{0x01,0x1f,0xd9,0xcb}, //Channel_2_EQ1_A2
{0x02,0x3f,0xb3,0x6a}, //Channel_2_EQ1_B1
{0x03,0xe0,0x4c,0x3d}, //Channel_2_EQ1_B2
{0x04,0x1f,0xd9,0xcb}, //Channel_2_EQ1_A0
{0x05,0xc0,0xba,0xc5}, //Channel_2_EQ2_A1
{0x06,0x1f,0x5f,0x51}, //Channel_2_EQ2_A2
............................
{0x7b,0x20,0x00,0x00}, //Channel_2_DEQ4_A0
//0x7c ~ 0x7f : Reserved
};
/*============================================================================
* SUB 默认配置(初始与 SPK 相同,需要时可替换为独立数据)
============================================================================*/
/* ACM8625 SUB 默认配置 - 与 SPK 共用 */
#define acm8625_sub_dsp_cfg acm8625_spk_dsp_cfg
#define ACM8625_SUB_DSP_CFG_COUNT ACM8625_SPK_DSP_CFG_COUNT
/* TAS5731 SUB 默认配置 - 与 SPK 共用 */
#define tas5731_sub_dsp_cfg tas5731_spk_dsp_cfg
#define TAS5731_SUB_DSP_CFG_SIZE TAS5731_SPK_DSP_CFG_SIZE
/* AD82120 SUB 默认配置 - 与 SPK 共用 */
#define ad82120_sub_reg_cfg ad82120_spk_reg_cfg
#define AD82120_SUB_REG_CFG_COUNT AD82120_SPK_REG_CFG_COUNT
#define ad82120_sub_ram_cfg ad82120_spk_ram_cfg
#define AD82120_SUB_RAM_CFG_COUNT AD82120_SPK_RAM_CFG_COUNT
/* AD82088 SUB 默认配置 - 与 SPK 共用 */
#define ad82088_sub_reg_cfg ad82088_spk_reg_cfg
#define AD82088_SUB_REG_CFG_COUNT AD82088_SPK_REG_CFG_COUNT
#define ad82088_sub_ram_cfg ad82088_spk_ram_cfg
#define AD82088_SUB_RAM_CFG_COUNT AD82088_SPK_RAM_CFG_COUNT
#endif /* __SKG_AMP_REGISTERS_H__ */
设备树配置举例
/* 单芯片(默认 spk) */
skg_amp_spk@2e {
compatible = "ebsw, skg_amp";
reg = <0x2e>;
mute-gpios = <&gpio4 RK_PA3 GPIO_ACTIVE_HIGH>;
clocks = <&mclkout_i2s0>;
clock-names = "mclk";
status = "okay";
};
/* 双芯片 */
skg_amp_spk@6a {
compatible = "ebsw, skg_amp";
reg = <0x6a>;
skg,amp-role = "spk";
...
};
skg_amp_sub@6b {
compatible = "ebsw, skg_amp";
reg = <0x6b>;
skg,amp-role = "sub";
no_mclk;
...
};
四、新增功放芯片示例
以新增虚拟芯片 XYZ9999 为例(I2C 地址 0x50,ID 寄存器 0x00,ID 值 0x99,有 REG + RAM 数据)。
第一步:skg_amp_registers.h
添加芯片 ID 宏、RAM 寄存器地址、默认配置表、SUB 宏别名。
第二步:skg_amp.h
enum amp_chip_type 中添加 CHIP_XYZ9999。
第三步:skg_amp.c
定义实现操作集并加入列表
static const struct amp_chip_ops xyz9999_ops = {
.name = "XYZ9999",
.chip_id = XYZ9999_CHIP_ID,
.chip_type = CHIP_XYZ9999,
.init = xyz9999_init,
.shutdown = xyz9999_shutdown,
.set_mute = xyz9999_set_mute,
.get_mute = xyz9999_get_mute,
.write_bulk = xyz9999_write_bulk,
.write_ram = xyz9999_write_ram, /* 无 RAM 则不填 */
};
/* 芯片操作集列表 */
static const struct amp_chip_ops *chip_ops_list[] = {
&acm8625_ops,
&ad82120_ops,
&tas5731_ops,
&ad82088_ops,
&xyz9999_ops, //新增
NULL
};

796

被折叠的 条评论
为什么被折叠?



