RK3588兼容多款功放驱动框架(Audio)

一、简介

本文讲述的多功放兼容驱动框架是一套基于 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
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值