Codec confiuguration implementation.

This commit is contained in:
George Norton 2023-06-12 14:03:39 +01:00
parent 9beeb23c89
commit eae22f28a8
5 changed files with 78 additions and 39 deletions

View File

@ -56,12 +56,12 @@ static const default_configuration default_config = {
.f4 = { HIGHSHELF, {0}, 8400, 2, 0.7 },
.f5 = { PEAKING, {0}, 4800, 3, 5 }
},
.preprocessing = { .header = { PREPROCESSING_CONFIGURATION, sizeof(default_config.preprocessing) }, -0.1f, false, {0} }
.preprocessing = { .header = { PREPROCESSING_CONFIGURATION, sizeof(default_config.preprocessing) }, -0.2f, false, {0} }
};
// Grab the last 4k page of flash for our configuration strutures.
#ifndef TEST_TARGET
static const size_t USER_CONFIGURATION_OFFSET = PICO_FLASH_SIZE_BYTES - 0x1000;
static const size_t USER_CONFIGURATION_OFFSET = PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE;
const uint8_t *user_configuration = (const uint8_t *) (XIP_BASE + USER_CONFIGURATION_OFFSET);
#endif
/**
@ -74,8 +74,6 @@ static uint8_t inactive_working_configuration = 0;
static uint8_t result_buffer[256] = { U16_TO_U8S_LE(NOK), U16_TO_U8S_LE(4) };
static bool reload_config = false;
static bool save_config = false;
static bool factory_reset = false;
static uint16_t write_offset = 0;
static uint16_t read_offset = 0;
@ -208,13 +206,20 @@ bool validate_configuration(tlv_header *config) {
break;
case PREPROCESSING_CONFIGURATION: {
preprocessing_configuration_tlv* preprocessing_config = (preprocessing_configuration_tlv*) tlv;
//printf("Preproc %0.2f %d\n", preprocessing_config->preamp, preprocessing_config->reverse_stereo);
if (tlv->length != sizeof(preprocessing_configuration_tlv)) {
printf("Preprocessing size missmatch: %u != %zu\n", tlv->length, sizeof(preprocessing_configuration_tlv));
return false;
}
break;
}
case PCM3060_CONFIGURATION: {
pcm3060_configuration_tlv* pcm3060_config = (pcm3060_configuration_tlv*) tlv;
if (tlv->length != sizeof(pcm3060_configuration_tlv)) {
printf("PCM3060 config size missmatch: %u != %zu\n", tlv->length, sizeof(pcm3060_configuration_tlv));
return false;
}
break;
}
default:
// Unknown TLVs are not invalid, just ignored.
break;
@ -253,6 +258,14 @@ bool apply_configuration(tlv_header *config) {
preprocessing.reverse_stereo = preprocessing_config->reverse_stereo;
break;
}
case PCM3060_CONFIGURATION: {
pcm3060_configuration_tlv* pcm3060_config = (pcm3060_configuration_tlv*) tlv;
audio_state.oversampling = pcm3060_config->oversampling;
audio_state.phase = pcm3060_config->phase;
audio_state.rolloff = pcm3060_config->rolloff;
audio_state.de_emphasis = pcm3060_config->de_emphasis;
break;
}
default:
break;
}
@ -275,15 +288,16 @@ void load_config() {
}
#ifndef TEST_TARGET
// Must be in RAM
uint8_t flash_buffer[FLASH_PAGE_SIZE];
bool __no_inline_not_in_flash_func(save_configuration)() {
const uint8_t active_configuration = inactive_working_configuration ? 0 : 1;
tlv_header* config = (tlv_header*) working_configuration[active_configuration];
if (validate_configuration(config)) {
power_down_dac();
const size_t config_length = config->length - (size_t)((size_t)config->value - (size_t)config);
// Write data to flash
uint8_t flash_buffer[FLASH_PAGE_SIZE];
flash_header_tlv* flash_header = (flash_header_tlv*) flash_buffer;
flash_header->header.type = FLASH_HEADER;
flash_header->header.length = sizeof(flash_header) + config_length;
@ -291,8 +305,6 @@ bool __no_inline_not_in_flash_func(save_configuration)() {
flash_header->version = CONFIG_VERSION;
memcpy((void*)(flash_header->tlvs), config->value, config_length);
power_down_dac();
uint32_t ints = save_and_disable_interrupts();
flash_range_erase(USER_CONFIGURATION_OFFSET, FLASH_SECTOR_SIZE);
flash_range_program(USER_CONFIGURATION_OFFSET, flash_buffer, FLASH_PAGE_SIZE);
@ -305,6 +317,15 @@ bool __no_inline_not_in_flash_func(save_configuration)() {
return false;
}
bool __no_inline_not_in_flash_func(factory_reset)() {
power_down_dac();
uint32_t ints = save_and_disable_interrupts();
flash_range_erase(USER_CONFIGURATION_OFFSET, FLASH_SECTOR_SIZE);
restore_interrupts(ints);
power_up_dac();
return true;
}
bool process_cmd(tlv_header* cmd) {
tlv_header* result = ((tlv_header*) result_buffer);
switch (cmd->type) {
@ -318,8 +339,7 @@ bool process_cmd(tlv_header* cmd) {
}
break;
case SAVE_CONFIGURATION: {
if (cmd->length == 4) {
save_config = true;
if (cmd->length == 4 && save_configuration()) {
result->type = OK;
result->length = 4;
return true;
@ -327,8 +347,7 @@ bool process_cmd(tlv_header* cmd) {
break;
}
case FACTORY_RESET: {
if (cmd->length == 4) {
factory_reset = true;
if (cmd->length == 4 && factory_reset()) {
flash_header_tlv flash_header = { 0 };
result->type = OK;
result->length = 4;
@ -411,21 +430,6 @@ void config_in_packet(struct usb_endpoint *ep) {
usb_packet_done(ep);
}
void apply_core0_config() {
if (save_config) {
save_config = false;
save_configuration();
}
if (factory_reset) {
factory_reset = false;
power_down_dac();
uint32_t ints = save_and_disable_interrupts();
flash_range_erase(USER_CONFIGURATION_OFFSET, FLASH_SECTOR_SIZE);
restore_interrupts(ints);
power_up_dac();
}
}
void apply_core1_config() {
if (reload_config) {
reload_config = false;

View File

@ -41,7 +41,6 @@ struct usb_endpoint;
void config_in_packet(struct usb_endpoint *ep);
void config_out_packet(struct usb_endpoint *ep);
extern void load_config();
extern void apply_core0_config();
extern void apply_core1_config();
#endif // CONFIGURATION_MANAGER_H

View File

@ -96,6 +96,15 @@ typedef struct __attribute__((__packed__)) _filter_configuration_tlv {
const uint8_t filters[];
} filter_configuration_tlv;
typedef struct __attribute__((__packed__)) _pcm3060_configuration_tlv {
tlv_header header;
const uint8_t oversampling;
const uint8_t phase;
const uint8_t rolloff;
const uint8_t de_emphasis;
} pcm3060_configuration_tlv;
typedef struct __attribute__((__packed__)) _version_status_tlv {
tlv_header header;
uint16_t current_version;

View File

@ -51,6 +51,7 @@ static uint8_t *userbuf;
audio_state_config audio_state = {
.freq = 48000,
.de_emphasis_frequency = 0x1, // 48khz
};
preprocessing_config preprocessing = {
@ -97,8 +98,6 @@ static void _as_audio_packet(struct usb_endpoint *ep) {
int32_t *out = (int32_t *) userbuf;
int samples = usb_buffer->data_len / 2;
// Update filters if required
apply_core0_config();
if (preprocessing.reverse_stereo) {
for (int i = 0; i < samples; i+=2) {
@ -152,6 +151,15 @@ static void update_volume()
audio_state._volume = audio_state._target_volume;
}
if (audio_state.pcm3060_registers != audio_state._target_pcm3060_registers) {
uint8_t buf[3];
buf[0] = 68; // register addr
buf[1] = audio_state.target_pcm3060_registers[0]; // Reg 68
buf[2] = audio_state.target_pcm3060_registers[1]; // Reg 69
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 3, false);
audio_state.pcm3060_registers = audio_state._target_pcm3060_registers;
}
}
void core1_entry() {
@ -589,7 +597,7 @@ static bool do_get_current(struct usb_setup_packet *setup) {
if ((setup->bmRequestType & USB_REQ_TYPE_RECIPIENT_MASK) == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (setup->wValue >> 8u) {
case 1: { // mute
usb_start_tiny_control_in_transfer(audio_state.mute, 1);
usb_start_tiny_control_in_transfer((audio_state.mute != 0), 1);
return true;
}
case 2: { // volume
@ -705,11 +713,7 @@ static void audio_cmd_packet(struct usb_endpoint *ep) {
if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
switch (audio_control_cmd_t.cs) {
case 1: { // mute
audio_state.mute = buffer->data[0];
uint8_t buf[2];
buf[0] = 68; // register addr
buf[1] = buffer->data[0] ? 0x3 : 0x0; // data
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 2, false);
audio_state.mute = buffer->data[0] ? 0x3 : 0x0;
break;
}
case 2: { // volume
@ -741,7 +745,11 @@ static const struct usb_transfer_type _audio_cmd_transfer_type = {
static bool as_set_alternate(struct usb_interface *interface, uint alt) {
assert(interface == &as_op_interface);
return alt < 2;
switch (alt) {
case 0: power_down_dac(); return true;
case 1: power_up_dac(); return true;
default: return false;
}
}
static bool do_set_current(struct usb_setup_packet *setup) {

View File

@ -56,7 +56,26 @@ typedef struct _audio_state_config {
int16_t target_volume[2];
int32_t _target_volume;
};
bool mute;
union {
struct {
// Register 68
uint8_t mute: 2;
uint8_t phase: 1;
uint8_t reserved1: 3;
uint8_t oversampling: 1;
uint8_t reserved2: 1;
// Register 69
uint8_t zero_fn: 1;
uint8_t zero_polarity: 1;
uint8_t reserved3: 2;
uint8_t de_emphasis: 1;
uint8_t de_emphasis_frequency: 2;
uint8_t rolloff: 1;
};
int8_t target_pcm3060_registers[2];
int16_t _target_pcm3060_registers;
};
int16_t pcm3060_registers;
} audio_state_config;
extern audio_state_config audio_state;