// SPDX-License-Identifier: GPL-2.0-or-later /* * Routines for control of the CS8427 via i2c bus * IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
int snd_cs8427_init(struct snd_i2c_bus *bus, struct snd_i2c_device *device)
{ staticunsignedchar initvals1[] = {
CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC, /* CS8427_REG_CONTROL1: RMCK to OMCK, valid PCM audio, disable mutes,
TCBL=output */
CS8427_SWCLK | CS8427_TCBLDIR, /* CS8427_REG_CONTROL2: hold last valid audio sample, RMCK=256*Fs,
normal stereo operation */
0x00, /* CS8427_REG_DATAFLOW: output drivers normal operation, Tx<=serial,
Rx=>serial */
CS8427_TXDSERIAL | CS8427_SPDAES3RECEIVER, /* CS8427_REG_CLOCKSOURCE: Run off, CMCK=256*Fs, output time base = OMCK, input time base = recovered input clock, recovered input clock source is ILRCK changed to AES3INPUT
(workaround, see snd_cs8427_reset) */
CS8427_RXDILRCK, /* CS8427_REG_SERIALINPUT: Serial audio input port data format = I2S,
24-bit, 64*Fsi */
CS8427_SIDEL | CS8427_SILRPOL, /* CS8427_REG_SERIALOUTPUT: Serial audio output port data format
= I2S, 24-bit, 64*Fsi */
CS8427_SODEL | CS8427_SOLRPOL,
}; staticunsignedchar initvals2[] = {
CS8427_REG_RECVERRMASK | CS8427_REG_AUTOINC, /* CS8427_REG_RECVERRMASK: unmask the input PLL clock, V, confidence,
biphase, parity status bits */ /* CS8427_UNLOCK | CS8427_V | CS8427_CONF | CS8427_BIP | CS8427_PAR,*/
0xff, /* set everything */ /* CS8427_REG_CSDATABUF: Registers 32-55 window to CS buffer Inhibit D->E transfers from overwriting first 5 bytes of CS data. Inhibit D->E transfers (all) of CS data. Allow E->F transfer of CS data. One byte mode; both A/B channels get same written CB data.
A channel info is output to chip's EMPH* pin. */
CS8427_CBMR | CS8427_DETCI, /* CS8427_REG_UDATABUF: Use internal buffer to transmit User (U) data. Chip's U pin is an output. Transmit all O's for user data. Inhibit D->E transfers.
Inhibit E->F transfers. */
CS8427_UD | CS8427_EFTUI | CS8427_DETUI,
}; struct cs8427 *chip = device->private_data; int err; unsignedchar buf[24];
snd_i2c_lock(bus);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER); if (err != CS8427_VER8427A) { /* give second chance */
dev_warn(device->bus->card->dev, "invalid CS8427 signature 0x%x: let me try again...\n",
err);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
} if (err != CS8427_VER8427A) {
snd_i2c_unlock(bus);
dev_err(device->bus->card->dev, "unable to find CS8427 signature (expected 0x%x, read 0x%x),\n",
CS8427_VER8427A, err);
dev_err(device->bus->card->dev, " initialization is not completed\n"); return -EFAULT;
} /* turn off run bit while making changes to configuration */
err = snd_cs8427_reg_write(device, CS8427_REG_CLOCKSOURCE, 0x00); if (err < 0) goto __fail; /* send initial values */
memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
err = snd_i2c_sendbytes(device, initvals1, 7); if (err != 7) {
err = err < 0 ? err : -EIO; goto __fail;
} /* Turn off CS8427 interrupt stuff that is not used in hardware */
memset(buf, 0, 7); /* from address 9 to 15 */
buf[0] = 9; /* register */
err = snd_i2c_sendbytes(device, buf, 7); if (err != 7) goto __fail; /* send transfer initialization sequence */
memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
err = snd_i2c_sendbytes(device, initvals2, 4); if (err != 4) {
err = err < 0 ? err : -EIO; goto __fail;
} /* write default channel status bytes */
put_unaligned_le32(SNDRV_PCM_DEFAULT_CON_SPDIF, buf);
memset(buf + 4, 0, 24 - 4); if (snd_cs8427_send_corudata(device, 0, buf, 24) < 0) goto __fail;
memcpy(chip->playback.def_status, buf, 24);
memcpy(chip->playback.pcm_status, buf, 24);
snd_i2c_unlock(bus);
/* turn on run bit and rock'n'roll */
snd_cs8427_reset(device);
return 0;
__fail:
snd_i2c_unlock(bus);
return err;
}
EXPORT_SYMBOL(snd_cs8427_init);
int snd_cs8427_create(struct snd_i2c_bus *bus, unsignedchar addr, unsignedint reset_timeout, struct snd_i2c_device **r_cs8427)
{ int err; struct cs8427 *chip; struct snd_i2c_device *device;
/* * Reset the chip using run bit, also lock PLL using ILRCK and * put back AES3INPUT. This workaround is described in latest * CS8427 datasheet, otherwise TXDSERIAL will not work.
*/ staticvoid snd_cs8427_reset(struct snd_i2c_device *cs8427)
{ struct cs8427 *chip; unsignedlong end_time; int data, aes3input = 0;
if (snd_BUG_ON(!cs8427)) return;
chip = cs8427->private_data;
snd_i2c_lock(cs8427->bus); if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) ==
CS8427_RXDAES3INPUT) /* AES3 bit is set */
aes3input = 1;
chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~(CS8427_RUN | CS8427_RXDMASK);
snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE,
chip->regmap[CS8427_REG_CLOCKSOURCE]);
udelay(200);
chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RUN | CS8427_RXDILRCK;
snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE,
chip->regmap[CS8427_REG_CLOCKSOURCE]);
udelay(200);
snd_i2c_unlock(cs8427->bus);
end_time = jiffies + chip->reset_timeout; while (time_after_eq(end_time, jiffies)) {
snd_i2c_lock(cs8427->bus);
data = snd_cs8427_reg_read(cs8427, CS8427_REG_RECVERRORS);
snd_i2c_unlock(cs8427->bus); if (!(data & CS8427_UNLOCK)) break;
schedule_timeout_uninterruptible(1);
}
snd_i2c_lock(cs8427->bus);
chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~CS8427_RXDMASK; if (aes3input)
chip->regmap[CS8427_REG_CLOCKSOURCE] |= CS8427_RXDAES3INPUT;
snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE,
chip->regmap[CS8427_REG_CLOCKSOURCE]);
snd_i2c_unlock(cs8427->bus);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.