From patchwork Fri Sep 9 13:53:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Martin_Povi=C5=A1er?= X-Patchwork-Id: 12971852 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id EC4A0ECAAA1 for ; Fri, 9 Sep 2022 13:56:28 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 10A00169C; Fri, 9 Sep 2022 15:55:37 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 10A00169C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1662731787; bh=J8pmQGLGZiWaftRKYwYdHxO3kgcjcINndp5q6I/rrZ4=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=RNsvoF/AwaPsLN1/fDslpDm09XxoC5M15/h/donvKqGatHUWm0ilHL95gQlEzYvmF KjzSE6MRkb838cyz67teJJTwa0/tUQdgSVN8fVddKeoL4lJbxtm4dgATRvIxV3aw00 9NX6khCMQjMX97GjRaEVP+CyA1ismZsSRQt0UfHM= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 9E4F0F80548; Fri, 9 Sep 2022 15:54:50 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id E999BF803DD; Fri, 9 Sep 2022 15:54:49 +0200 (CEST) Received: from hutie.ust.cz (unknown [IPv6:2a03:3b40:fe:f0::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 07094F803DD for ; Fri, 9 Sep 2022 15:54:39 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 07094F803DD Authentication-Results: alsa1.perex.cz; dkim=pass (1024-bit key) header.d=cutebit.org header.i=@cutebit.org header.b="HNhFN0cq" From: =?utf-8?q?Martin_Povi=C5=A1er?= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cutebit.org; s=mail; t=1662731678; bh=mwU7raAY9fo6v9UuCQHpSB7jxym3iPOGeJ1lOEZxH7A=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=HNhFN0cqmrFkdZEQj5Y6OSTWhv4oEzLyWoTK1NYg8wUxRyjl8JElspTH2yM6mmAcI RkV/W1HSRyBY11l1yNxZP+mH/cJBb8JPBPP4wHd7tUGWcfZdYK6GNSgeF8JrONKYnv QnwNPKNWtZclgSolbYsyJot1H1Mr1X+ZKISdVfeQ= To: James Schulman , David Rhodes , Lucas Tanure , Richard Fitzgerald , Liam Girdwood , Mark Brown , Rob Herring , Krzysztof Kozlowski , Jaroslav Kysela , Takashi Iwai , =?utf-8?q?Martin_Povi=C5=A1er?= Subject: [PATCH 04/10] ASoC: cs42l42: Split probe() and remove() into stages Date: Fri, 9 Sep 2022 15:53:28 +0200 Message-Id: <20220909135334.98220-5-povik+lin@cutebit.org> In-Reply-To: <20220909135334.98220-1-povik+lin@cutebit.org> References: <20220909135334.98220-1-povik+lin@cutebit.org> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Charles Keepax , - , Pierre-Louis Bossart , linux-kernel@vger.kernel.org, ChiYuan Huang , asahi@lists.linux.dev, Lukas Bulwahn , Matt Flax X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" To prepare for adding SoundWire the probe must be split into three parts: 1) The bus-specific probe 2) Common bus-agnostic probe steps 3) Initialization of the peripheral registers Step (3) must be separate because on SoundWire devices the probe must enable power supplies and release reset so that the peripheral can be enumerated by the bus, but it isn't possible to access registers until enumeration has completed. The call to devm_snd_soc_register_component() must be done at stage (2) so that it can EPROBE_DEFER if necessary. In SoundWire systems stage (3) is not a probe event so a deferral at this stage would not result in re-probing dependencies. A new init_done flag indicates that the chip has been identified and initialized. This is used to prevent cs42l42_remove(), cs42l42_suspend(), cs42l42_restore() and cs42l42_irq_thread() from attempting register accesses if the chip was not successfully initialized. Although this cannot happen on I2C, because the entire probe would fail, it is possible on SoundWire if probe succeeds but the cs42l42 is never enumerated. Signed-off-by: Richard Fitzgerald Signed-off-by: Martin PoviĊĦer --- sound/soc/codecs/cs42l42.c | 126 +++++++++++++++++++++++++------------ sound/soc/codecs/cs42l42.h | 2 + 2 files changed, 88 insertions(+), 40 deletions(-) diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 8e0dc14c55e0..451357a1c315 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -1627,7 +1627,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) int report = 0; mutex_lock(&cs42l42->irq_lock); - if (cs42l42->suspended) { + if (cs42l42->suspended || !cs42l42->init_done) { mutex_unlock(&cs42l42->irq_lock); return IRQ_NONE; } @@ -2199,28 +2199,13 @@ static int __maybe_unused cs42l42_resume(struct device *dev) return 0; } -static int cs42l42_i2c_probe(struct i2c_client *i2c_client) +static int cs42l42_common_probe(struct cs42l42_private *cs42l42) { - struct cs42l42_private *cs42l42; - int ret, i, devid; - unsigned int reg; + int ret, i; - cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private), - GFP_KERNEL); - if (!cs42l42) - return -ENOMEM; - - cs42l42->dev = &i2c_client->dev; - i2c_set_clientdata(i2c_client, cs42l42); + dev_set_drvdata(cs42l42->dev, cs42l42); mutex_init(&cs42l42->irq_lock); - cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); - if (IS_ERR(cs42l42->regmap)) { - ret = PTR_ERR(cs42l42->regmap); - dev_err(cs42l42->dev, "regmap_init() failed: %d\n", ret); - return ret; - } - BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies)); for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++) cs42l42->supplies[i].supply = cs42l42_supply_names[i]; @@ -2257,8 +2242,8 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client) usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); /* Request IRQ if one was specified */ - if (i2c_client->irq) { - ret = request_threaded_irq(i2c_client->irq, + if (cs42l42->irq) { + ret = request_threaded_irq(cs42l42->irq, NULL, cs42l42_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "cs42l42", cs42l42); @@ -2271,6 +2256,32 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client) } } + /* Register codec now so it can EPROBE_DEFER */ + ret = devm_snd_soc_register_component(cs42l42->dev, + &soc_component_dev_cs42l42, + &cs42l42_dai, 1); + if (ret < 0) + goto err; + + return 0; + +err: + if (cs42l42->irq) + free_irq(cs42l42->irq, cs42l42); + +err_disable_noirq: + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); +err_disable_noreset: + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + + return ret; +} + +static int cs42l42_init(struct cs42l42_private *cs42l42) +{ + unsigned int reg; + int devid, ret; + /* initialize codec */ devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB); if (devid < 0) { @@ -2320,15 +2331,15 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client) /* Setup headset detection */ cs42l42_setup_hs_type_detect(cs42l42); + /* + * Set init_done before unmasking interrupts so any triggered + * immediately will be handled. + */ + cs42l42->init_done = true; + /* Mask/Unmask Interrupts */ cs42l42_set_interrupt_masks(cs42l42); - /* Register codec for machine driver */ - ret = devm_snd_soc_register_component(cs42l42->dev, - &soc_component_dev_cs42l42, &cs42l42_dai, 1); - if (ret < 0) - goto err_shutdown; - return 0; err_shutdown: @@ -2337,34 +2348,69 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client) regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); err_disable: - if (i2c_client->irq) - free_irq(i2c_client->irq, cs42l42); - -err_disable_noirq: gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); -err_disable_noreset: regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); return ret; } -static int cs42l42_i2c_remove(struct i2c_client *i2c_client) +static void cs42l42_common_remove(struct cs42l42_private *cs42l42) { - struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client); - - if (i2c_client->irq) - free_irq(i2c_client->irq, cs42l42); + if (cs42l42->irq) + free_irq(cs42l42->irq, cs42l42); /* * The driver might not have control of reset and power supplies, * so ensure that the chip internals are powered down. */ - regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); - regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); - regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); + if (cs42l42->init_done) { + regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); + } gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); +} + +static int cs42l42_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct cs42l42_private *cs42l42; + struct regmap *regmap; + int ret; + + cs42l42 = devm_kzalloc(dev, sizeof(struct cs42l42_private), GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + cs42l42->dev = dev; + cs42l42->regmap = regmap; + cs42l42->irq = i2c_client->irq; + + ret = cs42l42_common_probe(cs42l42); + if (ret) + return ret; + + ret = cs42l42_init(cs42l42); + if (ret) + cs42l42_common_remove(cs42l42); + + return ret; +} + +static int cs42l42_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&i2c_client->dev); + + cs42l42_common_remove(cs42l42); return 0; } diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index b4ba1467c558..a8e0d5b414a5 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -29,6 +29,7 @@ struct cs42l42_private { struct completion pdn_done; struct snd_soc_jack *jack; struct mutex irq_lock; + int irq; int pll_config; u32 sclk; u32 srate; @@ -46,6 +47,7 @@ struct cs42l42_private { u8 stream_use; bool hp_adc_up_pending; bool suspended; + bool init_done; }; #endif /* __CS42L42_H__ */