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