drivers: add TLV320AIC3104 codec driver

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit df89a47c11d4d7db368f0536c0df18e87e5791fc
Author: Aidan MacDonald <[email protected]>
Date:   Tue Feb 17 11:05:55 2026 +0000

    drivers: add TLV320AIC3104 codec driver
    
    Change-Id: Ic8825fc8f057c28316e9f7cb6af3dd34e8200d48

diff --git a/firmware/SOURCES b/firmware/SOURCES
index 195b5e0caa..8ff0c20a9c 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -585,6 +585,8 @@ drivers/audio/cs42l55.c
 drivers/audio/rk27xx_codec.c
 #elif defined(HAVE_AIC3X)
 drivers/audio/aic3x.c
+#elif defined(HAVE_AIC310X)
+drivers/audio/aic310x.c
 #elif defined (HAVE_DUMMY_CODEC)
 drivers/audio/dummy_codec.c
 #elif defined (HAVE_DF1704_CODEC)
diff --git a/firmware/drivers/audio/aic310x.c b/firmware/drivers/audio/aic310x.c
new file mode 100644
index 0000000000..3f96f0b2a0
--- /dev/null
+++ b/firmware/drivers/audio/aic310x.c
@@ -0,0 +1,112 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2026 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "aic310x.h"
+#include "system.h"
+
+/* Call with mutex held */
+static int aic310x_set_page(struct aic310x *codec, uint8_t page)
+{
+    if (codec->current_page == page)
+        return 0;
+
+    codec->current_page = page;
+
+    int err = codec->write_multiple(AIC310X_PAGE_SELECT, &codec->current_page, 1);
+    if (err)
+    {
+        /* Clear current page on error */
+        codec->current_page = 0xFF;
+    }
+
+    return err;
+}
+
+static int aic310x_readwrite_multiple(struct aic310x *codec, bool write,
+                                      uint8_t reg, uint8_t *data, size_t count)
+{
+    mutex_lock(&codec->lock);
+
+    uint8_t reg_page = AIC310X_REG_PAGE(reg);
+    uint8_t reg_addr = AIC310X_REG_ADDR(reg);
+
+    int err = aic310x_set_page(codec, reg_page);
+    if (err)
+        goto out;
+
+    if (write)
+        err = codec->write_multiple(reg_addr, data, count);
+    else
+        err = codec->read_multiple(reg_addr, data, count);
+
+out:
+    mutex_unlock(&codec->lock);
+    return err;
+}
+
+void aic310x_init(struct aic310x *codec)
+{
+    mutex_init(&codec->lock);
+
+    /* ensure we issue a page select before any register access */
+    codec->current_page = 0xFF;
+}
+
+int aic310x_read_multiple(struct aic310x *codec,
+                                uint8_t reg, uint8_t *data, size_t count)
+{
+    return aic310x_readwrite_multiple(codec, false, reg, data, count);
+}
+
+int aic310x_write_multiple(struct aic310x *codec,
+                           uint8_t reg, const uint8_t *data, size_t count)
+{
+    return aic310x_readwrite_multiple(codec, true, reg, (uint8_t *)data, count);
+}
+
+int aic310x_read(struct aic310x *codec, uint8_t reg, uint8_t *value)
+{
+    return aic310x_read_multiple(codec, reg, value, 1);
+}
+
+int aic310x_write(struct aic310x *codec, uint8_t reg, uint8_t value)
+{
+    return aic310x_write_multiple(codec, reg, &value, 1);
+}
+
+int aic310x_modify(struct aic310x *codec,
+                   uint8_t reg, uint8_t mask, uint8_t newvalue)
+{
+    mutex_lock(&codec->lock);
+
+    uint8_t tmp;
+    int err = aic310x_read(codec, reg, &tmp);
+    if (err)
+        goto out;
+
+    tmp &= ~mask;
+    tmp |= newvalue;
+
+    err = aic310x_write(codec, reg, tmp);
+
+out:
+    mutex_unlock(&codec->lock);
+    return err;
+}
diff --git a/firmware/export/aic310x.h b/firmware/export/aic310x.h
new file mode 100644
index 0000000000..dfb514c6cb
--- /dev/null
+++ b/firmware/export/aic310x.h
@@ -0,0 +1,177 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2026 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __AIC310X_H__
+#define __AIC310X_H__
+
+#include "audiohw.h"
+#include "mutex.h"
+#include <stdint.h>
+#include <stddef.h>
+
+/* I2C address (7-bit address without r/w bit) */
+#define AIC310X_I2C_ADDR                    0x18
+
+/* Use bit 7 as page indicator */
+#define AIC310X_REG_PAGE_MASK               0x80
+#define AIC310X_REG_ADDR_MASK               0x7F
+
+/* Get register address / page number */
+#define AIC310X_REG_PAGE(x)                 ((x) & AIC310X_REG_PAGE_MASK ? 1 : 0)
+#define AIC310X_REG_ADDR(x)                 ((x) & AIC310X_REG_ADDR_MASK)
+
+/* Create page register address */
+#define AIC310X_PAGE0_REG(x)                (x)
+#define AIC310X_PAGE1_REG(x)                ((x) | AIC310X_REG_PAGE_MASK)
+
+/* Page 0 registers */
+#define AIC310X_PAGE_SELECT                 AIC310X_PAGE0_REG(0)
+#define AIC310X_SW_RESET                    AIC310X_PAGE0_REG(1)
+#define AIC310X_SAMPLE_RATE_SELECT          AIC310X_PAGE0_REG(2)
+#define AIC310X_PLL_A                       AIC310X_PAGE0_REG(3)
+#define AIC310X_PLL_B                       AIC310X_PAGE0_REG(4)
+#define AIC310X_PLL_C                       AIC310X_PAGE0_REG(5)
+#define AIC310X_PLL_D                       AIC310X_PAGE0_REG(6)
+#define AIC310X_DATA_PATH                   AIC310X_PAGE0_REG(7)
+#define AIC310X_INTERFACE_CONTROL_A         AIC310X_PAGE0_REG(8)
+#define AIC310X_INTERFACE_CONTROL_B         AIC310X_PAGE0_REG(9)
+#define AIC310X_INTERFACE_CONTROL_C         AIC310X_PAGE0_REG(10)
+#define AIC310X_OVERFLOW_FLAG               AIC310X_PAGE0_REG(11)
+#define AIC310X_DIGITAL_FILTER_CONTROL      AIC310X_PAGE0_REG(12)
+#define AIC310X_HEADSET_DETECT_A            AIC310X_PAGE0_REG(13)
+#define AIC310X_HEADSET_DETECT_B            AIC310X_PAGE0_REG(14)
+#define AIC310X_LEFT_PGA_GAIN               AIC310X_PAGE0_REG(15)
+#define AIC310X_RIGHT_PGA_GAIN              AIC310X_PAGE0_REG(16)
+#define AIC310X_MIC2_LEFT_ADC_CTRL          AIC310X_PAGE0_REG(17)
+#define AIC310X_MIC2_RIGHT_ADC_CTRL         AIC310X_PAGE0_REG(18)
+#define AIC310X_MIC1LP_LEFT_ADC_CTRL        AIC310X_PAGE0_REG(19)
+#define AIC310X_MIC1RP_LEFT_ADC_CTRL        AIC310X_PAGE0_REG(21)
+#define AIC310X_MIC1RP_RIGHT_ADC_CTRL       AIC310X_PAGE0_REG(22)
+#define AIC310X_MIC1LP_RIGHT_ADC_CTRL       AIC310X_PAGE0_REG(24)
+#define AIC310X_MICBIAS_CTRL                AIC310X_PAGE0_REG(25)
+#define AIC310X_LEFT_AGC_CTRL_A             AIC310X_PAGE0_REG(26)
+#define AIC310X_LEFT_AGC_CTRL_B             AIC310X_PAGE0_REG(27)
+#define AIC310X_LEFT_AGC_CTRL_C             AIC310X_PAGE0_REG(28)
+#define AIC310X_RIGHT_AGC_CTRL_A            AIC310X_PAGE0_REG(29)
+#define AIC310X_RIGHT_AGC_CTRL_B            AIC310X_PAGE0_REG(30)
+#define AIC310X_RIGHT_AGC_CTRL_C            AIC310X_PAGE0_REG(31)
+#define AIC310X_LEFT_AGC_GAIN               AIC310X_PAGE0_REG(32)
+#define AIC310X_RIGHT_AGC_GAIN              AIC310X_PAGE0_REG(33)
+#define AIC310X_LEFT_AGC_NOISE_GATE         AIC310X_PAGE0_REG(34)
+#define AIC310X_RIGHT_AGC_NOISE_GATE        AIC310X_PAGE0_REG(35)
+#define AIC310X_ADC_FLAG                    AIC310X_PAGE0_REG(36)
+#define AIC310X_DAC_POWER_CTRL              AIC310X_PAGE0_REG(37)
+#define AIC310X_HPOUT_DRIVER_CTRL           AIC310X_PAGE0_REG(38)
+#define AIC310X_HPOUT_STAGE_CTRL            AIC310X_PAGE0_REG(40)
+#define AIC310X_DAC_SWITCHING_CTRL          AIC310X_PAGE0_REG(41)
+#define AIC310X_DRIVER_POP_REDUCTION        AIC310X_PAGE0_REG(42)
+#define AIC310X_LEFT_DAC_DIGITAL_VOLUME     AIC310X_PAGE0_REG(43)
+#define AIC310X_RIGHT_DAC_DIGITAL_VOLUME    AIC310X_PAGE0_REG(44)
+#define AIC310X_PGA_L_TO_HPLOUT_VOLUME      AIC310X_PAGE0_REG(46)
+#define AIC310X_DAC_L1_TO_HPLOUT_VOLUME     AIC310X_PAGE0_REG(47)
+#define AIC310X_PGA_R_TO_HPLOUT_VOLUME      AIC310X_PAGE0_REG(49)
+#define AIC310X_DAC_R1_TO_HPLOUT_VOLUME     AIC310X_PAGE0_REG(50)
+#define AIC310X_HPLOUT_LEVEL_CONTROL        AIC310X_PAGE0_REG(51)
+#define AIC310X_PGA_L_TO_HPLCOM_VOLUME      AIC310X_PAGE0_REG(53)
+#define AIC310X_DAC_L1_TO_HPLCOM_VOLUME     AIC310X_PAGE0_REG(54)
+#define AIC310X_PGA_R_TO_HPLCOM_VOLUME      AIC310X_PAGE0_REG(56)
+#define AIC310X_DAC_R1_TO_HPLCOM_VOLUME     AIC310X_PAGE0_REG(57)
+#define AIC310X_HPLCOM_LEVEL_CONTROL        AIC310X_PAGE0_REG(58)
+#define AIC310X_PGA_L_TO_HPROUT_VOLUME      AIC310X_PAGE0_REG(60)
+#define AIC310X_DAC_L1_TO_HPROUT_VOLUME     AIC310X_PAGE0_REG(61)
+#define AIC310X_PGA_R_TO_HPROUT_VOLUME      AIC310X_PAGE0_REG(63)
+#define AIC310X_DAC_R1_TO_HPROUT_VOLUME     AIC310X_PAGE0_REG(64)
+#define AIC310X_HPROUT_LEVEL_CONTROL        AIC310X_PAGE0_REG(65)
+#define AIC310X_PGA_L_TO_HPRCOM_VOLUME      AIC310X_PAGE0_REG(67)
+#define AIC310X_DAC_L1_TO_HPRCOM_VOLUME     AIC310X_PAGE0_REG(68)
+#define AIC310X_PGA_R_TO_HPRCOM_VOLUME      AIC310X_PAGE0_REG(70)
+#define AIC310X_DAC_R1_TO_HPRCOM_VOLUME     AIC310X_PAGE0_REG(71)
+#define AIC310X_HPRCOM_LEVEL_CONTROL        AIC310X_PAGE0_REG(72)
+#define AIC310X_PGA_L_TO_LEFT_LO_VOLUME     AIC310X_PAGE0_REG(81)
+#define AIC310X_DAC_L1_TO_LEFT_LO_VOLUME    AIC310X_PAGE0_REG(82)
+#define AIC310X_PGA_R_TO_LEFT_LO_VOLUME     AIC310X_PAGE0_REG(84)
+#define AIC310X_DAC_R1_TO_LEFT_LO_VOLUME    AIC310X_PAGE0_REG(85)
+#define AIC310X_LEFT_LO_LEVEL_CONTROL       AIC310X_PAGE0_REG(86)
+#define AIC310X_PGA_L_TO_RIGHT_LO_VOLUME    AIC310X_PAGE0_REG(88)
+#define AIC310X_DAC_L1_TO_RIGHT_LO_VOLUME   AIC310X_PAGE0_REG(89)
+#define AIC310X_PGA_R_TO_RIGHT_LO_VOLUME    AIC310X_PAGE0_REG(91)
+#define AIC310X_DAC_R1_TO_RIGHT_LO_VOLUME   AIC310X_PAGE0_REG(92)
+#define AIC310X_RIGHT_LO_LEVEL_CONTROL      AIC310X_PAGE0_REG(93)
+#define AIC310X_MODULE_PWR_STATUS           AIC310X_PAGE0_REG(94)
+#define AIC310X_SHORT_CIRCUIT_STATUS        AIC310X_PAGE0_REG(95)
+#define AIC310X_STICKY_INTERRUPT_FLAGS      AIC310X_PAGE0_REG(96)
+#define AIC310X_REAL_TIME_INTERRUPT_FLAGS   AIC310X_PAGE0_REG(97)
+#define AIC310X_CLOCK                       AIC310X_PAGE0_REG(101)
+#define AIC310X_CLOCK_GEN_CTRL              AIC310X_PAGE0_REG(102)
+#define AIC310X_LEFT_AGC_ATTACK_TIME        AIC310X_PAGE0_REG(103)
+#define AIC310X_LEFT_AGC_DECAY_TIME         AIC310X_PAGE0_REG(104)
+#define AIC310X_RIGHT_AGC_ATTACK_TIME       AIC310X_PAGE0_REG(105)
+#define AIC310X_RIGHT_AGC_DECAY_TIME        AIC310X_PAGE0_REG(106)
+#define AIC310X_ADC_PATH_AND_I2C_BUS        AIC310X_PAGE0_REG(107)
+#define AIC310X_BYPASS_SEL                  AIC310X_PAGE0_REG(108)
+#define AIC310X_DAC_CURRENT_ADJUST          AIC310X_PAGE0_REG(109)
+
+/* Page 1 registers */
+#define AIC310X_LEFT_EFF_FILTER             AIC310X_PAGE1_REG(1)
+#define AIC310X_LEFT_DEEMPH_FILTER          AIC310X_PAGE1_REG(21)
+#define AIC310X_RIGHT_EFF_FILTER            AIC310X_PAGE1_REG(27)
+#define AIC310X_RIGHT_DEEMPH_FILTER         AIC310X_PAGE1_REG(47)
+#define AIC310X_3D_ATTEN_COEFF              AIC310X_PAGE1_REG(53)
+#define AIC310X_LEFT_ADC_HIGHPASS_FILTER    AIC310X_PAGE1_REG(65)
+#define AIC310X_RIGHT_ADC_HIGHPASS_FILTER   AIC310X_PAGE1_REG(71)
+
+struct aic310x
+{
+    /* Mutex to protect bus access */
+    struct mutex lock;
+
+    /* Cached page select register */
+    uint8_t current_page;
+
+    /* I2C bus operations */
+    int (*read_multiple) (uint8_t reg, uint8_t *data, size_t count);
+    int (*write_multiple) (uint8_t reg, const uint8_t *data, size_t count);
+};
+
+void aic310x_init(struct aic310x *codec);
+
+/* Read/write multiple I2C registers */
+int aic310x_read_multiple(struct aic310x *codec,
+                          uint8_t reg, uint8_t *data, size_t count);
+int aic310x_write_multiple(struct aic310x *codec,
+                           uint8_t reg, const uint8_t *data, size_t count);
+
+/* Single I2C register read/write and modify operations */
+int aic310x_read(struct aic310x *codec, uint8_t reg, uint8_t *value);
+int aic310x_write(struct aic310x *codec, uint8_t reg, uint8_t value);
+int aic310x_modify(struct aic310x *codec, uint8_t reg, uint8_t mask, uint8_t newvalue);
+
+/* Lock/unlock the codec for performing multi-register operations */
+static inline void aic310x_lock(struct aic310x *codec)
+{
+    mutex_lock(&codec->lock);
+}
+
+static inline void aic310x_unlock(struct aic310x *codec)
+{
+    mutex_unlock(&codec->lock);
+}
+
+#endif /* __AIC310X_H__ */
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.