162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Socionext UniPhier AIO ALSA common driver. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2016-2018 Socionext Inc. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <sound/core.h> 1262306a36Sopenharmony_ci#include <sound/pcm.h> 1362306a36Sopenharmony_ci#include <sound/pcm_params.h> 1462306a36Sopenharmony_ci#include <sound/soc.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "aio.h" 1762306a36Sopenharmony_ci#include "aio-reg.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic u64 rb_cnt(u64 wr, u64 rd, u64 len) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (rd <= wr) 2262306a36Sopenharmony_ci return wr - rd; 2362306a36Sopenharmony_ci else 2462306a36Sopenharmony_ci return len - (rd - wr); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic u64 rb_cnt_to_end(u64 wr, u64 rd, u64 len) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci if (rd <= wr) 3062306a36Sopenharmony_ci return wr - rd; 3162306a36Sopenharmony_ci else 3262306a36Sopenharmony_ci return len - rd; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic u64 rb_space(u64 wr, u64 rd, u64 len) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (rd <= wr) 3862306a36Sopenharmony_ci return len - (wr - rd) - 8; 3962306a36Sopenharmony_ci else 4062306a36Sopenharmony_ci return rd - wr - 8; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic u64 rb_space_to_end(u64 wr, u64 rd, u64 len) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci if (rd > wr) 4662306a36Sopenharmony_ci return rd - wr - 8; 4762306a36Sopenharmony_ci else if (rd > 0) 4862306a36Sopenharmony_ci return len - wr; 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci return len - wr - 8; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciu64 aio_rb_cnt(struct uniphier_aio_sub *sub) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return rb_cnt(sub->wr_offs, sub->rd_offs, sub->compr_bytes); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ciu64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return rb_cnt_to_end(sub->wr_offs, sub->rd_offs, sub->compr_bytes); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciu64 aio_rb_space(struct uniphier_aio_sub *sub) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci return rb_space(sub->wr_offs, sub->rd_offs, sub->compr_bytes); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciu64 aio_rb_space_to_end(struct uniphier_aio_sub *sub) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return rb_space_to_end(sub->wr_offs, sub->rd_offs, sub->compr_bytes); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * aio_iecout_set_enable - setup IEC output via SoC glue 7562306a36Sopenharmony_ci * @chip: the AIO chip pointer 7662306a36Sopenharmony_ci * @enable: false to stop the output, true to start 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Set enabled or disabled S/PDIF signal output to out of SoC via AOnIEC pins. 7962306a36Sopenharmony_ci * This function need to call at driver startup. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * The regmap of SoC glue is specified by 'socionext,syscon' optional property 8262306a36Sopenharmony_ci * of DT. This function has no effect if no property. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_civoid aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct regmap *r = chip->regmap_sg; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (!r) 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci regmap_write(r, SG_AOUTEN, (enable) ? ~0 : 0); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * aio_chip_set_pll - set frequency to audio PLL 9662306a36Sopenharmony_ci * @chip: the AIO chip pointer 9762306a36Sopenharmony_ci * @pll_id: PLL 9862306a36Sopenharmony_ci * @freq: frequency in Hz, 0 is ignored 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Sets frequency of audio PLL. This function can be called anytime, 10162306a36Sopenharmony_ci * but it takes time till PLL is locked. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ciint aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id, 10662306a36Sopenharmony_ci unsigned int freq) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct device *dev = &chip->pdev->dev; 10962306a36Sopenharmony_ci struct regmap *r = chip->regmap; 11062306a36Sopenharmony_ci int shift; 11162306a36Sopenharmony_ci u32 v; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Not change */ 11462306a36Sopenharmony_ci if (freq == 0) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci switch (pll_id) { 11862306a36Sopenharmony_ci case AUD_PLL_A1: 11962306a36Sopenharmony_ci shift = 0; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case AUD_PLL_F1: 12262306a36Sopenharmony_ci shift = 1; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case AUD_PLL_A2: 12562306a36Sopenharmony_ci shift = 2; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci case AUD_PLL_F2: 12862306a36Sopenharmony_ci shift = 3; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci default: 13162306a36Sopenharmony_ci dev_err(dev, "PLL(%d) not supported\n", pll_id); 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (freq) { 13662306a36Sopenharmony_ci case 36864000: 13762306a36Sopenharmony_ci v = A2APLLCTR1_APLLX_36MHZ; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case 33868800: 14062306a36Sopenharmony_ci v = A2APLLCTR1_APLLX_33MHZ; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci default: 14362306a36Sopenharmony_ci dev_err(dev, "PLL frequency not supported(%d)\n", freq); 14462306a36Sopenharmony_ci return -EINVAL; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci chip->plls[pll_id].freq = freq; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLLX_MASK << shift, 14962306a36Sopenharmony_ci v << shift); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * aio_chip_init - initialize AIO whole settings 15662306a36Sopenharmony_ci * @chip: the AIO chip pointer 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * Sets AIO fixed and whole device settings to AIO. 15962306a36Sopenharmony_ci * This function need to call once at driver startup. 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * The register area that is changed by this function is shared by all 16262306a36Sopenharmony_ci * modules of AIO. But there is not race condition since this function 16362306a36Sopenharmony_ci * has always set the same initialize values. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_civoid aio_chip_init(struct uniphier_aio_chip *chip) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct regmap *r = chip->regmap; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci regmap_update_bits(r, A2APLLCTR0, 17062306a36Sopenharmony_ci A2APLLCTR0_APLLXPOW_MASK, 17162306a36Sopenharmony_ci A2APLLCTR0_APLLXPOW_PWON); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci regmap_update_bits(r, A2EXMCLKSEL0, 17462306a36Sopenharmony_ci A2EXMCLKSEL0_EXMCLK_MASK, 17562306a36Sopenharmony_ci A2EXMCLKSEL0_EXMCLK_OUTPUT); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci regmap_update_bits(r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK, 17862306a36Sopenharmony_ci A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 | 17962306a36Sopenharmony_ci A2AIOINPUTSEL_RXSEL_PCMI2_SIF | 18062306a36Sopenharmony_ci A2AIOINPUTSEL_RXSEL_PCMI3_EVEA | 18162306a36Sopenharmony_ci A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (chip->chip_spec->addr_ext) 18462306a36Sopenharmony_ci regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, 18562306a36Sopenharmony_ci CDA2D_TEST_DDR_MODE_EXTON0); 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, 18862306a36Sopenharmony_ci CDA2D_TEST_DDR_MODE_EXTOFF1); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * aio_init - initialize AIO substream 19362306a36Sopenharmony_ci * @sub: the AIO substream pointer 19462306a36Sopenharmony_ci * 19562306a36Sopenharmony_ci * Sets fixed settings of each AIO substreams. 19662306a36Sopenharmony_ci * This function need to call once at substream startup. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ciint aio_init(struct uniphier_aio_sub *sub) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct device *dev = &sub->aio->chip->pdev->dev; 20362306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci regmap_write(r, A2RBNMAPCTR0(sub->swm->rb.hw), 20662306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->rb.map); 20762306a36Sopenharmony_ci regmap_write(r, A2CHNMAPCTR0(sub->swm->ch.hw), 20862306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->ch.map); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci switch (sub->swm->type) { 21162306a36Sopenharmony_ci case PORT_TYPE_I2S: 21262306a36Sopenharmony_ci case PORT_TYPE_SPDIF: 21362306a36Sopenharmony_ci case PORT_TYPE_EVE: 21462306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_INPUT) { 21562306a36Sopenharmony_ci regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw), 21662306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->iif.map); 21762306a36Sopenharmony_ci regmap_write(r, A2IPORTNMAPCTR0(sub->swm->iport.hw), 21862306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->iport.map); 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw), 22162306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->oif.map); 22262306a36Sopenharmony_ci regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw), 22362306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->oport.map); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case PORT_TYPE_CONV: 22762306a36Sopenharmony_ci regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw), 22862306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->oif.map); 22962306a36Sopenharmony_ci regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw), 23062306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->oport.map); 23162306a36Sopenharmony_ci regmap_write(r, A2CHNMAPCTR0(sub->swm->och.hw), 23262306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->och.map); 23362306a36Sopenharmony_ci regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw), 23462306a36Sopenharmony_ci MAPCTR0_EN | sub->swm->iif.map); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci default: 23762306a36Sopenharmony_ci dev_err(dev, "Unknown port type %d.\n", sub->swm->type); 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/** 24562306a36Sopenharmony_ci * aio_port_reset - reset AIO port block 24662306a36Sopenharmony_ci * @sub: the AIO substream pointer 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Resets the digital signal input/output port block of AIO. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_civoid aio_port_reset(struct uniphier_aio_sub *sub) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 25562306a36Sopenharmony_ci regmap_write(r, AOUTRSTCTR0, BIT(sub->swm->oport.map)); 25662306a36Sopenharmony_ci regmap_write(r, AOUTRSTCTR1, BIT(sub->swm->oport.map)); 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map), 25962306a36Sopenharmony_ci IPORTMXRSTCTR_RSTPI_MASK, 26062306a36Sopenharmony_ci IPORTMXRSTCTR_RSTPI_RESET); 26162306a36Sopenharmony_ci regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map), 26262306a36Sopenharmony_ci IPORTMXRSTCTR_RSTPI_MASK, 26362306a36Sopenharmony_ci IPORTMXRSTCTR_RSTPI_RELEASE); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/** 26862306a36Sopenharmony_ci * aio_port_set_ch - set channels of LPCM 26962306a36Sopenharmony_ci * @sub: the AIO substream pointer, PCM substream only 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * Set suitable slot selecting to input/output port block of AIO. 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * This function may return error if non-PCM substream. 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistatic int aio_port_set_ch(struct uniphier_aio_sub *sub) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 28062306a36Sopenharmony_ci static const u32 slotsel_2ch[] = { 28162306a36Sopenharmony_ci 0, 0, 0, 0, 0, 28262306a36Sopenharmony_ci }; 28362306a36Sopenharmony_ci static const u32 slotsel_multi[] = { 28462306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_SLOT0, 28562306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_SLOT1, 28662306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_SLOT2, 28762306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_SLOT3, 28862306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_SLOT4, 28962306a36Sopenharmony_ci }; 29062306a36Sopenharmony_ci u32 mode; 29162306a36Sopenharmony_ci const u32 *slotsel; 29262306a36Sopenharmony_ci int i; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci switch (params_channels(&sub->params)) { 29562306a36Sopenharmony_ci case 8: 29662306a36Sopenharmony_ci case 6: 29762306a36Sopenharmony_ci mode = OPORTMXTYSLOTCTR_MODE; 29862306a36Sopenharmony_ci slotsel = slotsel_multi; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case 2: 30162306a36Sopenharmony_ci mode = 0; 30262306a36Sopenharmony_ci slotsel = slotsel_2ch; 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci default: 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (i = 0; i < AUD_MAX_SLOTSEL; i++) { 30962306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), 31062306a36Sopenharmony_ci OPORTMXTYSLOTCTR_MODE, mode); 31162306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), 31262306a36Sopenharmony_ci OPORTMXTYSLOTCTR_SLOTSEL_MASK, slotsel[i]); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/** 31962306a36Sopenharmony_ci * aio_port_set_rate - set sampling rate of LPCM 32062306a36Sopenharmony_ci * @sub: the AIO substream pointer, PCM substream only 32162306a36Sopenharmony_ci * @rate: Sampling rate in Hz. 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Set suitable I2S format settings to input/output port block of AIO. 32462306a36Sopenharmony_ci * Parameter is specified by hw_params(). 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * This function may return error if non-PCM substream. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 33362306a36Sopenharmony_ci struct device *dev = &sub->aio->chip->pdev->dev; 33462306a36Sopenharmony_ci u32 v; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 33762306a36Sopenharmony_ci switch (rate) { 33862306a36Sopenharmony_ci case 8000: 33962306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_8; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case 11025: 34262306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_11_025; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case 12000: 34562306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_12; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case 16000: 34862306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_16; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci case 22050: 35162306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_22_05; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case 24000: 35462306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_24; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case 32000: 35762306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_32; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case 44100: 36062306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_44_1; 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case 48000: 36362306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_48; 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case 88200: 36662306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_88_2; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case 96000: 36962306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_96; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci case 176400: 37262306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_176_4; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case 192000: 37562306a36Sopenharmony_ci v = OPORTMXCTR1_FSSEL_192; 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci default: 37862306a36Sopenharmony_ci dev_err(dev, "Rate not supported(%d)\n", rate); 37962306a36Sopenharmony_ci return -EINVAL; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map), 38362306a36Sopenharmony_ci OPORTMXCTR1_FSSEL_MASK, v); 38462306a36Sopenharmony_ci } else { 38562306a36Sopenharmony_ci switch (rate) { 38662306a36Sopenharmony_ci case 8000: 38762306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_8; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case 11025: 39062306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_11_025; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case 12000: 39362306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_12; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case 16000: 39662306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_16; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case 22050: 39962306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_22_05; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case 24000: 40262306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_24; 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case 32000: 40562306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_32; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci case 44100: 40862306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_44_1; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case 48000: 41162306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_48; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case 88200: 41462306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_88_2; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case 96000: 41762306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_96; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci case 176400: 42062306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_176_4; 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci case 192000: 42362306a36Sopenharmony_ci v = IPORTMXCTR1_FSSEL_192; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci dev_err(dev, "Rate not supported(%d)\n", rate); 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci regmap_update_bits(r, IPORTMXCTR1(sub->swm->iport.map), 43162306a36Sopenharmony_ci IPORTMXCTR1_FSSEL_MASK, v); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * aio_port_set_fmt - set format of I2S data 43962306a36Sopenharmony_ci * @sub: the AIO substream pointer, PCM substream only 44062306a36Sopenharmony_ci * This parameter has no effect if substream is I2S or PCM. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Set suitable I2S format settings to input/output port block of AIO. 44362306a36Sopenharmony_ci * Parameter is specified by set_fmt(). 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * This function may return error if non-PCM substream. 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic int aio_port_set_fmt(struct uniphier_aio_sub *sub) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 45262306a36Sopenharmony_ci struct device *dev = &sub->aio->chip->pdev->dev; 45362306a36Sopenharmony_ci u32 v; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 45662306a36Sopenharmony_ci switch (sub->aio->fmt) { 45762306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 45862306a36Sopenharmony_ci v = OPORTMXCTR1_I2SLRSEL_LEFT; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 46162306a36Sopenharmony_ci v = OPORTMXCTR1_I2SLRSEL_RIGHT; 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 46462306a36Sopenharmony_ci v = OPORTMXCTR1_I2SLRSEL_I2S; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci default: 46762306a36Sopenharmony_ci dev_err(dev, "Format is not supported(%d)\n", 46862306a36Sopenharmony_ci sub->aio->fmt); 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci v |= OPORTMXCTR1_OUTBITSEL_24; 47362306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map), 47462306a36Sopenharmony_ci OPORTMXCTR1_I2SLRSEL_MASK | 47562306a36Sopenharmony_ci OPORTMXCTR1_OUTBITSEL_MASK, v); 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci switch (sub->aio->fmt) { 47862306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 47962306a36Sopenharmony_ci v = IPORTMXCTR1_LRSEL_LEFT; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 48262306a36Sopenharmony_ci v = IPORTMXCTR1_LRSEL_RIGHT; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 48562306a36Sopenharmony_ci v = IPORTMXCTR1_LRSEL_I2S; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci default: 48862306a36Sopenharmony_ci dev_err(dev, "Format is not supported(%d)\n", 48962306a36Sopenharmony_ci sub->aio->fmt); 49062306a36Sopenharmony_ci return -EINVAL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci v |= IPORTMXCTR1_OUTBITSEL_24 | 49462306a36Sopenharmony_ci IPORTMXCTR1_CHSEL_ALL; 49562306a36Sopenharmony_ci regmap_update_bits(r, IPORTMXCTR1(sub->swm->iport.map), 49662306a36Sopenharmony_ci IPORTMXCTR1_LRSEL_MASK | 49762306a36Sopenharmony_ci IPORTMXCTR1_OUTBITSEL_MASK | 49862306a36Sopenharmony_ci IPORTMXCTR1_CHSEL_MASK, v); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/** 50562306a36Sopenharmony_ci * aio_port_set_clk - set clock and divider of AIO port block 50662306a36Sopenharmony_ci * @sub: the AIO substream pointer 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * Set suitable PLL clock divider and relational settings to 50962306a36Sopenharmony_ci * input/output port block of AIO. Parameters are specified by 51062306a36Sopenharmony_ci * set_sysclk() and set_pll(). 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_cistatic int aio_port_set_clk(struct uniphier_aio_sub *sub) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct uniphier_aio_chip *chip = sub->aio->chip; 51762306a36Sopenharmony_ci struct device *dev = &sub->aio->chip->pdev->dev; 51862306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 51962306a36Sopenharmony_ci static const u32 v_pll[] = { 52062306a36Sopenharmony_ci OPORTMXCTR2_ACLKSEL_A1, OPORTMXCTR2_ACLKSEL_F1, 52162306a36Sopenharmony_ci OPORTMXCTR2_ACLKSEL_A2, OPORTMXCTR2_ACLKSEL_F2, 52262306a36Sopenharmony_ci OPORTMXCTR2_ACLKSEL_A2PLL, 52362306a36Sopenharmony_ci OPORTMXCTR2_ACLKSEL_RX1, 52462306a36Sopenharmony_ci }; 52562306a36Sopenharmony_ci static const u32 v_div[] = { 52662306a36Sopenharmony_ci OPORTMXCTR2_DACCKSEL_1_2, OPORTMXCTR2_DACCKSEL_1_3, 52762306a36Sopenharmony_ci OPORTMXCTR2_DACCKSEL_1_1, OPORTMXCTR2_DACCKSEL_2_3, 52862306a36Sopenharmony_ci }; 52962306a36Sopenharmony_ci u32 v; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 53262306a36Sopenharmony_ci if (sub->swm->type == PORT_TYPE_I2S) { 53362306a36Sopenharmony_ci if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) { 53462306a36Sopenharmony_ci dev_err(dev, "PLL(%d) is invalid\n", 53562306a36Sopenharmony_ci sub->aio->pll_out); 53662306a36Sopenharmony_ci return -EINVAL; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci if (sub->aio->plldiv >= ARRAY_SIZE(v_div)) { 53962306a36Sopenharmony_ci dev_err(dev, "PLL divider(%d) is invalid\n", 54062306a36Sopenharmony_ci sub->aio->plldiv); 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci v = v_pll[sub->aio->pll_out] | 54562306a36Sopenharmony_ci OPORTMXCTR2_MSSEL_MASTER | 54662306a36Sopenharmony_ci v_div[sub->aio->plldiv]; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (chip->plls[sub->aio->pll_out].freq) { 54962306a36Sopenharmony_ci case 0: 55062306a36Sopenharmony_ci case 36864000: 55162306a36Sopenharmony_ci case 33868800: 55262306a36Sopenharmony_ci v |= OPORTMXCTR2_EXTLSIFSSEL_36; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci default: 55562306a36Sopenharmony_ci v |= OPORTMXCTR2_EXTLSIFSSEL_24; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } else if (sub->swm->type == PORT_TYPE_EVE) { 55962306a36Sopenharmony_ci v = OPORTMXCTR2_ACLKSEL_A2PLL | 56062306a36Sopenharmony_ci OPORTMXCTR2_MSSEL_MASTER | 56162306a36Sopenharmony_ci OPORTMXCTR2_EXTLSIFSSEL_36 | 56262306a36Sopenharmony_ci OPORTMXCTR2_DACCKSEL_1_2; 56362306a36Sopenharmony_ci } else if (sub->swm->type == PORT_TYPE_SPDIF) { 56462306a36Sopenharmony_ci if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) { 56562306a36Sopenharmony_ci dev_err(dev, "PLL(%d) is invalid\n", 56662306a36Sopenharmony_ci sub->aio->pll_out); 56762306a36Sopenharmony_ci return -EINVAL; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci v = v_pll[sub->aio->pll_out] | 57062306a36Sopenharmony_ci OPORTMXCTR2_MSSEL_MASTER | 57162306a36Sopenharmony_ci OPORTMXCTR2_DACCKSEL_1_2; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci switch (chip->plls[sub->aio->pll_out].freq) { 57462306a36Sopenharmony_ci case 0: 57562306a36Sopenharmony_ci case 36864000: 57662306a36Sopenharmony_ci case 33868800: 57762306a36Sopenharmony_ci v |= OPORTMXCTR2_EXTLSIFSSEL_36; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci default: 58062306a36Sopenharmony_ci v |= OPORTMXCTR2_EXTLSIFSSEL_24; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci } else { 58462306a36Sopenharmony_ci v = OPORTMXCTR2_ACLKSEL_A1 | 58562306a36Sopenharmony_ci OPORTMXCTR2_MSSEL_MASTER | 58662306a36Sopenharmony_ci OPORTMXCTR2_EXTLSIFSSEL_36 | 58762306a36Sopenharmony_ci OPORTMXCTR2_DACCKSEL_1_2; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci regmap_write(r, OPORTMXCTR2(sub->swm->oport.map), v); 59062306a36Sopenharmony_ci } else { 59162306a36Sopenharmony_ci v = IPORTMXCTR2_ACLKSEL_A1 | 59262306a36Sopenharmony_ci IPORTMXCTR2_MSSEL_SLAVE | 59362306a36Sopenharmony_ci IPORTMXCTR2_EXTLSIFSSEL_36 | 59462306a36Sopenharmony_ci IPORTMXCTR2_DACCKSEL_1_2; 59562306a36Sopenharmony_ci regmap_write(r, IPORTMXCTR2(sub->swm->iport.map), v); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/** 60262306a36Sopenharmony_ci * aio_port_set_param - set parameters of AIO port block 60362306a36Sopenharmony_ci * @sub: the AIO substream pointer 60462306a36Sopenharmony_ci * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. 60562306a36Sopenharmony_ci * This parameter has no effect if substream is I2S or PCM. 60662306a36Sopenharmony_ci * @params: hardware parameters of ALSA 60762306a36Sopenharmony_ci * 60862306a36Sopenharmony_ci * Set suitable setting to input/output port block of AIO to process the 60962306a36Sopenharmony_ci * specified in params. 61062306a36Sopenharmony_ci * 61162306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ciint aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, 61462306a36Sopenharmony_ci const struct snd_pcm_hw_params *params) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 61762306a36Sopenharmony_ci unsigned int rate; 61862306a36Sopenharmony_ci u32 v; 61962306a36Sopenharmony_ci int ret; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!pass_through) { 62262306a36Sopenharmony_ci if (sub->swm->type == PORT_TYPE_EVE || 62362306a36Sopenharmony_ci sub->swm->type == PORT_TYPE_CONV) { 62462306a36Sopenharmony_ci rate = 48000; 62562306a36Sopenharmony_ci } else { 62662306a36Sopenharmony_ci rate = params_rate(params); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci ret = aio_port_set_ch(sub); 63062306a36Sopenharmony_ci if (ret) 63162306a36Sopenharmony_ci return ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ret = aio_port_set_rate(sub, rate); 63462306a36Sopenharmony_ci if (ret) 63562306a36Sopenharmony_ci return ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ret = aio_port_set_fmt(sub); 63862306a36Sopenharmony_ci if (ret) 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ret = aio_port_set_clk(sub); 64362306a36Sopenharmony_ci if (ret) 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 64762306a36Sopenharmony_ci if (pass_through) 64862306a36Sopenharmony_ci v = OPORTMXCTR3_SRCSEL_STREAM | 64962306a36Sopenharmony_ci OPORTMXCTR3_VALID_STREAM; 65062306a36Sopenharmony_ci else 65162306a36Sopenharmony_ci v = OPORTMXCTR3_SRCSEL_PCM | 65262306a36Sopenharmony_ci OPORTMXCTR3_VALID_PCM; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci v |= OPORTMXCTR3_IECTHUR_IECOUT | 65562306a36Sopenharmony_ci OPORTMXCTR3_PMSEL_PAUSE | 65662306a36Sopenharmony_ci OPORTMXCTR3_PMSW_MUTE_OFF; 65762306a36Sopenharmony_ci regmap_write(r, OPORTMXCTR3(sub->swm->oport.map), v); 65862306a36Sopenharmony_ci } else { 65962306a36Sopenharmony_ci regmap_write(r, IPORTMXACLKSEL0EX(sub->swm->iport.map), 66062306a36Sopenharmony_ci IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL); 66162306a36Sopenharmony_ci regmap_write(r, IPORTMXEXNOE(sub->swm->iport.map), 66262306a36Sopenharmony_ci IPORTMXEXNOE_PCMINOE_INPUT); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return 0; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/** 66962306a36Sopenharmony_ci * aio_port_set_enable - start or stop of AIO port block 67062306a36Sopenharmony_ci * @sub: the AIO substream pointer 67162306a36Sopenharmony_ci * @enable: zero to stop the block, otherwise to start 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * Start or stop the signal input/output port block of AIO. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_civoid aio_port_set_enable(struct uniphier_aio_sub *sub, int enable) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 68062306a36Sopenharmony_ci regmap_write(r, OPORTMXPATH(sub->swm->oport.map), 68162306a36Sopenharmony_ci sub->swm->oif.map); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXMASK(sub->swm->oport.map), 68462306a36Sopenharmony_ci OPORTMXMASK_IUDXMSK_MASK | 68562306a36Sopenharmony_ci OPORTMXMASK_IUXCKMSK_MASK | 68662306a36Sopenharmony_ci OPORTMXMASK_DXMSK_MASK | 68762306a36Sopenharmony_ci OPORTMXMASK_XCKMSK_MASK, 68862306a36Sopenharmony_ci OPORTMXMASK_IUDXMSK_OFF | 68962306a36Sopenharmony_ci OPORTMXMASK_IUXCKMSK_OFF | 69062306a36Sopenharmony_ci OPORTMXMASK_DXMSK_OFF | 69162306a36Sopenharmony_ci OPORTMXMASK_XCKMSK_OFF); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (enable) 69462306a36Sopenharmony_ci regmap_write(r, AOUTENCTR0, BIT(sub->swm->oport.map)); 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci regmap_write(r, AOUTENCTR1, BIT(sub->swm->oport.map)); 69762306a36Sopenharmony_ci } else { 69862306a36Sopenharmony_ci regmap_update_bits(r, IPORTMXMASK(sub->swm->iport.map), 69962306a36Sopenharmony_ci IPORTMXMASK_IUXCKMSK_MASK | 70062306a36Sopenharmony_ci IPORTMXMASK_XCKMSK_MASK, 70162306a36Sopenharmony_ci IPORTMXMASK_IUXCKMSK_OFF | 70262306a36Sopenharmony_ci IPORTMXMASK_XCKMSK_OFF); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (enable) 70562306a36Sopenharmony_ci regmap_update_bits(r, 70662306a36Sopenharmony_ci IPORTMXCTR2(sub->swm->iport.map), 70762306a36Sopenharmony_ci IPORTMXCTR2_REQEN_MASK, 70862306a36Sopenharmony_ci IPORTMXCTR2_REQEN_ENABLE); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci regmap_update_bits(r, 71162306a36Sopenharmony_ci IPORTMXCTR2(sub->swm->iport.map), 71262306a36Sopenharmony_ci IPORTMXCTR2_REQEN_MASK, 71362306a36Sopenharmony_ci IPORTMXCTR2_REQEN_DISABLE); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/** 71862306a36Sopenharmony_ci * aio_port_get_volume - get volume of AIO port block 71962306a36Sopenharmony_ci * @sub: the AIO substream pointer 72062306a36Sopenharmony_ci * 72162306a36Sopenharmony_ci * Return: current volume, range is 0x0000 - 0xffff 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ciint aio_port_get_volume(struct uniphier_aio_sub *sub) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 72662306a36Sopenharmony_ci u32 v; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci regmap_read(r, OPORTMXTYVOLGAINSTATUS(sub->swm->oport.map, 0), &v); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return FIELD_GET(OPORTMXTYVOLGAINSTATUS_CUR_MASK, v); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci/** 73462306a36Sopenharmony_ci * aio_port_set_volume - set volume of AIO port block 73562306a36Sopenharmony_ci * @sub: the AIO substream pointer 73662306a36Sopenharmony_ci * @vol: target volume, range is 0x0000 - 0xffff. 73762306a36Sopenharmony_ci * 73862306a36Sopenharmony_ci * Change digital volume and perfome fade-out/fade-in effect for specified 73962306a36Sopenharmony_ci * output slot of port. Gained PCM value can calculate as the following: 74062306a36Sopenharmony_ci * Gained = Original * vol / 0x4000 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_civoid aio_port_set_volume(struct uniphier_aio_sub *sub, int vol) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 74562306a36Sopenharmony_ci int oport_map = sub->swm->oport.map; 74662306a36Sopenharmony_ci int cur, diff, slope = 0, fs; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_INPUT) 74962306a36Sopenharmony_ci return; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci cur = aio_port_get_volume(sub); 75262306a36Sopenharmony_ci diff = abs(vol - cur); 75362306a36Sopenharmony_ci fs = params_rate(&sub->params); 75462306a36Sopenharmony_ci if (fs) 75562306a36Sopenharmony_ci slope = diff / AUD_VOL_FADE_TIME * 1000 / fs; 75662306a36Sopenharmony_ci slope = max(1, slope); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYVOLPARA1(oport_map, 0), 75962306a36Sopenharmony_ci OPORTMXTYVOLPARA1_SLOPEU_MASK, slope << 16); 76062306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), 76162306a36Sopenharmony_ci OPORTMXTYVOLPARA2_TARGET_MASK, vol); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (cur < vol) 76462306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), 76562306a36Sopenharmony_ci OPORTMXTYVOLPARA2_FADE_MASK, 76662306a36Sopenharmony_ci OPORTMXTYVOLPARA2_FADE_FADEIN); 76762306a36Sopenharmony_ci else 76862306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0), 76962306a36Sopenharmony_ci OPORTMXTYVOLPARA2_FADE_MASK, 77062306a36Sopenharmony_ci OPORTMXTYVOLPARA2_FADE_FADEOUT); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci regmap_write(r, AOUTFADECTR0, BIT(oport_map)); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/** 77662306a36Sopenharmony_ci * aio_if_set_param - set parameters of AIO DMA I/F block 77762306a36Sopenharmony_ci * @sub: the AIO substream pointer 77862306a36Sopenharmony_ci * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. 77962306a36Sopenharmony_ci * This parameter has no effect if substream is I2S or PCM. 78062306a36Sopenharmony_ci * 78162306a36Sopenharmony_ci * Set suitable setting to DMA interface block of AIO to process the 78262306a36Sopenharmony_ci * specified in settings. 78362306a36Sopenharmony_ci * 78462306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ciint aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 78962306a36Sopenharmony_ci u32 memfmt, v; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 79262306a36Sopenharmony_ci if (pass_through) { 79362306a36Sopenharmony_ci v = PBOUTMXCTR0_ENDIAN_0123 | 79462306a36Sopenharmony_ci PBOUTMXCTR0_MEMFMT_STREAM; 79562306a36Sopenharmony_ci } else { 79662306a36Sopenharmony_ci switch (params_channels(&sub->params)) { 79762306a36Sopenharmony_ci case 2: 79862306a36Sopenharmony_ci memfmt = PBOUTMXCTR0_MEMFMT_2CH; 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci case 6: 80162306a36Sopenharmony_ci memfmt = PBOUTMXCTR0_MEMFMT_6CH; 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case 8: 80462306a36Sopenharmony_ci memfmt = PBOUTMXCTR0_MEMFMT_8CH; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci default: 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci v = PBOUTMXCTR0_ENDIAN_3210 | memfmt; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v); 81362306a36Sopenharmony_ci regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0); 81462306a36Sopenharmony_ci } else { 81562306a36Sopenharmony_ci regmap_write(r, PBINMXCTR(sub->swm->iif.map), 81662306a36Sopenharmony_ci PBINMXCTR_NCONNECT_CONNECT | 81762306a36Sopenharmony_ci PBINMXCTR_INOUTSEL_IN | 81862306a36Sopenharmony_ci (sub->swm->iport.map << PBINMXCTR_PBINSEL_SHIFT) | 81962306a36Sopenharmony_ci PBINMXCTR_ENDIAN_3210 | 82062306a36Sopenharmony_ci PBINMXCTR_MEMFMT_D0); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/** 82762306a36Sopenharmony_ci * aio_oport_set_stream_type - set parameters of AIO playback port block 82862306a36Sopenharmony_ci * @sub: the AIO substream pointer 82962306a36Sopenharmony_ci * @pc: Pc type of IEC61937 83062306a36Sopenharmony_ci * 83162306a36Sopenharmony_ci * Set special setting to output port block of AIO to output the stream 83262306a36Sopenharmony_ci * via S/PDIF. 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ciint aio_oport_set_stream_type(struct uniphier_aio_sub *sub, 83762306a36Sopenharmony_ci enum IEC61937_PC pc) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 84062306a36Sopenharmony_ci u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci switch (pc) { 84362306a36Sopenharmony_ci case IEC61937_PC_AC3: 84462306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_AC3 | 84562306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_AC3; 84662306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_AC3; 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci case IEC61937_PC_MPA: 84962306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_MPA | 85062306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_MPA; 85162306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_MPA; 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci case IEC61937_PC_MP3: 85462306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_MP3 | 85562306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_MP3; 85662306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_MP3; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci case IEC61937_PC_DTS1: 85962306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_DTS1 | 86062306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_DTS1; 86162306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_DTS1; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case IEC61937_PC_DTS2: 86462306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_DTS2 | 86562306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_DTS2; 86662306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_DTS2; 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci case IEC61937_PC_DTS3: 86962306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_DTS3 | 87062306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_DTS3; 87162306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_DTS3; 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci case IEC61937_PC_AAC: 87462306a36Sopenharmony_ci repet = OPORTMXREPET_STRLENGTH_AAC | 87562306a36Sopenharmony_ci OPORTMXREPET_PMLENGTH_AAC; 87662306a36Sopenharmony_ci pause |= OPORTMXPAUDAT_PAUSEPD_AAC; 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci case IEC61937_PC_PAUSE: 87962306a36Sopenharmony_ci /* Do nothing */ 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci regmap_write(r, OPORTMXREPET(sub->swm->oport.map), repet); 88462306a36Sopenharmony_ci regmap_write(r, OPORTMXPAUDAT(sub->swm->oport.map), pause); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/** 89062306a36Sopenharmony_ci * aio_src_reset - reset AIO SRC block 89162306a36Sopenharmony_ci * @sub: the AIO substream pointer 89262306a36Sopenharmony_ci * 89362306a36Sopenharmony_ci * Resets the digital signal input/output port with sampling rate converter 89462306a36Sopenharmony_ci * block of AIO. 89562306a36Sopenharmony_ci * This function has no effect if substream is not supported rate converter. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_civoid aio_src_reset(struct uniphier_aio_sub *sub) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (sub->swm->dir != PORT_DIR_OUTPUT) 90262306a36Sopenharmony_ci return; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci regmap_write(r, AOUTSRCRSTCTR0, BIT(sub->swm->oport.map)); 90562306a36Sopenharmony_ci regmap_write(r, AOUTSRCRSTCTR1, BIT(sub->swm->oport.map)); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci/** 90962306a36Sopenharmony_ci * aio_src_set_param - set parameters of AIO SRC block 91062306a36Sopenharmony_ci * @sub: the AIO substream pointer 91162306a36Sopenharmony_ci * @params: hardware parameters of ALSA 91262306a36Sopenharmony_ci * 91362306a36Sopenharmony_ci * Set suitable setting to input/output port with sampling rate converter 91462306a36Sopenharmony_ci * block of AIO to process the specified in params. 91562306a36Sopenharmony_ci * This function has no effect if substream is not supported rate converter. 91662306a36Sopenharmony_ci * 91762306a36Sopenharmony_ci * Return: Zero if successful, otherwise a negative value on error. 91862306a36Sopenharmony_ci */ 91962306a36Sopenharmony_ciint aio_src_set_param(struct uniphier_aio_sub *sub, 92062306a36Sopenharmony_ci const struct snd_pcm_hw_params *params) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 92362306a36Sopenharmony_ci u32 v; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (sub->swm->dir != PORT_DIR_OUTPUT) 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci regmap_write(r, OPORTMXSRC1CTR(sub->swm->oport.map), 92962306a36Sopenharmony_ci OPORTMXSRC1CTR_THMODE_SRC | 93062306a36Sopenharmony_ci OPORTMXSRC1CTR_SRCPATH_CALC | 93162306a36Sopenharmony_ci OPORTMXSRC1CTR_SYNC_ASYNC | 93262306a36Sopenharmony_ci OPORTMXSRC1CTR_FSIIPSEL_INNER | 93362306a36Sopenharmony_ci OPORTMXSRC1CTR_FSISEL_ACLK); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci switch (params_rate(params)) { 93662306a36Sopenharmony_ci default: 93762306a36Sopenharmony_ci case 48000: 93862306a36Sopenharmony_ci v = OPORTMXRATE_I_ACLKSEL_APLLA1 | 93962306a36Sopenharmony_ci OPORTMXRATE_I_MCKSEL_36 | 94062306a36Sopenharmony_ci OPORTMXRATE_I_FSSEL_48; 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci case 44100: 94362306a36Sopenharmony_ci v = OPORTMXRATE_I_ACLKSEL_APLLA2 | 94462306a36Sopenharmony_ci OPORTMXRATE_I_MCKSEL_33 | 94562306a36Sopenharmony_ci OPORTMXRATE_I_FSSEL_44_1; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case 32000: 94862306a36Sopenharmony_ci v = OPORTMXRATE_I_ACLKSEL_APLLA1 | 94962306a36Sopenharmony_ci OPORTMXRATE_I_MCKSEL_36 | 95062306a36Sopenharmony_ci OPORTMXRATE_I_FSSEL_32; 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci regmap_write(r, OPORTMXRATE_I(sub->swm->oport.map), 95562306a36Sopenharmony_ci v | OPORTMXRATE_I_ACLKSRC_APLL | 95662306a36Sopenharmony_ci OPORTMXRATE_I_LRCKSTP_STOP); 95762306a36Sopenharmony_ci regmap_update_bits(r, OPORTMXRATE_I(sub->swm->oport.map), 95862306a36Sopenharmony_ci OPORTMXRATE_I_LRCKSTP_MASK, 95962306a36Sopenharmony_ci OPORTMXRATE_I_LRCKSTP_START); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci return 0; 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciint aio_srcif_set_param(struct uniphier_aio_sub *sub) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci regmap_write(r, PBINMXCTR(sub->swm->iif.map), 96962306a36Sopenharmony_ci PBINMXCTR_NCONNECT_CONNECT | 97062306a36Sopenharmony_ci PBINMXCTR_INOUTSEL_OUT | 97162306a36Sopenharmony_ci (sub->swm->oport.map << PBINMXCTR_PBINSEL_SHIFT) | 97262306a36Sopenharmony_ci PBINMXCTR_ENDIAN_3210 | 97362306a36Sopenharmony_ci PBINMXCTR_MEMFMT_D0); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ciint aio_srcch_set_param(struct uniphier_aio_sub *sub) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->och.map), 98362306a36Sopenharmony_ci CDA2D_CHMXCTRL1_INDSIZE_INFINITE); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->och.map), 98662306a36Sopenharmony_ci CDA2D_CHMXAMODE_ENDIAN_3210 | 98762306a36Sopenharmony_ci CDA2D_CHMXAMODE_AUPDT_FIX | 98862306a36Sopenharmony_ci CDA2D_CHMXAMODE_TYPE_NORMAL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->och.map), 99162306a36Sopenharmony_ci CDA2D_CHMXAMODE_ENDIAN_3210 | 99262306a36Sopenharmony_ci CDA2D_CHMXAMODE_AUPDT_INC | 99362306a36Sopenharmony_ci CDA2D_CHMXAMODE_TYPE_RING | 99462306a36Sopenharmony_ci (sub->swm->och.map << CDA2D_CHMXAMODE_RSSEL_SHIFT)); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_civoid aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 100262306a36Sopenharmony_ci u32 v; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (enable) 100562306a36Sopenharmony_ci v = CDA2D_STRT0_STOP_START; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci v = CDA2D_STRT0_STOP_STOP; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci regmap_write(r, CDA2D_STRT0, 101062306a36Sopenharmony_ci v | BIT(sub->swm->och.map)); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ciint aiodma_ch_set_param(struct uniphier_aio_sub *sub) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 101662306a36Sopenharmony_ci u32 v; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->ch.map), 101962306a36Sopenharmony_ci CDA2D_CHMXCTRL1_INDSIZE_INFINITE); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci v = CDA2D_CHMXAMODE_ENDIAN_3210 | 102262306a36Sopenharmony_ci CDA2D_CHMXAMODE_AUPDT_INC | 102362306a36Sopenharmony_ci CDA2D_CHMXAMODE_TYPE_NORMAL | 102462306a36Sopenharmony_ci (sub->swm->rb.map << CDA2D_CHMXAMODE_RSSEL_SHIFT); 102562306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) 102662306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->ch.map), v); 102762306a36Sopenharmony_ci else 102862306a36Sopenharmony_ci regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->ch.map), v); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_civoid aiodma_ch_set_enable(struct uniphier_aio_sub *sub, int enable) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (enable) { 103862306a36Sopenharmony_ci regmap_write(r, CDA2D_STRT0, 103962306a36Sopenharmony_ci CDA2D_STRT0_STOP_START | BIT(sub->swm->ch.map)); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci regmap_update_bits(r, INTRBIM(0), 104262306a36Sopenharmony_ci BIT(sub->swm->rb.map), 104362306a36Sopenharmony_ci BIT(sub->swm->rb.map)); 104462306a36Sopenharmony_ci } else { 104562306a36Sopenharmony_ci regmap_write(r, CDA2D_STRT0, 104662306a36Sopenharmony_ci CDA2D_STRT0_STOP_STOP | BIT(sub->swm->ch.map)); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci regmap_update_bits(r, INTRBIM(0), 104962306a36Sopenharmony_ci BIT(sub->swm->rb.map), 105062306a36Sopenharmony_ci 0); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic u64 aiodma_rb_get_rp(struct uniphier_aio_sub *sub) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 105762306a36Sopenharmony_ci u32 pos_u, pos_l; 105862306a36Sopenharmony_ci int i; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci regmap_write(r, CDA2D_RDPTRLOAD, 106162306a36Sopenharmony_ci CDA2D_RDPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); 106262306a36Sopenharmony_ci /* Wait for setup */ 106362306a36Sopenharmony_ci for (i = 0; i < 6; i++) 106462306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l); 106762306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), &pos_u); 106862306a36Sopenharmony_ci pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci return ((u64)pos_u << 32) | pos_l; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic void aiodma_rb_set_rp(struct uniphier_aio_sub *sub, u64 pos) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 107662306a36Sopenharmony_ci u32 tmp; 107762306a36Sopenharmony_ci int i; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), (u32)pos); 108062306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), (u32)(pos >> 32)); 108162306a36Sopenharmony_ci regmap_write(r, CDA2D_RDPTRLOAD, BIT(sub->swm->rb.map)); 108262306a36Sopenharmony_ci /* Wait for setup */ 108362306a36Sopenharmony_ci for (i = 0; i < 6; i++) 108462306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &tmp); 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic u64 aiodma_rb_get_wp(struct uniphier_aio_sub *sub) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 109062306a36Sopenharmony_ci u32 pos_u, pos_l; 109162306a36Sopenharmony_ci int i; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci regmap_write(r, CDA2D_WRPTRLOAD, 109462306a36Sopenharmony_ci CDA2D_WRPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); 109562306a36Sopenharmony_ci /* Wait for setup */ 109662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 109762306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l); 110062306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), &pos_u); 110162306a36Sopenharmony_ci pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci return ((u64)pos_u << 32) | pos_l; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic void aiodma_rb_set_wp(struct uniphier_aio_sub *sub, u64 pos) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 110962306a36Sopenharmony_ci u32 tmp; 111062306a36Sopenharmony_ci int i; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), 111362306a36Sopenharmony_ci lower_32_bits(pos)); 111462306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), 111562306a36Sopenharmony_ci upper_32_bits(pos)); 111662306a36Sopenharmony_ci regmap_write(r, CDA2D_WRPTRLOAD, BIT(sub->swm->rb.map)); 111762306a36Sopenharmony_ci /* Wait for setup */ 111862306a36Sopenharmony_ci for (i = 0; i < 6; i++) 111962306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &tmp); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ciint aiodma_rb_set_threshold(struct uniphier_aio_sub *sub, u64 size, u32 th) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (size <= th) 112762306a36Sopenharmony_ci return -EINVAL; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXBTH(sub->swm->rb.map), th); 113062306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXRTH(sub->swm->rb.map), th); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ciint aiodma_rb_set_buffer(struct uniphier_aio_sub *sub, u64 start, u64 end, 113662306a36Sopenharmony_ci int period) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 113962306a36Sopenharmony_ci u64 size = end - start; 114062306a36Sopenharmony_ci int ret; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (end < start || period < 0) 114362306a36Sopenharmony_ci return -EINVAL; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXCNFG(sub->swm->rb.map), 0); 114662306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXBGNADRS(sub->swm->rb.map), 114762306a36Sopenharmony_ci lower_32_bits(start)); 114862306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXBGNADRSU(sub->swm->rb.map), 114962306a36Sopenharmony_ci upper_32_bits(start)); 115062306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXENDADRS(sub->swm->rb.map), 115162306a36Sopenharmony_ci lower_32_bits(end)); 115262306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXENDADRSU(sub->swm->rb.map), 115362306a36Sopenharmony_ci upper_32_bits(end)); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci regmap_write(r, CDA2D_RBADRSLOAD, BIT(sub->swm->rb.map)); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci ret = aiodma_rb_set_threshold(sub, size, 2 * period); 115862306a36Sopenharmony_ci if (ret) 115962306a36Sopenharmony_ci return ret; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 116262306a36Sopenharmony_ci aiodma_rb_set_rp(sub, start); 116362306a36Sopenharmony_ci aiodma_rb_set_wp(sub, end - period); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map), 116662306a36Sopenharmony_ci CDA2D_RBMXIX_SPACE, 116762306a36Sopenharmony_ci CDA2D_RBMXIX_SPACE); 116862306a36Sopenharmony_ci } else { 116962306a36Sopenharmony_ci aiodma_rb_set_rp(sub, end - period); 117062306a36Sopenharmony_ci aiodma_rb_set_wp(sub, start); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map), 117362306a36Sopenharmony_ci CDA2D_RBMXIX_REMAIN, 117462306a36Sopenharmony_ci CDA2D_RBMXIX_REMAIN); 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci sub->threshold = 2 * period; 117862306a36Sopenharmony_ci sub->rd_offs = 0; 117962306a36Sopenharmony_ci sub->wr_offs = 0; 118062306a36Sopenharmony_ci sub->rd_org = 0; 118162306a36Sopenharmony_ci sub->wr_org = 0; 118262306a36Sopenharmony_ci sub->rd_total = 0; 118362306a36Sopenharmony_ci sub->wr_total = 0; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return 0; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_civoid aiodma_rb_sync(struct uniphier_aio_sub *sub, u64 start, u64 size, 118962306a36Sopenharmony_ci int period) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) { 119262306a36Sopenharmony_ci sub->rd_offs = aiodma_rb_get_rp(sub) - start; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (sub->use_mmap) { 119562306a36Sopenharmony_ci sub->threshold = 2 * period; 119662306a36Sopenharmony_ci aiodma_rb_set_threshold(sub, size, 2 * period); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci sub->wr_offs = sub->rd_offs - period; 119962306a36Sopenharmony_ci if (sub->rd_offs < period) 120062306a36Sopenharmony_ci sub->wr_offs += size; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci aiodma_rb_set_wp(sub, sub->wr_offs + start); 120362306a36Sopenharmony_ci } else { 120462306a36Sopenharmony_ci sub->wr_offs = aiodma_rb_get_wp(sub) - start; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (sub->use_mmap) { 120762306a36Sopenharmony_ci sub->threshold = 2 * period; 120862306a36Sopenharmony_ci aiodma_rb_set_threshold(sub, size, 2 * period); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci sub->rd_offs = sub->wr_offs - period; 121162306a36Sopenharmony_ci if (sub->wr_offs < period) 121262306a36Sopenharmony_ci sub->rd_offs += size; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci aiodma_rb_set_rp(sub, sub->rd_offs + start); 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci sub->rd_total += sub->rd_offs - sub->rd_org; 121862306a36Sopenharmony_ci if (sub->rd_offs < sub->rd_org) 121962306a36Sopenharmony_ci sub->rd_total += size; 122062306a36Sopenharmony_ci sub->wr_total += sub->wr_offs - sub->wr_org; 122162306a36Sopenharmony_ci if (sub->wr_offs < sub->wr_org) 122262306a36Sopenharmony_ci sub->wr_total += size; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci sub->rd_org = sub->rd_offs; 122562306a36Sopenharmony_ci sub->wr_org = sub->wr_offs; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_cibool aiodma_rb_is_irq(struct uniphier_aio_sub *sub) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 123162306a36Sopenharmony_ci u32 ir; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci regmap_read(r, CDA2D_RBMXIR(sub->swm->rb.map), &ir); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) 123662306a36Sopenharmony_ci return !!(ir & CDA2D_RBMXIX_SPACE); 123762306a36Sopenharmony_ci else 123862306a36Sopenharmony_ci return !!(ir & CDA2D_RBMXIX_REMAIN); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_civoid aiodma_rb_clear_irq(struct uniphier_aio_sub *sub) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci struct regmap *r = sub->aio->chip->regmap; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (sub->swm->dir == PORT_DIR_OUTPUT) 124662306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map), 124762306a36Sopenharmony_ci CDA2D_RBMXIX_SPACE); 124862306a36Sopenharmony_ci else 124962306a36Sopenharmony_ci regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map), 125062306a36Sopenharmony_ci CDA2D_RBMXIX_REMAIN); 125162306a36Sopenharmony_ci} 1252