// SPDX-License-Identifier: GPL-2.0-only /* * wm2000.c -- WM2000 ALSA Soc Audio driver * * Copyright 2008-2011 Wolfson Microelectronics PLC. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * The download image for the WM2000 will be requested as * 'wm2000_anc.bin' by default (overridable via platform data) at * runtime and is expected to be in flat binary format. This is * generated by Wolfson configuration tools and includes * system-specific calibration information. If supplied as a * sequence of ASCII-encoded hexidecimal bytes this can be converted * into a flat binary with a command such as this on the command line: * * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }' * < file > wm2000_anc.bin
*/
/* Wait for ANC engine to become ready */ if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
WM2000_ANC_ENG_IDLE)) {
dev_err(&i2c->dev, "ANC engine failed to reset\n");
regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_BOOT_COMPLETE)) {
dev_err(&i2c->dev, "ANC engine failed to initialise\n");
regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); return -ETIMEDOUT;
}
/* Open code download of the data since it is the only bulk
* write we do. */
dev_dbg(&i2c->dev, "Downloading %d bytes\n",
wm2000->anc_download_size - 2);
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_POWER_DOWN_COMPLETE)) {
dev_err(&i2c->dev, "Timeout waiting for ANC power down\n"); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
WM2000_ANC_ENG_IDLE)) {
dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_MOUSE_ACTIVE)) {
dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_ANC_DISABLED)) {
dev_err(&i2c->dev, "Timed out waiting for ANC disable after 1ms\n"); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE)) {
dev_err(&i2c->dev, "Timed out waiting for standby\n"); return -ETIMEDOUT;
}
if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
WM2000_STATUS_MOUSE_ACTIVE)) {
dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); return -ETIMEDOUT;
}
staticint wm2000_anc_transition(struct wm2000_priv *wm2000, enum wm2000_anc_mode mode)
{ struct i2c_client *i2c = wm2000->i2c; int i, j; int ret = 0;
if (wm2000->anc_mode == mode) return 0;
for (i = 0; i < ARRAY_SIZE(anc_transitions); i++) if (anc_transitions[i].source == wm2000->anc_mode &&
anc_transitions[i].dest == mode) break; if (i == ARRAY_SIZE(anc_transitions)) {
dev_err(&i2c->dev, "No transition for %d->%d\n",
wm2000->anc_mode, mode); return -EINVAL;
}
/* Maintain clock while active */ if (anc_transitions[i].source == ANC_OFF) {
ret = clk_prepare_enable(wm2000->mclk); if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret); return ret;
}
}
for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) { if (!anc_transitions[i].step[j]) break;
ret = anc_transitions[i].step[j](i2c,
anc_transitions[i].analogue); if (ret != 0) break;
}
if (anc_transitions[i].dest == ANC_OFF)
clk_disable_unprepare(wm2000->mclk);
staticbool wm2000_readable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case WM2000_REG_SYS_START: case WM2000_REG_ANC_GAIN_CTRL: case WM2000_REG_MSE_TH1: case WM2000_REG_MSE_TH2: case WM2000_REG_SPEECH_CLARITY: case WM2000_REG_SYS_WATCHDOG: case WM2000_REG_ANA_VMID_PD_TIME: case WM2000_REG_ANA_VMID_PU_TIME: case WM2000_REG_CAT_FLTR_INDX: case WM2000_REG_CAT_GAIN_0: case WM2000_REG_SYS_STATUS: case WM2000_REG_SYS_MODE_CNTRL: case WM2000_REG_SYS_START0: case WM2000_REG_SYS_START1: case WM2000_REG_ID1: case WM2000_REG_ID2: case WM2000_REG_REVISON: case WM2000_REG_SYS_CTL1: case WM2000_REG_SYS_CTL2: case WM2000_REG_ANC_STAT: case WM2000_REG_IF_CTL: case WM2000_REG_ANA_MIC_CTL: case WM2000_REG_SPK_CTL: returntrue; default: returnfalse;
}
}
wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); if (!wm2000) return -ENOMEM;
mutex_init(&wm2000->lock);
dev_set_drvdata(&i2c->dev, wm2000);
wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); if (IS_ERR(wm2000->regmap)) {
ret = PTR_ERR(wm2000->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret); goto out;
}
for (i = 0; i < WM2000_NUM_SUPPLIES; i++)
wm2000->supplies[i].supply = wm2000_supplies[i];
ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES,
wm2000->supplies); if (ret != 0) {
dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); return ret;
}
ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); if (ret != 0) {
dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); return ret;
}
/* Verify that this is a WM2000 */
ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, ®); if (ret != 0) {
dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret); return ret;
}
id = reg << 8;
ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, ®); if (ret != 0) {
dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret); return ret;
}
id |= reg & 0xff;
if (id != 0x2000) {
dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
ret = -ENODEV; goto err_supplies;
}
ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, ®); if (ret != 0) {
dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret); return ret;
}
dev_info(&i2c->dev, "revision %c\n", reg + 'A');
wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); if (IS_ERR(wm2000->mclk)) {
ret = PTR_ERR(wm2000->mclk);
dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret); goto err_supplies;
}
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.