brintos

brintos / linux-shallow public Read only

0
0
Text · 31.1 KiB · 0eba607 Raw
1252 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Socionext UniPhier AIO ALSA common driver.4//5// Copyright (c) 2016-2018 Socionext Inc.6 7#include <linux/bitfield.h>8#include <linux/errno.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <sound/core.h>12#include <sound/pcm.h>13#include <sound/pcm_params.h>14#include <sound/soc.h>15 16#include "aio.h"17#include "aio-reg.h"18 19static u64 rb_cnt(u64 wr, u64 rd, u64 len)20{21	if (rd <= wr)22		return wr - rd;23	else24		return len - (rd - wr);25}26 27static u64 rb_cnt_to_end(u64 wr, u64 rd, u64 len)28{29	if (rd <= wr)30		return wr - rd;31	else32		return len - rd;33}34 35static u64 rb_space(u64 wr, u64 rd, u64 len)36{37	if (rd <= wr)38		return len - (wr - rd) - 8;39	else40		return rd - wr - 8;41}42 43static u64 rb_space_to_end(u64 wr, u64 rd, u64 len)44{45	if (rd > wr)46		return rd - wr - 8;47	else if (rd > 0)48		return len - wr;49	else50		return len - wr - 8;51}52 53u64 aio_rb_cnt(struct uniphier_aio_sub *sub)54{55	return rb_cnt(sub->wr_offs, sub->rd_offs, sub->compr_bytes);56}57 58u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub)59{60	return rb_cnt_to_end(sub->wr_offs, sub->rd_offs, sub->compr_bytes);61}62 63u64 aio_rb_space(struct uniphier_aio_sub *sub)64{65	return rb_space(sub->wr_offs, sub->rd_offs, sub->compr_bytes);66}67 68u64 aio_rb_space_to_end(struct uniphier_aio_sub *sub)69{70	return rb_space_to_end(sub->wr_offs, sub->rd_offs, sub->compr_bytes);71}72 73/**74 * aio_iecout_set_enable - setup IEC output via SoC glue75 * @chip: the AIO chip pointer76 * @enable: false to stop the output, true to start77 *78 * Set enabled or disabled S/PDIF signal output to out of SoC via AOnIEC pins.79 * This function need to call at driver startup.80 *81 * The regmap of SoC glue is specified by 'socionext,syscon' optional property82 * of DT. This function has no effect if no property.83 */84void aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable)85{86	struct regmap *r = chip->regmap_sg;87 88	if (!r)89		return;90 91	regmap_write(r, SG_AOUTEN, (enable) ? ~0 : 0);92}93 94/**95 * aio_chip_set_pll - set frequency to audio PLL96 * @chip: the AIO chip pointer97 * @pll_id: PLL98 * @freq: frequency in Hz, 0 is ignored99 *100 * Sets frequency of audio PLL. This function can be called anytime,101 * but it takes time till PLL is locked.102 *103 * Return: Zero if successful, otherwise a negative value on error.104 */105int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id,106		     unsigned int freq)107{108	struct device *dev = &chip->pdev->dev;109	struct regmap *r = chip->regmap;110	int shift;111	u32 v;112 113	/* Not change */114	if (freq == 0)115		return 0;116 117	switch (pll_id) {118	case AUD_PLL_A1:119		shift = 0;120		break;121	case AUD_PLL_F1:122		shift = 1;123		break;124	case AUD_PLL_A2:125		shift = 2;126		break;127	case AUD_PLL_F2:128		shift = 3;129		break;130	default:131		dev_err(dev, "PLL(%d) not supported\n", pll_id);132		return -EINVAL;133	}134 135	switch (freq) {136	case 36864000:137		v = A2APLLCTR1_APLLX_36MHZ;138		break;139	case 33868800:140		v = A2APLLCTR1_APLLX_33MHZ;141		break;142	default:143		dev_err(dev, "PLL frequency not supported(%d)\n", freq);144		return -EINVAL;145	}146	chip->plls[pll_id].freq = freq;147 148	regmap_update_bits(r, A2APLLCTR1, A2APLLCTR1_APLLX_MASK << shift,149			   v << shift);150 151	return 0;152}153 154/**155 * aio_chip_init - initialize AIO whole settings156 * @chip: the AIO chip pointer157 *158 * Sets AIO fixed and whole device settings to AIO.159 * This function need to call once at driver startup.160 *161 * The register area that is changed by this function is shared by all162 * modules of AIO. But there is not race condition since this function163 * has always set the same initialize values.164 */165void aio_chip_init(struct uniphier_aio_chip *chip)166{167	struct regmap *r = chip->regmap;168 169	regmap_update_bits(r, A2APLLCTR0,170			   A2APLLCTR0_APLLXPOW_MASK,171			   A2APLLCTR0_APLLXPOW_PWON);172 173	regmap_update_bits(r, A2EXMCLKSEL0,174			   A2EXMCLKSEL0_EXMCLK_MASK,175			   A2EXMCLKSEL0_EXMCLK_OUTPUT);176 177	regmap_update_bits(r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK,178			   A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 |179			   A2AIOINPUTSEL_RXSEL_PCMI2_SIF |180			   A2AIOINPUTSEL_RXSEL_PCMI3_EVEA |181			   A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1);182 183	if (chip->chip_spec->addr_ext)184		regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK,185				   CDA2D_TEST_DDR_MODE_EXTON0);186	else187		regmap_update_bits(r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK,188				   CDA2D_TEST_DDR_MODE_EXTOFF1);189}190 191/**192 * aio_init - initialize AIO substream193 * @sub: the AIO substream pointer194 *195 * Sets fixed settings of each AIO substreams.196 * This function need to call once at substream startup.197 *198 * Return: Zero if successful, otherwise a negative value on error.199 */200int aio_init(struct uniphier_aio_sub *sub)201{202	struct device *dev = &sub->aio->chip->pdev->dev;203	struct regmap *r = sub->aio->chip->regmap;204 205	regmap_write(r, A2RBNMAPCTR0(sub->swm->rb.hw),206		     MAPCTR0_EN | sub->swm->rb.map);207	regmap_write(r, A2CHNMAPCTR0(sub->swm->ch.hw),208		     MAPCTR0_EN | sub->swm->ch.map);209 210	switch (sub->swm->type) {211	case PORT_TYPE_I2S:212	case PORT_TYPE_SPDIF:213	case PORT_TYPE_EVE:214		if (sub->swm->dir == PORT_DIR_INPUT) {215			regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw),216				     MAPCTR0_EN | sub->swm->iif.map);217			regmap_write(r, A2IPORTNMAPCTR0(sub->swm->iport.hw),218				     MAPCTR0_EN | sub->swm->iport.map);219		} else {220			regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw),221				     MAPCTR0_EN | sub->swm->oif.map);222			regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw),223				     MAPCTR0_EN | sub->swm->oport.map);224		}225		break;226	case PORT_TYPE_CONV:227		regmap_write(r, A2OIFNMAPCTR0(sub->swm->oif.hw),228			     MAPCTR0_EN | sub->swm->oif.map);229		regmap_write(r, A2OPORTNMAPCTR0(sub->swm->oport.hw),230			     MAPCTR0_EN | sub->swm->oport.map);231		regmap_write(r, A2CHNMAPCTR0(sub->swm->och.hw),232			     MAPCTR0_EN | sub->swm->och.map);233		regmap_write(r, A2IIFNMAPCTR0(sub->swm->iif.hw),234			     MAPCTR0_EN | sub->swm->iif.map);235		break;236	default:237		dev_err(dev, "Unknown port type %d.\n", sub->swm->type);238		return -EINVAL;239	}240 241	return 0;242}243 244/**245 * aio_port_reset - reset AIO port block246 * @sub: the AIO substream pointer247 *248 * Resets the digital signal input/output port block of AIO.249 */250void aio_port_reset(struct uniphier_aio_sub *sub)251{252	struct regmap *r = sub->aio->chip->regmap;253 254	if (sub->swm->dir == PORT_DIR_OUTPUT) {255		regmap_write(r, AOUTRSTCTR0, BIT(sub->swm->oport.map));256		regmap_write(r, AOUTRSTCTR1, BIT(sub->swm->oport.map));257	} else {258		regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map),259				   IPORTMXRSTCTR_RSTPI_MASK,260				   IPORTMXRSTCTR_RSTPI_RESET);261		regmap_update_bits(r, IPORTMXRSTCTR(sub->swm->iport.map),262				   IPORTMXRSTCTR_RSTPI_MASK,263				   IPORTMXRSTCTR_RSTPI_RELEASE);264	}265}266 267/**268 * aio_port_set_ch - set channels of LPCM269 * @sub: the AIO substream pointer, PCM substream only270 *271 * Set suitable slot selecting to input/output port block of AIO.272 *273 * This function may return error if non-PCM substream.274 *275 * Return: Zero if successful, otherwise a negative value on error.276 */277static int aio_port_set_ch(struct uniphier_aio_sub *sub)278{279	struct regmap *r = sub->aio->chip->regmap;280	static const u32 slotsel_2ch[] = {281		0, 0, 0, 0, 0,282	};283	static const u32 slotsel_multi[] = {284		OPORTMXTYSLOTCTR_SLOTSEL_SLOT0,285		OPORTMXTYSLOTCTR_SLOTSEL_SLOT1,286		OPORTMXTYSLOTCTR_SLOTSEL_SLOT2,287		OPORTMXTYSLOTCTR_SLOTSEL_SLOT3,288		OPORTMXTYSLOTCTR_SLOTSEL_SLOT4,289	};290	u32 mode;291	const u32 *slotsel;292	int i;293 294	switch (params_channels(&sub->params)) {295	case 8:296	case 6:297		mode = OPORTMXTYSLOTCTR_MODE;298		slotsel = slotsel_multi;299		break;300	case 2:301		mode = 0;302		slotsel = slotsel_2ch;303		break;304	default:305		return -EINVAL;306	}307 308	for (i = 0; i < AUD_MAX_SLOTSEL; i++) {309		regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i),310				   OPORTMXTYSLOTCTR_MODE, mode);311		regmap_update_bits(r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i),312				   OPORTMXTYSLOTCTR_SLOTSEL_MASK, slotsel[i]);313	}314 315	return 0;316}317 318/**319 * aio_port_set_rate - set sampling rate of LPCM320 * @sub: the AIO substream pointer, PCM substream only321 * @rate: Sampling rate in Hz.322 *323 * Set suitable I2S format settings to input/output port block of AIO.324 * Parameter is specified by hw_params().325 *326 * This function may return error if non-PCM substream.327 *328 * Return: Zero if successful, otherwise a negative value on error.329 */330static int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate)331{332	struct regmap *r = sub->aio->chip->regmap;333	struct device *dev = &sub->aio->chip->pdev->dev;334	u32 v;335 336	if (sub->swm->dir == PORT_DIR_OUTPUT) {337		switch (rate) {338		case 8000:339			v = OPORTMXCTR1_FSSEL_8;340			break;341		case 11025:342			v = OPORTMXCTR1_FSSEL_11_025;343			break;344		case 12000:345			v = OPORTMXCTR1_FSSEL_12;346			break;347		case 16000:348			v = OPORTMXCTR1_FSSEL_16;349			break;350		case 22050:351			v = OPORTMXCTR1_FSSEL_22_05;352			break;353		case 24000:354			v = OPORTMXCTR1_FSSEL_24;355			break;356		case 32000:357			v = OPORTMXCTR1_FSSEL_32;358			break;359		case 44100:360			v = OPORTMXCTR1_FSSEL_44_1;361			break;362		case 48000:363			v = OPORTMXCTR1_FSSEL_48;364			break;365		case 88200:366			v = OPORTMXCTR1_FSSEL_88_2;367			break;368		case 96000:369			v = OPORTMXCTR1_FSSEL_96;370			break;371		case 176400:372			v = OPORTMXCTR1_FSSEL_176_4;373			break;374		case 192000:375			v = OPORTMXCTR1_FSSEL_192;376			break;377		default:378			dev_err(dev, "Rate not supported(%d)\n", rate);379			return -EINVAL;380		}381 382		regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map),383				   OPORTMXCTR1_FSSEL_MASK, v);384	} else {385		switch (rate) {386		case 8000:387			v = IPORTMXCTR1_FSSEL_8;388			break;389		case 11025:390			v = IPORTMXCTR1_FSSEL_11_025;391			break;392		case 12000:393			v = IPORTMXCTR1_FSSEL_12;394			break;395		case 16000:396			v = IPORTMXCTR1_FSSEL_16;397			break;398		case 22050:399			v = IPORTMXCTR1_FSSEL_22_05;400			break;401		case 24000:402			v = IPORTMXCTR1_FSSEL_24;403			break;404		case 32000:405			v = IPORTMXCTR1_FSSEL_32;406			break;407		case 44100:408			v = IPORTMXCTR1_FSSEL_44_1;409			break;410		case 48000:411			v = IPORTMXCTR1_FSSEL_48;412			break;413		case 88200:414			v = IPORTMXCTR1_FSSEL_88_2;415			break;416		case 96000:417			v = IPORTMXCTR1_FSSEL_96;418			break;419		case 176400:420			v = IPORTMXCTR1_FSSEL_176_4;421			break;422		case 192000:423			v = IPORTMXCTR1_FSSEL_192;424			break;425		default:426			dev_err(dev, "Rate not supported(%d)\n", rate);427			return -EINVAL;428		}429 430		regmap_update_bits(r, IPORTMXCTR1(sub->swm->iport.map),431				   IPORTMXCTR1_FSSEL_MASK, v);432	}433 434	return 0;435}436 437/**438 * aio_port_set_fmt - set format of I2S data439 * @sub: the AIO substream pointer, PCM substream only440 * This parameter has no effect if substream is I2S or PCM.441 *442 * Set suitable I2S format settings to input/output port block of AIO.443 * Parameter is specified by set_fmt().444 *445 * This function may return error if non-PCM substream.446 *447 * Return: Zero if successful, otherwise a negative value on error.448 */449static int aio_port_set_fmt(struct uniphier_aio_sub *sub)450{451	struct regmap *r = sub->aio->chip->regmap;452	struct device *dev = &sub->aio->chip->pdev->dev;453	u32 v;454 455	if (sub->swm->dir == PORT_DIR_OUTPUT) {456		switch (sub->aio->fmt) {457		case SND_SOC_DAIFMT_LEFT_J:458			v = OPORTMXCTR1_I2SLRSEL_LEFT;459			break;460		case SND_SOC_DAIFMT_RIGHT_J:461			v = OPORTMXCTR1_I2SLRSEL_RIGHT;462			break;463		case SND_SOC_DAIFMT_I2S:464			v = OPORTMXCTR1_I2SLRSEL_I2S;465			break;466		default:467			dev_err(dev, "Format is not supported(%d)\n",468				sub->aio->fmt);469			return -EINVAL;470		}471 472		v |= OPORTMXCTR1_OUTBITSEL_24;473		regmap_update_bits(r, OPORTMXCTR1(sub->swm->oport.map),474				   OPORTMXCTR1_I2SLRSEL_MASK |475				   OPORTMXCTR1_OUTBITSEL_MASK, v);476	} else {477		switch (sub->aio->fmt) {478		case SND_SOC_DAIFMT_LEFT_J:479			v = IPORTMXCTR1_LRSEL_LEFT;480			break;481		case SND_SOC_DAIFMT_RIGHT_J:482			v = IPORTMXCTR1_LRSEL_RIGHT;483			break;484		case SND_SOC_DAIFMT_I2S:485			v = IPORTMXCTR1_LRSEL_I2S;486			break;487		default:488			dev_err(dev, "Format is not supported(%d)\n",489				sub->aio->fmt);490			return -EINVAL;491		}492 493		v |= IPORTMXCTR1_OUTBITSEL_24 |494			IPORTMXCTR1_CHSEL_ALL;495		regmap_update_bits(r, IPORTMXCTR1(sub->swm->iport.map),496				   IPORTMXCTR1_LRSEL_MASK |497				   IPORTMXCTR1_OUTBITSEL_MASK |498				   IPORTMXCTR1_CHSEL_MASK, v);499	}500 501	return 0;502}503 504/**505 * aio_port_set_clk - set clock and divider of AIO port block506 * @sub: the AIO substream pointer507 *508 * Set suitable PLL clock divider and relational settings to509 * input/output port block of AIO. Parameters are specified by510 * set_sysclk() and set_pll().511 *512 * Return: Zero if successful, otherwise a negative value on error.513 */514static int aio_port_set_clk(struct uniphier_aio_sub *sub)515{516	struct uniphier_aio_chip *chip = sub->aio->chip;517	struct device *dev = &sub->aio->chip->pdev->dev;518	struct regmap *r = sub->aio->chip->regmap;519	static const u32 v_pll[] = {520		OPORTMXCTR2_ACLKSEL_A1, OPORTMXCTR2_ACLKSEL_F1,521		OPORTMXCTR2_ACLKSEL_A2, OPORTMXCTR2_ACLKSEL_F2,522		OPORTMXCTR2_ACLKSEL_A2PLL,523		OPORTMXCTR2_ACLKSEL_RX1,524	};525	static const u32 v_div[] = {526		OPORTMXCTR2_DACCKSEL_1_2, OPORTMXCTR2_DACCKSEL_1_3,527		OPORTMXCTR2_DACCKSEL_1_1, OPORTMXCTR2_DACCKSEL_2_3,528	};529	u32 v;530 531	if (sub->swm->dir == PORT_DIR_OUTPUT) {532		if (sub->swm->type == PORT_TYPE_I2S) {533			if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) {534				dev_err(dev, "PLL(%d) is invalid\n",535					sub->aio->pll_out);536				return -EINVAL;537			}538			if (sub->aio->plldiv >= ARRAY_SIZE(v_div)) {539				dev_err(dev, "PLL divider(%d) is invalid\n",540					sub->aio->plldiv);541				return -EINVAL;542			}543 544			v = v_pll[sub->aio->pll_out] |545				OPORTMXCTR2_MSSEL_MASTER |546				v_div[sub->aio->plldiv];547 548			switch (chip->plls[sub->aio->pll_out].freq) {549			case 0:550			case 36864000:551			case 33868800:552				v |= OPORTMXCTR2_EXTLSIFSSEL_36;553				break;554			default:555				v |= OPORTMXCTR2_EXTLSIFSSEL_24;556				break;557			}558		} else if (sub->swm->type == PORT_TYPE_EVE) {559			v = OPORTMXCTR2_ACLKSEL_A2PLL |560				OPORTMXCTR2_MSSEL_MASTER |561				OPORTMXCTR2_EXTLSIFSSEL_36 |562				OPORTMXCTR2_DACCKSEL_1_2;563		} else if (sub->swm->type == PORT_TYPE_SPDIF) {564			if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) {565				dev_err(dev, "PLL(%d) is invalid\n",566					sub->aio->pll_out);567				return -EINVAL;568			}569			v = v_pll[sub->aio->pll_out] |570				OPORTMXCTR2_MSSEL_MASTER |571				OPORTMXCTR2_DACCKSEL_1_2;572 573			switch (chip->plls[sub->aio->pll_out].freq) {574			case 0:575			case 36864000:576			case 33868800:577				v |= OPORTMXCTR2_EXTLSIFSSEL_36;578				break;579			default:580				v |= OPORTMXCTR2_EXTLSIFSSEL_24;581				break;582			}583		} else {584			v = OPORTMXCTR2_ACLKSEL_A1 |585				OPORTMXCTR2_MSSEL_MASTER |586				OPORTMXCTR2_EXTLSIFSSEL_36 |587				OPORTMXCTR2_DACCKSEL_1_2;588		}589		regmap_write(r, OPORTMXCTR2(sub->swm->oport.map), v);590	} else {591		v = IPORTMXCTR2_ACLKSEL_A1 |592			IPORTMXCTR2_MSSEL_SLAVE |593			IPORTMXCTR2_EXTLSIFSSEL_36 |594			IPORTMXCTR2_DACCKSEL_1_2;595		regmap_write(r, IPORTMXCTR2(sub->swm->iport.map), v);596	}597 598	return 0;599}600 601/**602 * aio_port_set_param - set parameters of AIO port block603 * @sub: the AIO substream pointer604 * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM.605 * This parameter has no effect if substream is I2S or PCM.606 * @params: hardware parameters of ALSA607 *608 * Set suitable setting to input/output port block of AIO to process the609 * specified in params.610 *611 * Return: Zero if successful, otherwise a negative value on error.612 */613int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through,614		       const struct snd_pcm_hw_params *params)615{616	struct regmap *r = sub->aio->chip->regmap;617	unsigned int rate;618	u32 v;619	int ret;620 621	if (!pass_through) {622		if (sub->swm->type == PORT_TYPE_EVE ||623		    sub->swm->type == PORT_TYPE_CONV) {624			rate = 48000;625		} else {626			rate = params_rate(params);627		}628 629		ret = aio_port_set_ch(sub);630		if (ret)631			return ret;632 633		ret = aio_port_set_rate(sub, rate);634		if (ret)635			return ret;636 637		ret = aio_port_set_fmt(sub);638		if (ret)639			return ret;640	}641 642	ret = aio_port_set_clk(sub);643	if (ret)644		return ret;645 646	if (sub->swm->dir == PORT_DIR_OUTPUT) {647		if (pass_through)648			v = OPORTMXCTR3_SRCSEL_STREAM |649				OPORTMXCTR3_VALID_STREAM;650		else651			v = OPORTMXCTR3_SRCSEL_PCM |652				OPORTMXCTR3_VALID_PCM;653 654		v |= OPORTMXCTR3_IECTHUR_IECOUT |655			OPORTMXCTR3_PMSEL_PAUSE |656			OPORTMXCTR3_PMSW_MUTE_OFF;657		regmap_write(r, OPORTMXCTR3(sub->swm->oport.map), v);658	} else {659		regmap_write(r, IPORTMXACLKSEL0EX(sub->swm->iport.map),660			     IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL);661		regmap_write(r, IPORTMXEXNOE(sub->swm->iport.map),662			     IPORTMXEXNOE_PCMINOE_INPUT);663	}664 665	return 0;666}667 668/**669 * aio_port_set_enable - start or stop of AIO port block670 * @sub: the AIO substream pointer671 * @enable: zero to stop the block, otherwise to start672 *673 * Start or stop the signal input/output port block of AIO.674 */675void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable)676{677	struct regmap *r = sub->aio->chip->regmap;678 679	if (sub->swm->dir == PORT_DIR_OUTPUT) {680		regmap_write(r, OPORTMXPATH(sub->swm->oport.map),681			     sub->swm->oif.map);682 683		regmap_update_bits(r, OPORTMXMASK(sub->swm->oport.map),684				   OPORTMXMASK_IUDXMSK_MASK |685				   OPORTMXMASK_IUXCKMSK_MASK |686				   OPORTMXMASK_DXMSK_MASK |687				   OPORTMXMASK_XCKMSK_MASK,688				   OPORTMXMASK_IUDXMSK_OFF |689				   OPORTMXMASK_IUXCKMSK_OFF |690				   OPORTMXMASK_DXMSK_OFF |691				   OPORTMXMASK_XCKMSK_OFF);692 693		if (enable)694			regmap_write(r, AOUTENCTR0, BIT(sub->swm->oport.map));695		else696			regmap_write(r, AOUTENCTR1, BIT(sub->swm->oport.map));697	} else {698		regmap_update_bits(r, IPORTMXMASK(sub->swm->iport.map),699				   IPORTMXMASK_IUXCKMSK_MASK |700				   IPORTMXMASK_XCKMSK_MASK,701				   IPORTMXMASK_IUXCKMSK_OFF |702				   IPORTMXMASK_XCKMSK_OFF);703 704		if (enable)705			regmap_update_bits(r,706					   IPORTMXCTR2(sub->swm->iport.map),707					   IPORTMXCTR2_REQEN_MASK,708					   IPORTMXCTR2_REQEN_ENABLE);709		else710			regmap_update_bits(r,711					   IPORTMXCTR2(sub->swm->iport.map),712					   IPORTMXCTR2_REQEN_MASK,713					   IPORTMXCTR2_REQEN_DISABLE);714	}715}716 717/**718 * aio_port_get_volume - get volume of AIO port block719 * @sub: the AIO substream pointer720 *721 * Return: current volume, range is 0x0000 - 0xffff722 */723int aio_port_get_volume(struct uniphier_aio_sub *sub)724{725	struct regmap *r = sub->aio->chip->regmap;726	u32 v;727 728	regmap_read(r, OPORTMXTYVOLGAINSTATUS(sub->swm->oport.map, 0), &v);729 730	return FIELD_GET(OPORTMXTYVOLGAINSTATUS_CUR_MASK, v);731}732 733/**734 * aio_port_set_volume - set volume of AIO port block735 * @sub: the AIO substream pointer736 * @vol: target volume, range is 0x0000 - 0xffff.737 *738 * Change digital volume and perfome fade-out/fade-in effect for specified739 * output slot of port. Gained PCM value can calculate as the following:740 *   Gained = Original * vol / 0x4000741 */742void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol)743{744	struct regmap *r = sub->aio->chip->regmap;745	int oport_map = sub->swm->oport.map;746	int cur, diff, slope = 0, fs;747 748	if (sub->swm->dir == PORT_DIR_INPUT)749		return;750 751	cur = aio_port_get_volume(sub);752	diff = abs(vol - cur);753	fs = params_rate(&sub->params);754	if (fs)755		slope = diff / AUD_VOL_FADE_TIME * 1000 / fs;756	slope = max(1, slope);757 758	regmap_update_bits(r, OPORTMXTYVOLPARA1(oport_map, 0),759			   OPORTMXTYVOLPARA1_SLOPEU_MASK, slope << 16);760	regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0),761			   OPORTMXTYVOLPARA2_TARGET_MASK, vol);762 763	if (cur < vol)764		regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0),765				   OPORTMXTYVOLPARA2_FADE_MASK,766				   OPORTMXTYVOLPARA2_FADE_FADEIN);767	else768		regmap_update_bits(r, OPORTMXTYVOLPARA2(oport_map, 0),769				   OPORTMXTYVOLPARA2_FADE_MASK,770				   OPORTMXTYVOLPARA2_FADE_FADEOUT);771 772	regmap_write(r, AOUTFADECTR0, BIT(oport_map));773}774 775/**776 * aio_if_set_param - set parameters of AIO DMA I/F block777 * @sub: the AIO substream pointer778 * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM.779 * This parameter has no effect if substream is I2S or PCM.780 *781 * Set suitable setting to DMA interface block of AIO to process the782 * specified in settings.783 *784 * Return: Zero if successful, otherwise a negative value on error.785 */786int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through)787{788	struct regmap *r = sub->aio->chip->regmap;789	u32 memfmt, v;790 791	if (sub->swm->dir == PORT_DIR_OUTPUT) {792		if (pass_through) {793			v = PBOUTMXCTR0_ENDIAN_0123 |794				PBOUTMXCTR0_MEMFMT_STREAM;795		} else {796			switch (params_channels(&sub->params)) {797			case 2:798				memfmt = PBOUTMXCTR0_MEMFMT_2CH;799				break;800			case 6:801				memfmt = PBOUTMXCTR0_MEMFMT_6CH;802				break;803			case 8:804				memfmt = PBOUTMXCTR0_MEMFMT_8CH;805				break;806			default:807				return -EINVAL;808			}809			v = PBOUTMXCTR0_ENDIAN_3210 | memfmt;810		}811 812		regmap_write(r, PBOUTMXCTR0(sub->swm->oif.map), v);813		regmap_write(r, PBOUTMXCTR1(sub->swm->oif.map), 0);814	} else {815		regmap_write(r, PBINMXCTR(sub->swm->iif.map),816			     PBINMXCTR_NCONNECT_CONNECT |817			     PBINMXCTR_INOUTSEL_IN |818			     (sub->swm->iport.map << PBINMXCTR_PBINSEL_SHIFT) |819			     PBINMXCTR_ENDIAN_3210 |820			     PBINMXCTR_MEMFMT_D0);821	}822 823	return 0;824}825 826/**827 * aio_oport_set_stream_type - set parameters of AIO playback port block828 * @sub: the AIO substream pointer829 * @pc: Pc type of IEC61937830 *831 * Set special setting to output port block of AIO to output the stream832 * via S/PDIF.833 *834 * Return: Zero if successful, otherwise a negative value on error.835 */836int aio_oport_set_stream_type(struct uniphier_aio_sub *sub,837			      enum IEC61937_PC pc)838{839	struct regmap *r = sub->aio->chip->regmap;840	u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN;841 842	switch (pc) {843	case IEC61937_PC_AC3:844		repet = OPORTMXREPET_STRLENGTH_AC3 |845			OPORTMXREPET_PMLENGTH_AC3;846		pause |= OPORTMXPAUDAT_PAUSEPD_AC3;847		break;848	case IEC61937_PC_MPA:849		repet = OPORTMXREPET_STRLENGTH_MPA |850			OPORTMXREPET_PMLENGTH_MPA;851		pause |= OPORTMXPAUDAT_PAUSEPD_MPA;852		break;853	case IEC61937_PC_MP3:854		repet = OPORTMXREPET_STRLENGTH_MP3 |855			OPORTMXREPET_PMLENGTH_MP3;856		pause |= OPORTMXPAUDAT_PAUSEPD_MP3;857		break;858	case IEC61937_PC_DTS1:859		repet = OPORTMXREPET_STRLENGTH_DTS1 |860			OPORTMXREPET_PMLENGTH_DTS1;861		pause |= OPORTMXPAUDAT_PAUSEPD_DTS1;862		break;863	case IEC61937_PC_DTS2:864		repet = OPORTMXREPET_STRLENGTH_DTS2 |865			OPORTMXREPET_PMLENGTH_DTS2;866		pause |= OPORTMXPAUDAT_PAUSEPD_DTS2;867		break;868	case IEC61937_PC_DTS3:869		repet = OPORTMXREPET_STRLENGTH_DTS3 |870			OPORTMXREPET_PMLENGTH_DTS3;871		pause |= OPORTMXPAUDAT_PAUSEPD_DTS3;872		break;873	case IEC61937_PC_AAC:874		repet = OPORTMXREPET_STRLENGTH_AAC |875			OPORTMXREPET_PMLENGTH_AAC;876		pause |= OPORTMXPAUDAT_PAUSEPD_AAC;877		break;878	case IEC61937_PC_PAUSE:879		/* Do nothing */880		break;881	}882 883	regmap_write(r, OPORTMXREPET(sub->swm->oport.map), repet);884	regmap_write(r, OPORTMXPAUDAT(sub->swm->oport.map), pause);885 886	return 0;887}888 889/**890 * aio_src_reset - reset AIO SRC block891 * @sub: the AIO substream pointer892 *893 * Resets the digital signal input/output port with sampling rate converter894 * block of AIO.895 * This function has no effect if substream is not supported rate converter.896 */897void aio_src_reset(struct uniphier_aio_sub *sub)898{899	struct regmap *r = sub->aio->chip->regmap;900 901	if (sub->swm->dir != PORT_DIR_OUTPUT)902		return;903 904	regmap_write(r, AOUTSRCRSTCTR0, BIT(sub->swm->oport.map));905	regmap_write(r, AOUTSRCRSTCTR1, BIT(sub->swm->oport.map));906}907 908/**909 * aio_src_set_param - set parameters of AIO SRC block910 * @sub: the AIO substream pointer911 * @params: hardware parameters of ALSA912 *913 * Set suitable setting to input/output port with sampling rate converter914 * block of AIO to process the specified in params.915 * This function has no effect if substream is not supported rate converter.916 *917 * Return: Zero if successful, otherwise a negative value on error.918 */919int aio_src_set_param(struct uniphier_aio_sub *sub,920		      const struct snd_pcm_hw_params *params)921{922	struct regmap *r = sub->aio->chip->regmap;923	u32 v;924 925	if (sub->swm->dir != PORT_DIR_OUTPUT)926		return 0;927 928	regmap_write(r, OPORTMXSRC1CTR(sub->swm->oport.map),929		     OPORTMXSRC1CTR_THMODE_SRC |930		     OPORTMXSRC1CTR_SRCPATH_CALC |931		     OPORTMXSRC1CTR_SYNC_ASYNC |932		     OPORTMXSRC1CTR_FSIIPSEL_INNER |933		     OPORTMXSRC1CTR_FSISEL_ACLK);934 935	switch (params_rate(params)) {936	default:937	case 48000:938		v = OPORTMXRATE_I_ACLKSEL_APLLA1 |939			OPORTMXRATE_I_MCKSEL_36 |940			OPORTMXRATE_I_FSSEL_48;941		break;942	case 44100:943		v = OPORTMXRATE_I_ACLKSEL_APLLA2 |944			OPORTMXRATE_I_MCKSEL_33 |945			OPORTMXRATE_I_FSSEL_44_1;946		break;947	case 32000:948		v = OPORTMXRATE_I_ACLKSEL_APLLA1 |949			OPORTMXRATE_I_MCKSEL_36 |950			OPORTMXRATE_I_FSSEL_32;951		break;952	}953 954	regmap_write(r, OPORTMXRATE_I(sub->swm->oport.map),955		     v | OPORTMXRATE_I_ACLKSRC_APLL |956		     OPORTMXRATE_I_LRCKSTP_STOP);957	regmap_update_bits(r, OPORTMXRATE_I(sub->swm->oport.map),958			   OPORTMXRATE_I_LRCKSTP_MASK,959			   OPORTMXRATE_I_LRCKSTP_START);960 961	return 0;962}963 964int aio_srcif_set_param(struct uniphier_aio_sub *sub)965{966	struct regmap *r = sub->aio->chip->regmap;967 968	regmap_write(r, PBINMXCTR(sub->swm->iif.map),969		     PBINMXCTR_NCONNECT_CONNECT |970		     PBINMXCTR_INOUTSEL_OUT |971		     (sub->swm->oport.map << PBINMXCTR_PBINSEL_SHIFT) |972		     PBINMXCTR_ENDIAN_3210 |973		     PBINMXCTR_MEMFMT_D0);974 975	return 0;976}977 978int aio_srcch_set_param(struct uniphier_aio_sub *sub)979{980	struct regmap *r = sub->aio->chip->regmap;981 982	regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->och.map),983		     CDA2D_CHMXCTRL1_INDSIZE_INFINITE);984 985	regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->och.map),986		     CDA2D_CHMXAMODE_ENDIAN_3210 |987		     CDA2D_CHMXAMODE_AUPDT_FIX |988		     CDA2D_CHMXAMODE_TYPE_NORMAL);989 990	regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->och.map),991		     CDA2D_CHMXAMODE_ENDIAN_3210 |992		     CDA2D_CHMXAMODE_AUPDT_INC |993		     CDA2D_CHMXAMODE_TYPE_RING |994		     (sub->swm->och.map << CDA2D_CHMXAMODE_RSSEL_SHIFT));995 996	return 0;997}998 999void aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable)1000{1001	struct regmap *r = sub->aio->chip->regmap;1002	u32 v;1003 1004	if (enable)1005		v = CDA2D_STRT0_STOP_START;1006	else1007		v = CDA2D_STRT0_STOP_STOP;1008 1009	regmap_write(r, CDA2D_STRT0,1010		     v | BIT(sub->swm->och.map));1011}1012 1013int aiodma_ch_set_param(struct uniphier_aio_sub *sub)1014{1015	struct regmap *r = sub->aio->chip->regmap;1016	u32 v;1017 1018	regmap_write(r, CDA2D_CHMXCTRL1(sub->swm->ch.map),1019		     CDA2D_CHMXCTRL1_INDSIZE_INFINITE);1020 1021	v = CDA2D_CHMXAMODE_ENDIAN_3210 |1022		CDA2D_CHMXAMODE_AUPDT_INC |1023		CDA2D_CHMXAMODE_TYPE_NORMAL |1024		(sub->swm->rb.map << CDA2D_CHMXAMODE_RSSEL_SHIFT);1025	if (sub->swm->dir == PORT_DIR_OUTPUT)1026		regmap_write(r, CDA2D_CHMXSRCAMODE(sub->swm->ch.map), v);1027	else1028		regmap_write(r, CDA2D_CHMXDSTAMODE(sub->swm->ch.map), v);1029 1030	return 0;1031}1032 1033void aiodma_ch_set_enable(struct uniphier_aio_sub *sub, int enable)1034{1035	struct regmap *r = sub->aio->chip->regmap;1036 1037	if (enable) {1038		regmap_write(r, CDA2D_STRT0,1039			     CDA2D_STRT0_STOP_START | BIT(sub->swm->ch.map));1040 1041		regmap_update_bits(r, INTRBIM(0),1042				   BIT(sub->swm->rb.map),1043				   BIT(sub->swm->rb.map));1044	} else {1045		regmap_write(r, CDA2D_STRT0,1046			     CDA2D_STRT0_STOP_STOP | BIT(sub->swm->ch.map));1047 1048		regmap_update_bits(r, INTRBIM(0),1049				   BIT(sub->swm->rb.map),1050				   0);1051	}1052}1053 1054static u64 aiodma_rb_get_rp(struct uniphier_aio_sub *sub)1055{1056	struct regmap *r = sub->aio->chip->regmap;1057	u32 pos_u, pos_l;1058	int i;1059 1060	regmap_write(r, CDA2D_RDPTRLOAD,1061		     CDA2D_RDPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map));1062	/* Wait for setup */1063	for (i = 0; i < 6; i++)1064		regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l);1065 1066	regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &pos_l);1067	regmap_read(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), &pos_u);1068	pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u);1069 1070	return ((u64)pos_u << 32) | pos_l;1071}1072 1073static void aiodma_rb_set_rp(struct uniphier_aio_sub *sub, u64 pos)1074{1075	struct regmap *r = sub->aio->chip->regmap;1076	u32 tmp;1077	int i;1078 1079	regmap_write(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), (u32)pos);1080	regmap_write(r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), (u32)(pos >> 32));1081	regmap_write(r, CDA2D_RDPTRLOAD, BIT(sub->swm->rb.map));1082	/* Wait for setup */1083	for (i = 0; i < 6; i++)1084		regmap_read(r, CDA2D_RBMXRDPTR(sub->swm->rb.map), &tmp);1085}1086 1087static u64 aiodma_rb_get_wp(struct uniphier_aio_sub *sub)1088{1089	struct regmap *r = sub->aio->chip->regmap;1090	u32 pos_u, pos_l;1091	int i;1092 1093	regmap_write(r, CDA2D_WRPTRLOAD,1094		     CDA2D_WRPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map));1095	/* Wait for setup */1096	for (i = 0; i < 6; i++)1097		regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l);1098 1099	regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &pos_l);1100	regmap_read(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), &pos_u);1101	pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u);1102 1103	return ((u64)pos_u << 32) | pos_l;1104}1105 1106static void aiodma_rb_set_wp(struct uniphier_aio_sub *sub, u64 pos)1107{1108	struct regmap *r = sub->aio->chip->regmap;1109	u32 tmp;1110	int i;1111 1112	regmap_write(r, CDA2D_RBMXWRPTR(sub->swm->rb.map),1113		     lower_32_bits(pos));1114	regmap_write(r, CDA2D_RBMXWRPTRU(sub->swm->rb.map),1115		     upper_32_bits(pos));1116	regmap_write(r, CDA2D_WRPTRLOAD, BIT(sub->swm->rb.map));1117	/* Wait for setup */1118	for (i = 0; i < 6; i++)1119		regmap_read(r, CDA2D_RBMXWRPTR(sub->swm->rb.map), &tmp);1120}1121 1122int aiodma_rb_set_threshold(struct uniphier_aio_sub *sub, u64 size, u32 th)1123{1124	struct regmap *r = sub->aio->chip->regmap;1125 1126	if (size <= th)1127		return -EINVAL;1128 1129	regmap_write(r, CDA2D_RBMXBTH(sub->swm->rb.map), th);1130	regmap_write(r, CDA2D_RBMXRTH(sub->swm->rb.map), th);1131 1132	return 0;1133}1134 1135int aiodma_rb_set_buffer(struct uniphier_aio_sub *sub, u64 start, u64 end,1136			 int period)1137{1138	struct regmap *r = sub->aio->chip->regmap;1139	u64 size = end - start;1140	int ret;1141 1142	if (end < start || period < 0)1143		return -EINVAL;1144 1145	regmap_write(r, CDA2D_RBMXCNFG(sub->swm->rb.map), 0);1146	regmap_write(r, CDA2D_RBMXBGNADRS(sub->swm->rb.map),1147		     lower_32_bits(start));1148	regmap_write(r, CDA2D_RBMXBGNADRSU(sub->swm->rb.map),1149		     upper_32_bits(start));1150	regmap_write(r, CDA2D_RBMXENDADRS(sub->swm->rb.map),1151		     lower_32_bits(end));1152	regmap_write(r, CDA2D_RBMXENDADRSU(sub->swm->rb.map),1153		     upper_32_bits(end));1154 1155	regmap_write(r, CDA2D_RBADRSLOAD, BIT(sub->swm->rb.map));1156 1157	ret = aiodma_rb_set_threshold(sub, size, 2 * period);1158	if (ret)1159		return ret;1160 1161	if (sub->swm->dir == PORT_DIR_OUTPUT) {1162		aiodma_rb_set_rp(sub, start);1163		aiodma_rb_set_wp(sub, end - period);1164 1165		regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map),1166				   CDA2D_RBMXIX_SPACE,1167				   CDA2D_RBMXIX_SPACE);1168	} else {1169		aiodma_rb_set_rp(sub, end - period);1170		aiodma_rb_set_wp(sub, start);1171 1172		regmap_update_bits(r, CDA2D_RBMXIE(sub->swm->rb.map),1173				   CDA2D_RBMXIX_REMAIN,1174				   CDA2D_RBMXIX_REMAIN);1175	}1176 1177	sub->threshold = 2 * period;1178	sub->rd_offs = 0;1179	sub->wr_offs = 0;1180	sub->rd_org = 0;1181	sub->wr_org = 0;1182	sub->rd_total = 0;1183	sub->wr_total = 0;1184 1185	return 0;1186}1187 1188void aiodma_rb_sync(struct uniphier_aio_sub *sub, u64 start, u64 size,1189		    int period)1190{1191	if (sub->swm->dir == PORT_DIR_OUTPUT) {1192		sub->rd_offs = aiodma_rb_get_rp(sub) - start;1193 1194		if (sub->use_mmap) {1195			sub->threshold = 2 * period;1196			aiodma_rb_set_threshold(sub, size, 2 * period);1197 1198			sub->wr_offs = sub->rd_offs - period;1199			if (sub->rd_offs < period)1200				sub->wr_offs += size;1201		}1202		aiodma_rb_set_wp(sub, sub->wr_offs + start);1203	} else {1204		sub->wr_offs = aiodma_rb_get_wp(sub) - start;1205 1206		if (sub->use_mmap) {1207			sub->threshold = 2 * period;1208			aiodma_rb_set_threshold(sub, size, 2 * period);1209 1210			sub->rd_offs = sub->wr_offs - period;1211			if (sub->wr_offs < period)1212				sub->rd_offs += size;1213		}1214		aiodma_rb_set_rp(sub, sub->rd_offs + start);1215	}1216 1217	sub->rd_total += sub->rd_offs - sub->rd_org;1218	if (sub->rd_offs < sub->rd_org)1219		sub->rd_total += size;1220	sub->wr_total += sub->wr_offs - sub->wr_org;1221	if (sub->wr_offs < sub->wr_org)1222		sub->wr_total += size;1223 1224	sub->rd_org = sub->rd_offs;1225	sub->wr_org = sub->wr_offs;1226}1227 1228bool aiodma_rb_is_irq(struct uniphier_aio_sub *sub)1229{1230	struct regmap *r = sub->aio->chip->regmap;1231	u32 ir;1232 1233	regmap_read(r, CDA2D_RBMXIR(sub->swm->rb.map), &ir);1234 1235	if (sub->swm->dir == PORT_DIR_OUTPUT)1236		return !!(ir & CDA2D_RBMXIX_SPACE);1237	else1238		return !!(ir & CDA2D_RBMXIX_REMAIN);1239}1240 1241void aiodma_rb_clear_irq(struct uniphier_aio_sub *sub)1242{1243	struct regmap *r = sub->aio->chip->regmap;1244 1245	if (sub->swm->dir == PORT_DIR_OUTPUT)1246		regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map),1247			     CDA2D_RBMXIX_SPACE);1248	else1249		regmap_write(r, CDA2D_RBMXIR(sub->swm->rb.map),1250			     CDA2D_RBMXIX_REMAIN);1251}1252