eac1ca22bd
NOTE: this commit does not introduce any change, ideally even the binary should be almost the same. I checked the disassembly by hand and there are only a few differences here and there, mostly the compiler decides to compile very close expressions slightly differently. I tried to run the new code on several targets to make sure and saw no difference. The major syntax changes of the new headers are as follows: - BF_{WR,SET,CLR} are now superpowerful and allows to set several fileds at once: BF_WR(reg, field1(value1), field2(value2), ...) - BF_CS (use like BF_WR) does a write to reg_CLR and then reg_SET instead of RMW - there is no more need for macros like BF_{WR_,SET,CLR}_V, since one can simply BF_WR with field_V(name) - the old BF_SETV macro has no trivial equivalent and is replaced with its its equivalent for BF_WR(reg_SET, ...) I also rename the register headers: "regs/regs-x.h" -> "regs/x.h" to avoid the redundant "regs". Final note: the registers were generated using the following command: ./headergen_v2 -g imx -o ../../firmware/target/arm/imx233/regs/ desc/regs-stmp3{600,700,780}.xml Change-Id: I7485e8b4315a0929a8edb63e7fa1edcaa54b1edc
220 lines
7.4 KiB
C
220 lines
7.4 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2011 by Amaury Pouly
|
|
*
|
|
* 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 "audioin-imx233.h"
|
|
#include "pcm_sampr.h"
|
|
#include "string.h"
|
|
|
|
#include "regs/audioin.h"
|
|
/* some audioout registers impact audioin */
|
|
#include "regs/audioout.h"
|
|
|
|
/* values in half-dB, one for each setting */
|
|
static int audioin_vol[2][4]; /* 0=left, 1=right */
|
|
static int audioin_select[2]; /* idem */
|
|
|
|
void imx233_audioin_preinit(void)
|
|
{
|
|
/* Enable AUDIOIN block */
|
|
imx233_reset_block(&HW_AUDIOIN_CTRL);
|
|
/* Set word-length to 16-bit */
|
|
BF_SET(AUDIOIN_CTRL, WORD_LENGTH);
|
|
/* Gate Off */
|
|
BF_SET(AUDIOIN_CTRL, CLKGATE);
|
|
}
|
|
|
|
void imx233_audioin_postinit(void)
|
|
{
|
|
}
|
|
|
|
void imx233_audioin_open(void)
|
|
{
|
|
/* Gate On */
|
|
BF_CLR(AUDIOIN_CTRL, CLKGATE);
|
|
/* Enable ADC clock */
|
|
BF_CLR(AUDIOIN_ANACLKCTRL, CLKGATE);
|
|
/* Power up ADC (WARNING audioout register) */
|
|
BF_CLR(AUDIOOUT_PWRDN, ADC);
|
|
/* Start ADC */
|
|
BF_SET(AUDIOIN_CTRL, RUN);
|
|
}
|
|
|
|
void imx233_audioin_close(void)
|
|
{
|
|
/* Stop ADC (doc says it gate off the module but that's not the case) */
|
|
BF_CLR(AUDIOIN_CTRL, RUN);
|
|
/* Disable ADC clock */
|
|
BF_SET(AUDIOIN_ANACLKCTRL, CLKGATE);
|
|
/* Power down ADC (WARNING audioout register) */
|
|
BF_SET(AUDIOOUT_PWRDN, ADC);
|
|
/* Gate Off */
|
|
BF_SET(AUDIOIN_CTRL, CLKGATE);
|
|
}
|
|
|
|
static void apply_config(void)
|
|
{
|
|
int select_l = audioin_select[0];
|
|
int select_r = audioin_select[1];
|
|
int vol_l = audioin_vol[0][select_l];
|
|
int vol_r = audioin_vol[1][select_r];
|
|
/* Depending on the input, we have three available volumes to tweak:
|
|
* - adc volume: -100dB -> -0.5dB in 0.5dB steps
|
|
* - mux gain: 0dB -> 22.5dB in 1.5dB steps
|
|
* - mic gain: 0dB -> 40dB in 10dB steps (except for 10)
|
|
*
|
|
* This means two available volume ranges:
|
|
* - line1/line2/hp: -100dB -> 22dB in 0.5dB steps
|
|
* - microphone: -100dB -> 62dB in 0.5dB steps
|
|
*/
|
|
|
|
/* First apply mic gain if possible and necessary
|
|
* Only left volume is relevant with microphone
|
|
* If gain is > 22dB, use mic gain */
|
|
if(select_l == AUDIOIN_SELECT_MICROPHONE && vol_l > 22 * 2)
|
|
{
|
|
/* take lowest microphone gain to get back into the -100..22 range
|
|
* achievable with mux+adc.*/
|
|
|
|
/* from 52.5 dB and beyond: 40dB gain */
|
|
if(vol_l > 52 * 2)
|
|
{
|
|
BF_WR(AUDIOIN_MICLINE, MIC_GAIN_V(40dB));
|
|
vol_l -= 40 * 2;
|
|
}
|
|
/* from 42.5 dB to 52dB: 30dB gain */
|
|
else if(vol_l > 42 * 2)
|
|
{
|
|
BF_WR(AUDIOIN_MICLINE, MIC_GAIN_V(30dB));
|
|
vol_l -= 30 * 2;
|
|
}
|
|
/* from 22.5 dB to 42dB: 20dB gain */
|
|
else if(vol_l > 22 * 2)
|
|
{
|
|
BF_WR(AUDIOIN_MICLINE, MIC_GAIN_V(20dB));
|
|
vol_l -= 20 * 2;
|
|
}
|
|
/* otherwise 0dB gain */
|
|
else
|
|
BF_WR(AUDIOIN_MICLINE, MIC_GAIN_V(0dB));
|
|
}
|
|
/* max is 22dB */
|
|
vol_l = MIN(vol_l, 44);
|
|
vol_r = MIN(vol_r, 44);
|
|
/* we use the mux volume to reach the volume or higher with 1.5dB steps
|
|
* and then we use the ADC to go below 0dB or to obtain 0.5dB accuracy */
|
|
|
|
int mux_vol_l = MAX(0, (vol_l + 2) / 3); /* 1.5dB = 3 * 0.5dB */
|
|
int mux_vol_r = MAX(0, (vol_r + 2) / 3);
|
|
#if IMX233_SUBTARGET >= 3700
|
|
unsigned adc_zcd = BM_AUDIOIN_ADCVOL_EN_ADC_ZCD;
|
|
#else
|
|
unsigned adc_zcd = 0;
|
|
#endif
|
|
HW_AUDIOIN_ADCVOL = adc_zcd | BF_OR(AUDIOIN_ADCVOL, SELECT_LEFT(select_l),
|
|
SELECT_RIGHT(select_r), GAIN_LEFT(mux_vol_l), GAIN_RIGHT(mux_vol_r));
|
|
|
|
vol_l -= mux_vol_l * 3; /* mux vol is in 1.5dB = 3 * 0.5dB steps */
|
|
vol_r -= mux_vol_l * 3;
|
|
vol_l = MIN(MAX(-200, vol_l), -1);
|
|
vol_r = MIN(MAX(-200, vol_r), -1);
|
|
|
|
/* unmute, enable zero cross and set volume.
|
|
* 0xfe is -0.5dB */
|
|
BF_WR_ALL(AUDIOIN_ADCVOLUME, EN_ZCD(1),
|
|
VOLUME_LEFT(0xff + vol_l), VOLUME_RIGHT(0xff + vol_r));
|
|
}
|
|
|
|
void imx233_audioin_select_mux_input(bool right, int select)
|
|
{
|
|
audioin_select[right] = select;
|
|
apply_config();
|
|
}
|
|
|
|
void imx233_audioin_set_vol(bool right, int vol, int select)
|
|
{
|
|
audioin_vol[right][select] = vol;
|
|
apply_config();
|
|
}
|
|
|
|
void imx233_audioin_enable_mic(bool enable)
|
|
{
|
|
if(enable)
|
|
{
|
|
BF_WR(AUDIOIN_MICLINE, MIC_RESISTOR_V(2KOhm));
|
|
BF_WR(AUDIOIN_MICLINE, MIC_BIAS(4));
|
|
BF_WR(AUDIOIN_MICLINE, MIC_SELECT(1));
|
|
}
|
|
else
|
|
BF_WR(AUDIOIN_MICLINE, MIC_RESISTOR_V(Off));
|
|
}
|
|
|
|
void imx233_audioin_set_freq(int fsel)
|
|
{
|
|
static struct
|
|
{
|
|
int base_mult;
|
|
int src_hold;
|
|
int src_int;
|
|
int src_frac;
|
|
}dacssr[HW_NUM_FREQ] =
|
|
{
|
|
HW_HAVE_8_([HW_FREQ_8] = { 0x1, 0x3, 0x17, 0xe00 } ,)
|
|
HW_HAVE_11_([HW_FREQ_11] = { 0x1, 0x3, 0x11, 0x37 } ,)
|
|
HW_HAVE_12_([HW_FREQ_12] = { 0x1, 0x3, 0xf, 0x13ff },)
|
|
HW_HAVE_16_([HW_FREQ_16] = { 0x1, 0x1, 0x17, 0xe00},)
|
|
HW_HAVE_22_([HW_FREQ_22] = { 0x1, 0x1, 0x11, 0x37 },)
|
|
HW_HAVE_24_([HW_FREQ_24] = { 0x1, 0x1, 0xf, 0x13ff },)
|
|
HW_HAVE_32_([HW_FREQ_32] = { 0x1, 0x0, 0x17, 0xe00},)
|
|
HW_HAVE_44_([HW_FREQ_44] = { 0x1, 0x0, 0x11, 0x37 },)
|
|
HW_HAVE_48_([HW_FREQ_48] = { 0x1, 0x0, 0xf, 0x13ff },)
|
|
HW_HAVE_64_([HW_FREQ_64] = { 0x2, 0x0, 0x17, 0xe00},)
|
|
HW_HAVE_88_([HW_FREQ_88] = { 0x2, 0x0, 0x11, 0x37 },)
|
|
HW_HAVE_96_([HW_FREQ_96] = { 0x2, 0x0, 0xf, 0x13ff },)
|
|
};
|
|
|
|
BF_WR_ALL(AUDIOIN_ADCSRR,
|
|
SRC_FRAC(dacssr[fsel].src_frac), SRC_INT(dacssr[fsel].src_int),
|
|
SRC_HOLD(dacssr[fsel].src_hold), BASEMULT(dacssr[fsel].base_mult));
|
|
}
|
|
|
|
struct imx233_audioin_info_t imx233_audioin_get_info(void)
|
|
{
|
|
struct imx233_audioin_info_t info;
|
|
memset(&info, 0, sizeof(info));
|
|
/* 6*10^6*basemult/(src_frac*8*(src_hold+1)) in Hz */
|
|
info.freq = 60000000 * BF_RD(AUDIOIN_ADCSRR, BASEMULT) / 8 /
|
|
BF_RD(AUDIOIN_ADCSRR, SRC_FRAC) / (1 + BF_RD(AUDIOIN_ADCSRR, SRC_HOLD));
|
|
info.muxselect[0] = BF_RD(AUDIOIN_ADCVOL, SELECT_LEFT);
|
|
info.muxselect[1] = BF_RD(AUDIOIN_ADCVOL, SELECT_RIGHT);
|
|
/* convert half-dB to tenth-dB */
|
|
info.muxvol[0] = BF_RD(AUDIOIN_ADCVOL, GAIN_LEFT) * 15;
|
|
info.muxvol[1] = BF_RD(AUDIOIN_ADCVOL, GAIN_RIGHT) * 15;
|
|
info.muxmute[0] = info.adcmute[1] = BF_RD(AUDIOIN_ADCVOL, MUTE);
|
|
info.adcvol[0] = MAX((int)BF_RD(AUDIOIN_ADCVOLUME, VOLUME_LEFT) - 0xff, -100) * 5;
|
|
info.adcvol[1] = MAX((int)BF_RD(AUDIOIN_ADCVOLUME, VOLUME_RIGHT) - 0xff, -100) * 5;
|
|
info.adcmute[0] = info.adcmute[1] = false;
|
|
info.micvol[0] = BF_RD(AUDIOIN_MICLINE, MIC_GAIN);
|
|
if(info.micvol[0] != 0)
|
|
info.micvol[0] = info.micvol[1] = info.micvol[0] * 100 + 100;
|
|
info.micmute[0] = info.micmute[1] = false;
|
|
info.adc = !BF_RD(AUDIOOUT_PWRDN, ADC);
|
|
info.mic = info.mux = true;
|
|
return info;
|
|
}
|