staticbool cs53l30_writeable_register(struct device *dev, unsignedint reg)
{ switch (reg) { case CS53L30_DEVID_AB: case CS53L30_DEVID_CD: case CS53L30_DEVID_E: case CS53L30_REVID: case CS53L30_IS: returnfalse; default: returntrue;
}
}
staticbool cs53l30_readable_register(struct device *dev, unsignedint reg)
{ switch (reg) { case CS53L30_DEVID_AB: case CS53L30_DEVID_CD: case CS53L30_DEVID_E: case CS53L30_REVID: case CS53L30_PWRCTL: case CS53L30_MCLKCTL: case CS53L30_INT_SR_CTL: case CS53L30_MICBIAS_CTL: case CS53L30_ASPCFG_CTL: case CS53L30_ASP_CTL1: case CS53L30_ASP_TDMTX_CTL1: case CS53L30_ASP_TDMTX_CTL2: case CS53L30_ASP_TDMTX_CTL3: case CS53L30_ASP_TDMTX_CTL4: case CS53L30_ASP_TDMTX_EN1: case CS53L30_ASP_TDMTX_EN2: case CS53L30_ASP_TDMTX_EN3: case CS53L30_ASP_TDMTX_EN4: case CS53L30_ASP_TDMTX_EN5: case CS53L30_ASP_TDMTX_EN6: case CS53L30_ASP_CTL2: case CS53L30_SFT_RAMP: case CS53L30_LRCK_CTL1: case CS53L30_LRCK_CTL2: case CS53L30_MUTEP_CTL1: case CS53L30_MUTEP_CTL2: case CS53L30_INBIAS_CTL1: case CS53L30_INBIAS_CTL2: case CS53L30_DMIC1_STR_CTL: case CS53L30_DMIC2_STR_CTL: case CS53L30_ADCDMIC1_CTL1: case CS53L30_ADCDMIC1_CTL2: case CS53L30_ADC1_CTL3: case CS53L30_ADC1_NG_CTL: case CS53L30_ADC1A_AFE_CTL: case CS53L30_ADC1B_AFE_CTL: case CS53L30_ADC1A_DIG_VOL: case CS53L30_ADC1B_DIG_VOL: case CS53L30_ADCDMIC2_CTL1: case CS53L30_ADCDMIC2_CTL2: case CS53L30_ADC2_CTL3: case CS53L30_ADC2_NG_CTL: case CS53L30_ADC2A_AFE_CTL: case CS53L30_ADC2B_AFE_CTL: case CS53L30_ADC2A_DIG_VOL: case CS53L30_ADC2B_DIG_VOL: case CS53L30_INT_MASK: returntrue; default: returnfalse;
}
}
staticconstchar * const input1_sel_text[] = { "DMIC1 On AB In", "DMIC1 On A In", "DMIC1 On B In", "ADC1 On AB In", "ADC1 On A In", "ADC1 On B In", "DMIC1 Off ADC1 Off",
};
staticconstchar * const input2_sel_text[] = { "DMIC2 On AB In", "DMIC2 On A In", "DMIC2 On B In", "ADC2 On AB In", "ADC2 On A In", "ADC2 On B In", "DMIC2 Off ADC2 Off",
};
staticint cs53l30_get_mclkx_coeff(int mclkx)
{ int i;
for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) { if (cs53l30_mclkx_coeffs[i].mclkx == mclkx) return i;
}
return -EINVAL;
}
staticint cs53l30_get_mclk_coeff(int mclk_rate, int srate)
{ int i;
for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) { if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
cs53l30_mclk_coeffs[i].srate == srate) return i;
}
return -EINVAL;
}
staticint cs53l30_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsignedint freq, int dir)
{ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); int mclkx_coeff;
u32 mclk_rate;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBP_CFP:
aspcfg |= CS53L30_ASP_MS; break; case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL;
}
/* DAI mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: /* Set TDM_PDN to turn off TDM mode -- Reset default */
aspctl1 |= CS53L30_ASP_TDM_PDN; break; case SND_SOC_DAIFMT_DSP_A: /* * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0 * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in * the CS53L30 datasheet
*/
aspctl1 |= CS53L30_SHIFT_LEFT; break; default: return -EINVAL;
}
/* Check to see if the SCLK is inverted */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_IB_NF: case SND_SOC_DAIFMT_IB_IF:
aspcfg ^= CS53L30_ASP_SCLK_INV; break; default: break;
}
switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_LP_MASK, 0); break; case SND_SOC_BIAS_STANDBY: if (dapm->bias_level == SND_SOC_BIAS_OFF) {
ret = clk_prepare_enable(priv->mclk); if (ret) {
dev_err(component->dev, "failed to enable MCLK: %d\n", ret); return ret;
}
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_DIS_MASK, 0);
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK, 0);
msleep(50);
} else {
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK,
CS53L30_PDN_ULP);
} break; case SND_SOC_BIAS_OFF:
regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
CS53L30_PDN_DONE, 0); /* * If digital softramp is set, the amount of time required * for power down increases and depends on the digital * volume setting.
*/
/* Set the max possible time if digsft is set */
regmap_read(priv->regmap, CS53L30_SFT_RAMP, ®); if (reg & CS53L30_DIGSFT_MASK)
inter_max_check = CS53L30_PDN_POLL_MAX; else
inter_max_check = 10;
regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
CS53L30_PDN_ULP_MASK,
CS53L30_PDN_ULP); /* PDN_DONE will take a min of 20ms to be set.*/
msleep(20); /* Clr status */
regmap_read(priv->regmap, CS53L30_IS, ®); for (i = 0; i < inter_max_check; i++) { if (inter_max_check < 10) {
usleep_range(1000, 1100);
regmap_read(priv->regmap, CS53L30_IS, ®); if (reg & CS53L30_PDN_DONE) break;
} else {
usleep_range(10000, 10100);
regmap_read(priv->regmap, CS53L30_IS, ®); if (reg & CS53L30_PDN_DONE) break;
}
} /* PDN_DONE is set. We now can disable the MCLK */
regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
CS53L30_PDN_DONE, CS53L30_PDN_DONE);
regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
CS53L30_MCLK_DIS_MASK,
CS53L30_MCLK_DIS);
clk_disable_unprepare(priv->mclk); break;
}
return 0;
}
staticint cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
{ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
u8 val = tristate ? CS53L30_ASP_3ST : 0;
/* * Note: CS53L30 counts the slot number per byte while ASoC counts the slot * number per slot_width. So there is a difference between the slots of ASoC * and the slots of CS53L30.
*/ staticint cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai, unsignedint tx_mask, unsignedint rx_mask, int slots, int slot_width)
{ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); unsignedint loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48}; unsignedint slot_next, slot_step;
u64 tx_enable = 0; int i;
if (!rx_mask) {
dev_err(dai->dev, "rx masks must not be 0\n"); return -EINVAL;
}
/* Assuming slot_width is not supposed to be greater than 64 */ if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
dev_err(dai->dev, "invalid slot number or slot width\n"); return -EINVAL;
}
if (slot_width & 0x7) {
dev_err(dai->dev, "slot width must count in byte\n"); return -EINVAL;
}
/* How many bytes in each ASoC slot */
slot_step = slot_width >> 3;
for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) { /* Find the first slot from LSB */
slot_next = __ffs(rx_mask); /* Save the slot location by converting to CS53L30 slot */
loc[i] = slot_next * slot_step; /* Create the mask of CS53L30 slot */
tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i]; /* Clear this slot from rx_mask */
rx_mask &= ~(1 << slot_next);
}
/* Error out to avoid slot shift */ if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
CS53L30_TDM_SLOT_MAX); return -EINVAL;
}
/* Validate the last active CS53L30 slot */
slot_next = loc[i - 1] + slot_step - 1; if (slot_next > 47) {
dev_err(dai->dev, "slot selection out of bounds: %u\n",
slot_next); return -EINVAL;
}
for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
}
for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
tx_enable & 0xff);
tx_enable >>= 8;
dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
}
return 0;
}
staticint cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{ struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component);
staticint cs53l30_i2c_probe(struct i2c_client *client)
{ conststruct device_node *np = client->dev.of_node; struct device *dev = &client->dev; struct cs53l30_private *cs53l30; unsignedint reg; int ret = 0, i, devid;
u8 val;
cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL); if (!cs53l30) return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
cs53l30->supplies[i].supply = cs53l30_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies); if (ret) {
dev_err(dev, "failed to get supplies: %d\n", ret); return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
cs53l30->supplies); if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret); return ret;
}
/* Reset the Device */
cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW); if (IS_ERR(cs53l30->reset_gpio)) {
ret = PTR_ERR(cs53l30->reset_gpio); goto error_supplies;
}
gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
i2c_set_clientdata(client, cs53l30);
cs53l30->mclk_rate = 0;
cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap); if (IS_ERR(cs53l30->regmap)) {
ret = PTR_ERR(cs53l30->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret); goto error;
}
/* Initialize codec */
devid = cirrus_read_device_id(cs53l30->regmap, CS53L30_DEVID_AB); if (devid < 0) {
ret = devid;
dev_err(dev, "Failed to read device ID: %d\n", ret); goto error;
}
if (devid != CS53L30_DEVID) {
ret = -ENODEV;
dev_err(dev, "Device ID (%X). Expected %X\n",
devid, CS53L30_DEVID); goto error;
}
ret = regmap_read(cs53l30->regmap, CS53L30_REVID, ®); if (ret < 0) {
dev_err(dev, "failed to get Revision ID: %d\n", ret); goto error;
}
/* Check if MCLK provided */
cs53l30->mclk = devm_clk_get_optional(dev, "mclk"); if (IS_ERR(cs53l30->mclk)) {
ret = PTR_ERR(cs53l30->mclk); goto error;
}
/* Fetch the MUTE control */
cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
GPIOD_OUT_HIGH); if (IS_ERR(cs53l30->mute_gpio)) {
ret = PTR_ERR(cs53l30->mute_gpio); goto error;
}
if (cs53l30->mute_gpio) { /* Enable MUTE controls via MUTE pin */
regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
CS53L30_MUTEP_CTL1_MUTEALL); /* Flip the polarity of MUTE pin */ if (gpiod_is_active_low(cs53l30->mute_gpio))
regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
CS53L30_MUTE_PIN_POLARITY, 0);
}
if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
CS53L30_MIC_BIAS_CTRL_MASK, val);
if (of_property_read_bool(np, "cirrus,use-sdout2"))
cs53l30->use_sdout2 = true;
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.