Compare commits

...

3 commits

Author SHA1 Message Date
Aidan MacDonald
3a70003d23 echoplayer: fix STD_CONTEXT action being masked by STD_OK in keymap
Change-Id: I4e7546c0817b027d04ac12ba151dfca764915c6f
2026-02-26 18:15:24 -05:00
Aidan MacDonald
df89a47c11 drivers: add TLV320AIC3104 codec driver
Change-Id: Ic8825fc8f057c28316e9f7cb6af3dd34e8200d48
2026-02-26 15:00:13 +00:00
Aidan MacDonald
33cdbf87d7 echoplayer: add echoplayer-specific codec header
Remove the generic TLV320AIC3104 codec header. The codec
is sufficiently complex that a one-size-fits-all driver
isn't really feasible, eg. due to different clocking and
output configurations.

It's easier for targets to have their own audio headers
tailored to their use case than a generic header with
lots of ifdefs.

Change-Id: I63d92d57c28ddd7da7aa3174bd583d8afb1aa56d
2026-02-26 15:00:13 +00:00
7 changed files with 303 additions and 10 deletions

View file

@ -33,7 +33,7 @@ static const struct button_mapping button_context_standard[] = {
{ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE}, {ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_OK, BUTTON_RIGHT, BUTTON_NONE}, {ACTION_STD_OK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
{ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE}, {ACTION_STD_CANCEL, BUTTON_LEFT, BUTTON_NONE},
{ACTION_STD_CONTEXT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, {ACTION_STD_CONTEXT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_QUICKSCREEN, BUTTON_B, BUTTON_NONE}, {ACTION_STD_QUICKSCREEN, BUTTON_B, BUTTON_NONE},

View file

@ -585,6 +585,8 @@ drivers/audio/cs42l55.c
drivers/audio/rk27xx_codec.c drivers/audio/rk27xx_codec.c
#elif defined(HAVE_AIC3X) #elif defined(HAVE_AIC3X)
drivers/audio/aic3x.c drivers/audio/aic3x.c
#elif defined(HAVE_AIC310X)
drivers/audio/aic310x.c
#elif defined (HAVE_DUMMY_CODEC) #elif defined (HAVE_DUMMY_CODEC)
drivers/audio/dummy_codec.c drivers/audio/dummy_codec.c
#elif defined (HAVE_DF1704_CODEC) #elif defined (HAVE_DF1704_CODEC)

View file

@ -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;
}

177
firmware/export/aic310x.h Normal file
View file

@ -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__ */

View file

@ -241,8 +241,8 @@ struct sound_settings_info
#include "fiiolinux_codec.h" #include "fiiolinux_codec.h"
#elif defined(HAVE_EROSQ_LINUX_CODEC) #elif defined(HAVE_EROSQ_LINUX_CODEC)
#include "erosqlinux_codec.h" #include "erosqlinux_codec.h"
#elif defined(HAVE_TLV320AIC3104) #elif defined(HAVE_ECHOPLAYER_CODEC)
#include "tlv320aic3104_codec.h" #include "echoplayer_codec.h"
#elif defined(HAVE_HIBY_LINUX_CODEC) #elif defined(HAVE_HIBY_LINUX_CODEC)
#include "hibylinux_codec.h" #include "hibylinux_codec.h"
#endif #endif

View file

@ -54,7 +54,7 @@
#define INPUT_SRC_CAPS SRC_CAP_MIC #define INPUT_SRC_CAPS SRC_CAP_MIC
#define AUDIOHW_CAPS MIC_GAIN_CAP #define AUDIOHW_CAPS MIC_GAIN_CAP
#define HAVE_RECORDING #define HAVE_RECORDING
#define HAVE_TLV320AIC3104 // TODO: Sansa connect uses the AIC3106, possible code sharing? #define HAVE_ECHOPLAYER_CODEC
#define HAVE_SW_TONE_CONTROLS #define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL #define HAVE_SW_VOLUME_CONTROL
#define DEFAULT_REC_MIC_GAIN 12 #define DEFAULT_REC_MIC_GAIN 12

View file

@ -18,12 +18,14 @@
* KIND, either express or implied. * KIND, either express or implied.
* *
****************************************************************************/ ****************************************************************************/
#ifndef __ECHOPLAYER_CODEC_H__
#define __ECHOPLAYER_CODEC_H__
#ifndef __TLV320AIC3104_CODEC__ /* -79 to 0 dB in 0.5 dB steps; software volume control
#define __TLV320AIC3104_CODEC__ * is used because the hardware volume controls "click"
* when changing the volume */
AUDIOHW_SETTING(VOLUME, "dB", 1, 5, -790, 0, -200);
// FIXME: range AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, 0, 63, 12);
AUDIOHW_SETTING(VOLUME, "dB", 0, 2, -74, -2, -40)
AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, 0, 63, 12)
#endif /* __TLV320AIC3104_CODEC__ */ #endif /* __ECHOPLAYER_CODEC_H__ */