Received: by 2002:a25:ab43:0:0:0:0:0 with SMTP id u61csp4692669ybi; Tue, 11 Jun 2019 10:50:24 -0700 (PDT) X-Google-Smtp-Source: APXvYqxySxpuiFm1ECh2SN3rz+VutQidtxwSxHwT+ENICo4wLck8dtUbVpUW7tK3KDaJn6W6RwwF X-Received: by 2002:a17:902:7891:: with SMTP id q17mr21182835pll.236.1560275424579; Tue, 11 Jun 2019 10:50:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1560275424; cv=none; d=google.com; s=arc-20160816; b=mdRyijwriP6/eNmT6qwW6Pn5cKzeEAvH4uHsw0lqmdihAqccE54brgSqiWVy9w/hHE JPfwlPNDtY9uE9zzEBQaR3rIyLPHfvUglQGvpic/tb5qTxyv1/BrJpMaroOyOa/wN3R0 1RPyrt2IiXlj2yxVsIrDTKEdPm7tptVaBlHyksNQpevcTDyy/d3fRDxx3ZizmwsIZ2Ui 8eCwO3n3zpflukEQBzWoD8pzctlNB0u9HB+5FIOKSYcXc6bcdYbj+Ke16KU5C2d5nOKc M6jhB3SD+i6W0j3lRqCqkpKFBxCVLxZw/JxJMAa3jcQF1laE0ci0ei+KIi5pWPKCABW4 FzIA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:to:from; bh=IrvF8HeiL/RXroS0pTQATWTOjeVZXerZZ3iMy7e4NW8=; b=QmYUHisfaHF8Rd7d1oQ2b3RpIFV9lAGoKVSRokU/oy196/BImxGNF3+P8hOqzWxjGa OIIWYNVaDTX9aisrEmbrKRVAQSdtJmZXh17ECYufB/0v8XKeCg2u7KPkXMVq1iX1fA6l NTqm+12Qzp1dMnOnh1j8Ckq8scuUOxi8QQwHJCOr+BLF91N/GGywXPT8nIifyenw7lrI TK4/zuzMV5nA3CLtj1lvkFtu0+Gq89IaiadilD2V2XJD4PF4VSKAr29mQ4OGBg2BLLkg aqA43LnJLkxFPv8St54Gh3Iv6K5YkhCEJZ69piQjAilY8EJhEzlTLguZF41VLJot+EMZ KO2w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=codethink.co.uk Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id j14si12450036pll.251.2019.06.11.10.50.08; Tue, 11 Jun 2019 10:50:24 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=codethink.co.uk Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2406632AbfFKRt5 (ORCPT + 99 others); Tue, 11 Jun 2019 13:49:57 -0400 Received: from imap1.codethink.co.uk ([176.9.8.82]:36927 "EHLO imap1.codethink.co.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2406592AbfFKRtz (ORCPT ); Tue, 11 Jun 2019 13:49:55 -0400 Received: from [167.98.27.226] (helo=ct-lt-1124.office.codethink.co.uk) by imap1.codethink.co.uk with esmtpsa (Exim 4.84_2 #1 (Debian)) id 1haktg-0001PN-4P; Tue, 11 Jun 2019 18:49:24 +0100 From: Thomas Preston To: Liam Girdwood , Mark Brown , Rob Herring , Mark Rutland , Jaroslav Kysela , Takashi Iwai , Charles Keepax , Jerome Brunet , Srinivas Kandagatla , Marco Felsch , Paul Cercueil , Kirill Marinushkin , Cheng-Yi Chiang , Kuninori Morimoto , Vinod Koul , Annaliese McDermond , Thomas Preston , alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v1 4/4] ASoC: tda7802: Add speaker-test sysfs Date: Tue, 11 Jun 2019 18:49:09 +0100 Message-Id: <20190611174909.12162-5-thomas.preston@codethink.co.uk> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20190611174909.12162-1-thomas.preston@codethink.co.uk> References: <20190611174909.12162-1-thomas.preston@codethink.co.uk> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add speaker_test device attribute. When the speaker-test node is read the hardware speaker test is started. $ cat /sys/devices/.../device:00/speaker_test 04 04 04 04 Signed-off-by: Thomas Preston Cc: Patrick Glaser Cc: Rob Duncan Cc: Nate Case --- sound/soc/codecs/tda7802.c | 172 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/sound/soc/codecs/tda7802.c b/sound/soc/codecs/tda7802.c index 62aae011d9f1..edfa752c0c9f 100644 --- a/sound/soc/codecs/tda7802.c +++ b/sound/soc/codecs/tda7802.c @@ -157,6 +157,7 @@ static const u8 IB3_FORMAT[4][4] = { #define DUMP_NUM_REGS (DUMP_NUM_BLOCK * 2) struct tda7802_priv { + struct mutex mutex; struct i2c_client *i2c; struct regmap *regmap; struct regulator *enable_reg; @@ -458,6 +459,159 @@ static struct snd_soc_dai_driver tda7802_dai_driver = { .ops = &tda7802_dai_ops, }; +/* The speaker test is triggered by reading a sysfs attribute file attached to + * the I2C device. The user space thread that's doing the reading is blocked + * until the test completes (or times out). We only permit one test to be in + * progress at a time. + */ + +static int speaker_test_start(struct regmap *regmap) +{ + int err; + + err = regmap_update_bits(regmap, TDA7802_IB5, IB5_AMPLIFIER_ON, 0); + if (err < 0) { + dev_err(regmap_get_device(regmap), + "Cannot disable amp for speaker test (%d)\n", err); + return err; + } + + err = regmap_update_bits(regmap, TDA7802_IB4, IB4_DIAG_MODE_ENABLE, 0); + if (err < 0) { + dev_err(regmap_get_device(regmap), + "Cannot disable diag mode before speaker test (%d)\n", + err); + return err; + } + + /* Seem to need at least a 15 ms delay here before the rising + * edge. Otherwise the diagnostics never complete (perhaps + * they never start). + */ + msleep(20); + + err = regmap_update_bits(regmap, TDA7802_IB4, + IB4_DIAG_MODE_ENABLE, IB4_DIAG_MODE_ENABLE); + if (err < 0) { + dev_err(regmap_get_device(regmap), + "Cannot enable diag mode for speaker test (%d)\n", err); + return err; + } + + return 0; +} + +static int speaker_test_stop(struct regmap *regmap) +{ + int err; + + err = regmap_update_bits(regmap, TDA7802_IB4, IB4_DIAG_MODE_ENABLE, 0); + if (err < 0) + dev_err(regmap_get_device(regmap), + "Cannot disable diag mode after speaker test (%d)\n", + err); + return err; +} + +/* We poll the test completion bit, letting the current thread sleep + * until we're done. These values are not critical. + */ +#define SPEAKER_TEST_DONE_POLL_PERIOD_US 5000 +#define SPEAKER_TEST_DONE_TIMEOUT_US 5000000 + +static int speaker_test_check(struct tda7802_priv *tda7802, + unsigned int *status) +{ + struct regmap *regmap = tda7802->regmap; + struct device *dev = &tda7802->i2c->dev; + int reg_err = 0, err = 0, i, amp_on; + unsigned int val; + + reg_err = regulator_enable(tda7802->enable_reg); + if (reg_err < 0) { + dev_err(dev, "Could not enable (speaker-test).\n"); + return reg_err; + } + msleep(ENABLE_DELAY_MS); + + /* we should return amp on state when speaker-test is done */ + err = regmap_read(regmap, TDA7802_IB5, &_on); + if (err < 0) { + dev_err(dev, "Could not read amp on state (speaker-test)\n"); + goto speaker_test_disable; + } + + for (i = 0; i < CHANNELS_PER_AMP; ++i) + status[i] = 0xff; + + err = speaker_test_start(regmap); + if (err < 0) + goto speaker_test_restore; + + /* Wait until DB0_STARTUP_DIAG_STATUS is set */ + err = regmap_read_poll_timeout(regmap, TDA7802_DB0, val, + val & DB0_STARTUP_DIAG_STATUS, + SPEAKER_TEST_DONE_POLL_PERIOD_US, + SPEAKER_TEST_DONE_TIMEOUT_US); + if (err < 0) { + if (err == -ENODATA) + dev_err(dev, + "Speaker diagnostic test did not complete\n"); + speaker_test_stop(regmap); + goto speaker_test_restore; + } + + err = speaker_test_stop(regmap); + if (err < 0) + goto speaker_test_restore; + + for (i = 0; i < CHANNELS_PER_AMP; ++i) { + err = regmap_read(regmap, TDA7802_DB1 + i, status + i); + if (err < 0) { + dev_err(dev, + "Cannot get speaker %d status (%d)\n", i, err); + goto speaker_test_restore; + } + } + +speaker_test_restore: + err = regmap_update_bits(regmap, TDA7802_IB5, IB5_AMPLIFIER_ON, + amp_on); + if (err < 0) + dev_err(dev, "Could not restore amp on state (speaker-test)\n"); + +speaker_test_disable: + reg_err = regulator_disable(tda7802->enable_reg); + if (reg_err < 0) { + dev_err(dev, "Could not disable (speaker-test).\n"); + return reg_err; + } + return err; +} + +static ssize_t speaker_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tda7802_priv *tda7802 = dev_get_drvdata(dev); + unsigned int channel_status[CHANNELS_PER_AMP]; + char *str = buf; + int ret, i; + + mutex_lock(&tda7802->mutex); + ret = speaker_test_check(tda7802, channel_status); + mutex_unlock(&tda7802->mutex); + if (ret < 0) + return ret; + + for (i = 0; i < CHANNELS_PER_AMP; i++) + str += sprintf(str, "%02x ", channel_status[i]); + str += sprintf(str, "\n"); + + return str - buf; +} + +static DEVICE_ATTR_RO(speaker_test); + static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -537,12 +691,28 @@ static int tda7802_probe(struct snd_soc_component *component) dev_dbg(dev, "%s\n", __func__); + /* Device is ready, now we should create sysfs attributes. + * Rememer to cleanup + */ err = device_create_file(dev, &dev_attr_enable); if (err < 0) { dev_err(dev, "Could not create enable attr\n"); return err; } + mutex_init(&tda7802->mutex); + err = device_create_file(dev, &dev_attr_speaker_test); + if (err < 0) { + dev_err(dev, "Could not create speaker_test attr\n"); + goto cleanup_speaker_test; + } + + return 0; + +cleanup_speaker_test: + mutex_destroy(&tda7802->mutex); + device_remove_file(&tda7802->i2c->dev, &dev_attr_enable); + return err; } @@ -550,6 +720,8 @@ static void tda7802_remove(struct snd_soc_component *component) { struct tda7802_priv *tda7802 = snd_soc_component_get_drvdata(component); + device_remove_file(&tda7802->i2c->dev, &dev_attr_speaker_test); + mutex_destroy(&tda7802->mutex); device_remove_file(&tda7802->i2c->dev, &dev_attr_enable); } -- 2.11.0