/** * aio_iecout_set_enable - setup IEC output via SoC glue * @chip: the AIO chip pointer * @enable: false to stop the output, true to start * * Set enabled or disabled S/PDIF signal output to out of SoC via AOnIEC pins. * This function need to call at driver startup. * * The regmap of SoC glue is specified by 'socionext,syscon' optional property * of DT. This function has no effect if no property.
*/ void aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable)
{ struct regmap *r = chip->regmap_sg;
if (!r) return;
regmap_write(r, SG_AOUTEN, (enable) ? ~0 : 0);
}
/** * aio_chip_set_pll - set frequency to audio PLL * @chip: the AIO chip pointer * @pll_id: PLL * @freq: frequency in Hz, 0 is ignored * * Sets frequency of audio PLL. This function can be called anytime, * but it takes time till PLL is locked. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id, unsignedint freq)
{ struct device *dev = &chip->pdev->dev; struct regmap *r = chip->regmap; int shift;
u32 v;
/* Not change */ if (freq == 0) return 0;
switch (pll_id) { case AUD_PLL_A1:
shift = 0; break; case AUD_PLL_F1:
shift = 1; break; case AUD_PLL_A2:
shift = 2; break; case AUD_PLL_F2:
shift = 3; break; default:
dev_err(dev, "PLL(%d) not supported\n", pll_id); return -EINVAL;
}
switch (freq) { case 36864000:
v = A2APLLCTR1_APLLX_36MHZ; break; case 33868800:
v = A2APLLCTR1_APLLX_33MHZ; break; default:
dev_err(dev, "PLL frequency not supported(%d)\n", freq); return -EINVAL;
}
chip->plls[pll_id].freq = freq;
regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLLX_MASK << shift,
v << shift);
return 0;
}
/** * aio_chip_init - initialize AIO whole settings * @chip: the AIO chip pointer * * Sets AIO fixed and whole device settings to AIO. * This function need to call once at driver startup. * * The register area that is changed by this function is shared by all * modules of AIO. But there is not race condition since this function * has always set the same initialize values.
*/ void aio_chip_init(struct uniphier_aio_chip *chip)
{ struct regmap *r = chip->regmap;
/** * aio_init - initialize AIO substream * @sub: the AIO substream pointer * * Sets fixed settings of each AIO substreams. * This function need to call once at substream startup. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_init(struct uniphier_aio_sub *sub)
{ struct device *dev = &sub->aio->chip->pdev->dev; struct regmap *r = sub->aio->chip->regmap;
/** * aio_port_set_ch - set channels of LPCM * @sub: the AIO substream pointer, PCM substream only * * Set suitable slot selecting to input/output port block of AIO. * * This function may return error if non-PCM substream. * * Return: Zero if successful, otherwise a negative value on error.
*/ staticint aio_port_set_ch(struct uniphier_aio_sub *sub)
{ struct regmap *r = sub->aio->chip->regmap; staticconst u32 slotsel_2ch[] = {
0, 0, 0, 0, 0,
}; staticconst u32 slotsel_multi[] = {
OPORTMXTYSLOTCTR_SLOTSEL_SLOT0,
OPORTMXTYSLOTCTR_SLOTSEL_SLOT1,
OPORTMXTYSLOTCTR_SLOTSEL_SLOT2,
OPORTMXTYSLOTCTR_SLOTSEL_SLOT3,
OPORTMXTYSLOTCTR_SLOTSEL_SLOT4,
};
u32 mode; const u32 *slotsel; int i;
switch (params_channels(&sub->params)) { case 8: case 6:
mode = OPORTMXTYSLOTCTR_MODE;
slotsel = slotsel_multi; break; case 2:
mode = 0;
slotsel = slotsel_2ch; break; default: return -EINVAL;
}
for (i = 0; i < AUD_MAX_SLOTSEL; i++) {
regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i),
OPORTMXTYSLOTCTR_MODE, mode);
regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i),
OPORTMXTYSLOTCTR_SLOTSEL_MASK, slotsel[i]);
}
return 0;
}
/** * aio_port_set_rate - set sampling rate of LPCM * @sub: the AIO substream pointer, PCM substream only * @rate: Sampling rate in Hz. * * Set suitable I2S format settings to input/output port block of AIO. * Parameter is specified by hw_params(). * * This function may return error if non-PCM substream. * * Return: Zero if successful, otherwise a negative value on error.
*/ staticint aio_port_set_rate(struct uniphier_aio_sub *sub, int rate)
{ struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev;
u32 v;
if (sub->swm->dir == PORT_DIR_OUTPUT) { switch (rate) { case 8000:
v = OPORTMXCTR1_FSSEL_8; break; case 11025:
v = OPORTMXCTR1_FSSEL_11_025; break; case 12000:
v = OPORTMXCTR1_FSSEL_12; break; case 16000:
v = OPORTMXCTR1_FSSEL_16; break; case 22050:
v = OPORTMXCTR1_FSSEL_22_05; break; case 24000:
v = OPORTMXCTR1_FSSEL_24; break; case 32000:
v = OPORTMXCTR1_FSSEL_32; break; case 44100:
v = OPORTMXCTR1_FSSEL_44_1; break; case 48000:
v = OPORTMXCTR1_FSSEL_48; break; case 88200:
v = OPORTMXCTR1_FSSEL_88_2; break; case 96000:
v = OPORTMXCTR1_FSSEL_96; break; case 176400:
v = OPORTMXCTR1_FSSEL_176_4; break; case 192000:
v = OPORTMXCTR1_FSSEL_192; break; default:
dev_err(dev, "Rate not supported(%d)\n", rate); return -EINVAL;
}
regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map),
OPORTMXCTR1_FSSEL_MASK, v);
} else { switch (rate) { case 8000:
v = IPORTMXCTR1_FSSEL_8; break; case 11025:
v = IPORTMXCTR1_FSSEL_11_025; break; case 12000:
v = IPORTMXCTR1_FSSEL_12; break; case 16000:
v = IPORTMXCTR1_FSSEL_16; break; case 22050:
v = IPORTMXCTR1_FSSEL_22_05; break; case 24000:
v = IPORTMXCTR1_FSSEL_24; break; case 32000:
v = IPORTMXCTR1_FSSEL_32; break; case 44100:
v = IPORTMXCTR1_FSSEL_44_1; break; case 48000:
v = IPORTMXCTR1_FSSEL_48; break; case 88200:
v = IPORTMXCTR1_FSSEL_88_2; break; case 96000:
v = IPORTMXCTR1_FSSEL_96; break; case 176400:
v = IPORTMXCTR1_FSSEL_176_4; break; case 192000:
v = IPORTMXCTR1_FSSEL_192; break; default:
dev_err(dev, "Rate not supported(%d)\n", rate); return -EINVAL;
}
/** * aio_port_set_fmt - set format of I2S data * @sub: the AIO substream pointer, PCM substream only * This parameter has no effect if substream is I2S or PCM. * * Set suitable I2S format settings to input/output port block of AIO. * Parameter is specified by set_fmt(). * * This function may return error if non-PCM substream. * * Return: Zero if successful, otherwise a negative value on error.
*/ staticint aio_port_set_fmt(struct uniphier_aio_sub *sub)
{ struct regmap *r = sub->aio->chip->regmap; struct device *dev = &sub->aio->chip->pdev->dev;
u32 v;
if (sub->swm->dir == PORT_DIR_OUTPUT) { switch (sub->aio->fmt) { case SND_SOC_DAIFMT_LEFT_J:
v = OPORTMXCTR1_I2SLRSEL_LEFT; break; case SND_SOC_DAIFMT_RIGHT_J:
v = OPORTMXCTR1_I2SLRSEL_RIGHT; break; case SND_SOC_DAIFMT_I2S:
v = OPORTMXCTR1_I2SLRSEL_I2S; break; default:
dev_err(dev, "Format is not supported(%d)\n",
sub->aio->fmt); return -EINVAL;
}
v |= OPORTMXCTR1_OUTBITSEL_24;
regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map),
OPORTMXCTR1_I2SLRSEL_MASK |
OPORTMXCTR1_OUTBITSEL_MASK, v);
} else { switch (sub->aio->fmt) { case SND_SOC_DAIFMT_LEFT_J:
v = IPORTMXCTR1_LRSEL_LEFT; break; case SND_SOC_DAIFMT_RIGHT_J:
v = IPORTMXCTR1_LRSEL_RIGHT; break; case SND_SOC_DAIFMT_I2S:
v = IPORTMXCTR1_LRSEL_I2S; break; default:
dev_err(dev, "Format is not supported(%d)\n",
sub->aio->fmt); return -EINVAL;
}
/** * aio_port_set_clk - set clock and divider of AIO port block * @sub: the AIO substream pointer * * Set suitable PLL clock divider and relational settings to * input/output port block of AIO. Parameters are specified by * set_sysclk() and set_pll(). * * Return: Zero if successful, otherwise a negative value on error.
*/ staticint aio_port_set_clk(struct uniphier_aio_sub *sub)
{ struct uniphier_aio_chip *chip = sub->aio->chip; struct device *dev = &sub->aio->chip->pdev->dev; struct regmap *r = sub->aio->chip->regmap; staticconst u32 v_pll[] = {
OPORTMXCTR2_ACLKSEL_A1, OPORTMXCTR2_ACLKSEL_F1,
OPORTMXCTR2_ACLKSEL_A2, OPORTMXCTR2_ACLKSEL_F2,
OPORTMXCTR2_ACLKSEL_A2PLL,
OPORTMXCTR2_ACLKSEL_RX1,
}; staticconst u32 v_div[] = {
OPORTMXCTR2_DACCKSEL_1_2, OPORTMXCTR2_DACCKSEL_1_3,
OPORTMXCTR2_DACCKSEL_1_1, OPORTMXCTR2_DACCKSEL_2_3,
};
u32 v;
if (sub->swm->dir == PORT_DIR_OUTPUT) { if (sub->swm->type == PORT_TYPE_I2S) { if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) {
dev_err(dev, "PLL(%d) is invalid\n",
sub->aio->pll_out); return -EINVAL;
} if (sub->aio->plldiv >= ARRAY_SIZE(v_div)) {
dev_err(dev, "PLL divider(%d) is invalid\n",
sub->aio->plldiv); return -EINVAL;
}
v = v_pll[sub->aio->pll_out] |
OPORTMXCTR2_MSSEL_MASTER |
v_div[sub->aio->plldiv];
switch (chip->plls[sub->aio->pll_out].freq) { case 0: case 36864000: case 33868800:
v |= OPORTMXCTR2_EXTLSIFSSEL_36; break; default:
v |= OPORTMXCTR2_EXTLSIFSSEL_24; break;
}
} elseif (sub->swm->type == PORT_TYPE_EVE) {
v = OPORTMXCTR2_ACLKSEL_A2PLL |
OPORTMXCTR2_MSSEL_MASTER |
OPORTMXCTR2_EXTLSIFSSEL_36 |
OPORTMXCTR2_DACCKSEL_1_2;
} elseif (sub->swm->type == PORT_TYPE_SPDIF) { if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) {
dev_err(dev, "PLL(%d) is invalid\n",
sub->aio->pll_out); return -EINVAL;
}
v = v_pll[sub->aio->pll_out] |
OPORTMXCTR2_MSSEL_MASTER |
OPORTMXCTR2_DACCKSEL_1_2;
switch (chip->plls[sub->aio->pll_out].freq) { case 0: case 36864000: case 33868800:
v |= OPORTMXCTR2_EXTLSIFSSEL_36; break; default:
v |= OPORTMXCTR2_EXTLSIFSSEL_24; break;
}
} else {
v = OPORTMXCTR2_ACLKSEL_A1 |
OPORTMXCTR2_MSSEL_MASTER |
OPORTMXCTR2_EXTLSIFSSEL_36 |
OPORTMXCTR2_DACCKSEL_1_2;
}
regmap_write(r, OPORTMXCTR2(sub->swm->oport.map), v);
} else {
v = IPORTMXCTR2_ACLKSEL_A1 |
IPORTMXCTR2_MSSEL_SLAVE |
IPORTMXCTR2_EXTLSIFSSEL_36 |
IPORTMXCTR2_DACCKSEL_1_2;
regmap_write(r, IPORTMXCTR2(sub->swm->iport.map), v);
}
return 0;
}
/** * aio_port_set_param - set parameters of AIO port block * @sub: the AIO substream pointer * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. * This parameter has no effect if substream is I2S or PCM. * @params: hardware parameters of ALSA * * Set suitable setting to input/output port block of AIO to process the * specified in params. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, conststruct snd_pcm_hw_params *params)
{ struct regmap *r = sub->aio->chip->regmap; unsignedint rate;
u32 v; int ret;
ret = aio_port_set_rate(sub, rate); if (ret) return ret;
ret = aio_port_set_fmt(sub); if (ret) return ret;
}
ret = aio_port_set_clk(sub); if (ret) return ret;
if (sub->swm->dir == PORT_DIR_OUTPUT) { if (pass_through)
v = OPORTMXCTR3_SRCSEL_STREAM |
OPORTMXCTR3_VALID_STREAM; else
v = OPORTMXCTR3_SRCSEL_PCM |
OPORTMXCTR3_VALID_PCM;
/** * aio_port_set_enable - start or stop of AIO port block * @sub: the AIO substream pointer * @enable: zero to stop the block, otherwise to start * * Start or stop the signal input/output port block of AIO.
*/ void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable)
{ struct regmap *r = sub->aio->chip->regmap;
if (sub->swm->dir == PORT_DIR_OUTPUT) {
regmap_write(r, OPORTMXPATH(sub->swm->oport.map),
sub->swm->oif.map);
/** * aio_port_get_volume - get volume of AIO port block * @sub: the AIO substream pointer * * Return: current volume, range is 0x0000 - 0xffff
*/ int aio_port_get_volume(struct uniphier_aio_sub *sub)
{ struct regmap *r = sub->aio->chip->regmap;
u32 v;
/** * aio_port_set_volume - set volume of AIO port block * @sub: the AIO substream pointer * @vol: target volume, range is 0x0000 - 0xffff. * * Change digital volume and perfome fade-out/fade-in effect for specified * output slot of port. Gained PCM value can calculate as the following: * Gained = Original * vol / 0x4000
*/ void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol)
{ struct regmap *r = sub->aio->chip->regmap; int oport_map = sub->swm->oport.map; int cur, diff, slope = 0, fs;
/** * aio_if_set_param - set parameters of AIO DMA I/F block * @sub: the AIO substream pointer * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. * This parameter has no effect if substream is I2S or PCM. * * Set suitable setting to DMA interface block of AIO to process the * specified in settings. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through)
{ struct regmap *r = sub->aio->chip->regmap;
u32 memfmt, v;
if (sub->swm->dir == PORT_DIR_OUTPUT) { if (pass_through) {
v = PBOUTMXCTR0_ENDIAN_0123 |
PBOUTMXCTR0_MEMFMT_STREAM;
} else { switch (params_channels(&sub->params)) { case 2:
memfmt = PBOUTMXCTR0_MEMFMT_2CH; break; case 6:
memfmt = PBOUTMXCTR0_MEMFMT_6CH; break; case 8:
memfmt = PBOUTMXCTR0_MEMFMT_8CH; break; default: return -EINVAL;
}
v = PBOUTMXCTR0_ENDIAN_3210 | memfmt;
}
/** * aio_oport_set_stream_type - set parameters of AIO playback port block * @sub: the AIO substream pointer * @pc: Pc type of IEC61937 * * Set special setting to output port block of AIO to output the stream * via S/PDIF. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_oport_set_stream_type(struct uniphier_aio_sub *sub, enum IEC61937_PC pc)
{ struct regmap *r = sub->aio->chip->regmap;
u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN; int ret;
switch (pc) { case IEC61937_PC_AC3:
repet = OPORTMXREPET_STRLENGTH_AC3 |
OPORTMXREPET_PMLENGTH_AC3;
pause |= OPORTMXPAUDAT_PAUSEPD_AC3; break; case IEC61937_PC_MPA:
repet = OPORTMXREPET_STRLENGTH_MPA |
OPORTMXREPET_PMLENGTH_MPA;
pause |= OPORTMXPAUDAT_PAUSEPD_MPA; break; case IEC61937_PC_MP3:
repet = OPORTMXREPET_STRLENGTH_MP3 |
OPORTMXREPET_PMLENGTH_MP3;
pause |= OPORTMXPAUDAT_PAUSEPD_MP3; break; case IEC61937_PC_DTS1:
repet = OPORTMXREPET_STRLENGTH_DTS1 |
OPORTMXREPET_PMLENGTH_DTS1;
pause |= OPORTMXPAUDAT_PAUSEPD_DTS1; break; case IEC61937_PC_DTS2:
repet = OPORTMXREPET_STRLENGTH_DTS2 |
OPORTMXREPET_PMLENGTH_DTS2;
pause |= OPORTMXPAUDAT_PAUSEPD_DTS2; break; case IEC61937_PC_DTS3:
repet = OPORTMXREPET_STRLENGTH_DTS3 |
OPORTMXREPET_PMLENGTH_DTS3;
pause |= OPORTMXPAUDAT_PAUSEPD_DTS3; break; case IEC61937_PC_AAC:
repet = OPORTMXREPET_STRLENGTH_AAC |
OPORTMXREPET_PMLENGTH_AAC;
pause |= OPORTMXPAUDAT_PAUSEPD_AAC; break; case IEC61937_PC_PAUSE: /* Do nothing */ break;
}
ret = regmap_write(r, OPORTMXREPET(sub->swm->oport.map), repet); if (ret) return ret;
ret = regmap_write(r, OPORTMXPAUDAT(sub->swm->oport.map), pause); if (ret) return ret;
return 0;
}
/** * aio_src_reset - reset AIO SRC block * @sub: the AIO substream pointer * * Resets the digital signal input/output port with sampling rate converter * block of AIO. * This function has no effect if substream is not supported rate converter.
*/ void aio_src_reset(struct uniphier_aio_sub *sub)
{ struct regmap *r = sub->aio->chip->regmap;
/** * aio_src_set_param - set parameters of AIO SRC block * @sub: the AIO substream pointer * @params: hardware parameters of ALSA * * Set suitable setting to input/output port with sampling rate converter * block of AIO to process the specified in params. * This function has no effect if substream is not supported rate converter. * * Return: Zero if successful, otherwise a negative value on error.
*/ int aio_src_set_param(struct uniphier_aio_sub *sub, conststruct snd_pcm_hw_params *params)
{ struct regmap *r = sub->aio->chip->regmap;
u32 v; int ret;
if (sub->swm->dir != PORT_DIR_OUTPUT) return 0;
ret = regmap_write(r, OPORTMXSRC1CTR(sub->swm->oport.map),
OPORTMXSRC1CTR_THMODE_SRC |
OPORTMXSRC1CTR_SRCPATH_CALC |
OPORTMXSRC1CTR_SYNC_ASYNC |
OPORTMXSRC1CTR_FSIIPSEL_INNER |
OPORTMXSRC1CTR_FSISEL_ACLK); if (ret) return ret;
switch (params_rate(params)) { default: case 48000:
v = OPORTMXRATE_I_ACLKSEL_APLLA1 |
OPORTMXRATE_I_MCKSEL_36 |
OPORTMXRATE_I_FSSEL_48; break; case 44100:
v = OPORTMXRATE_I_ACLKSEL_APLLA2 |
OPORTMXRATE_I_MCKSEL_33 |
OPORTMXRATE_I_FSSEL_44_1; break; case 32000:
v = OPORTMXRATE_I_ACLKSEL_APLLA1 |
OPORTMXRATE_I_MCKSEL_36 |
OPORTMXRATE_I_FSSEL_32; break;
}
ret = regmap_write(r, OPORTMXRATE_I(sub->swm->oport.map),
v | OPORTMXRATE_I_ACLKSRC_APLL |
OPORTMXRATE_I_LRCKSTP_STOP); if (ret) return ret;
ret = regmap_update_bits(r, OPORTMXRATE_I(sub->swm->oport.map),
OPORTMXRATE_I_LRCKSTP_MASK,
OPORTMXRATE_I_LRCKSTP_START); if (ret) return ret;
return 0;
}
int aio_srcif_set_param(struct uniphier_aio_sub *sub)
{ struct regmap *r = sub->aio->chip->regmap;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.