Re: [PATCH v7 3/6] mfd: cs42l43: Add support for cs42l43 core driver

From: andy . shevchenko
Date: Thu Jan 18 2024 - 11:43:46 EST


Fri, Aug 04, 2023 at 11:45:59AM +0100, Charles Keepax kirjoitti:
> The CS42L43 is an audio CODEC with integrated MIPI SoundWire interface
> (Version 1.2.1 compliant), I2C, SPI, and I2S/TDM interfaces designed
> for portable applications. It provides a high dynamic range, stereo
> DAC for headphone output, two integrated Class D amplifiers for
> loudspeakers, and two ADCs for wired headset microphone input or
> stereo line input. PDM inputs are provided for digital microphones.
>
> The MFD component registers and initialises the device and provides
> PM/system power management.

..

> +#include <linux/err.h>
> +#include <linux/errno.h>

It seems errno is not used (as Linux kernel error codes, otherwise err.h
already includes necessary header).

Also seems array_size.h, mod_devicetable.h are missing (at least).

..

> +#if IS_ENABLED(CONFIG_OF)

We are trying hard to get rid of this ugly ifdefferies (ACPI as well) along
with respective macros that are often the PITA for CIs.

> +static const struct of_device_id cs42l43_of_match[] = {
> + { .compatible = "cirrus,cs42l43", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, cs42l43_of_match);
> +#endif
> +
> +#if IS_ENABLED(CONFIG_ACPI)
> +static const struct acpi_device_id cs42l43_acpi_match[] = {
> + { "CSC4243", 0 },
> + {}
> +};
> +MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match);
> +#endif
> +
> +static struct i2c_driver cs42l43_i2c_driver = {
> + .driver = {
> + .name = "cs42l43",
> + .pm = pm_ptr(&cs42l43_pm_ops),
> + .of_match_table = of_match_ptr(cs42l43_of_match),
> + .acpi_match_table = ACPI_PTR(cs42l43_acpi_match),
> + },
> +
> + .probe = cs42l43_i2c_probe,
> + .remove = cs42l43_i2c_remove,
> +};

..

> +#include <linux/err.h>
> +#include <linux/errno.h>

As per above.

> +#include <linux/mfd/cs42l43-regs.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/soundwire/sdw.h>
> +#include <linux/soundwire/sdw_registers.h>
> +#include <linux/soundwire/sdw_type.h>

..

> + mutex_lock(&cs42l43->pll_lock);

This can be converted using cleanup.h.

> + mutex_unlock(&cs42l43->pll_lock);

..

> + cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap);
> + if (IS_ERR(cs42l43->regmap)) {
> + ret = PTR_ERR(cs42l43->regmap);
> + dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret);
> + return ret;
> + }

Can be simplified as

cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap);
if (IS_ERR(cs42l43->regmap))
dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->regmap),
"Failed to allocate regmap: %d\n", ret);

..

> +#include <linux/err.h>
> +#include <linux/errno.h>

As per above.

..

> +#define CS42L43_RESET_DELAY 20
> +
> +#define CS42L43_SDW_ATTACH_TIMEOUT 500
> +#define CS42L43_SDW_DETACH_TIMEOUT 100
> +
> +#define CS42L43_MCU_POLL 5000
> +#define CS42L43_MCU_CMD_TIMEOUT 20000

> +#define CS42L43_MCU_UPDATE_TIMEOUT 500000

> +#define CS42L43_VDDP_DELAY 50
> +#define CS42L43_VDDD_DELAY 1000
> +
> +#define CS42L43_AUTOSUSPEND_TIME 250

Usually we use units for the macro names as suffixes...
E.g., _US (for microseconds).

..

> +struct cs42l43_patch_header {
> + __le16 version;
> + __le16 size;
> + u8 reserved;
> + u8 secure;

Seems to me that __u8 is appropriate as patch is external to the kernel and
it's kinda firmware interface.

> + __le16 bss_size;
> + __le32 apply_addr;
> + __le32 checksum;
> + __le32 sha;
> + __le16 swrev;
> + __le16 patchid;
> + __le16 ipxid;
> + __le16 romver;
> + __le32 load_addr;
> +} __packed;

..

> +static const struct mfd_cell cs42l43_devs[] = {
> + { .name = "cs42l43-pinctrl", },
> + { .name = "cs42l43-spi", },

Inner commas are not needed

> + {
> + .name = "cs42l43-codec",
> + .parent_supplies = cs42l43_parent_supplies,
> + .num_parent_supplies = ARRAY_SIZE(cs42l43_parent_supplies),
> + },
> +};

> + case CS42L43_MCU_BOOT_STAGE2:
> + if (!patched) {
> + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
> + "cs42l43.bin", cs42l43->dev,
> + GFP_KERNEL, cs42l43,
> + cs42l43_mcu_load_firmware);
> + if (ret) {
> + dev_err(cs42l43->dev, "Failed to request firmware: %d\n", ret);
> + return ret;
> + }
> +
> + wait_for_completion(&cs42l43->firmware_download);
> +
> + if (cs42l43->firmware_error)
> + return cs42l43->firmware_error;
> +
> + return -EAGAIN;
> + } else {

Redundant 'else' and

> + return cs42l43_mcu_stage_2_3(cs42l43, shadow);
> + }

why not positive conditional as below?

if (patched)
return ...
...


> + case CS42L43_MCU_BOOT_STAGE3:
> + if (patched)
> + return cs42l43_mcu_disable(cs42l43);
> + else
> + return cs42l43_mcu_stage_3_2(cs42l43);

..

> + irq_flags = irqd_get_trigger_type(irq_data);
> + switch (irq_flags) {
> + case IRQF_TRIGGER_LOW:
> + case IRQF_TRIGGER_HIGH:
> + case IRQF_TRIGGER_RISING:
> + case IRQF_TRIGGER_FALLING:
> + break;
> + case IRQ_TYPE_NONE:

Are you sure it's a right place to interpret no type flags as a default?

> + default:
> + irq_flags = IRQF_TRIGGER_LOW;
> + break;
> + }

..

> +static int cs42l43_suspend(struct device *dev)
> +{
> + struct cs42l43 *cs42l43 = dev_get_drvdata(dev);
> + int ret;
> +
> + /*
> + * Don't care about being resumed here, but the driver does want
> + * force_resume to always trigger an actual resume, so that register
> + * state for the MCU/GPIOs is returned as soon as possible after system
> + * resume. force_resume will resume if the reference count is resumed on
> + * suspend hence the get_noresume.
> + */
> + pm_runtime_get_noresume(dev);
> +
> + ret = pm_runtime_force_suspend(dev);
> + if (ret) {
> + dev_err(cs42l43->dev, "Failed to force suspend: %d\n", ret);
> + pm_runtime_put_noidle(dev);
> + return ret;
> + }
> +
> + pm_runtime_put_noidle(dev);
> +
> + ret = cs42l43_power_down(cs42l43);
> + if (ret)
> + return ret;
> +
> + return 0;

return cs42l43_power_down(cs42l43);

> +}

..

> +EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = {
> + SET_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume)
> + SET_RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL)
> +};

Why do you need SET_*() versions of those macros? They are not supposed to be
used with the new macros such as EXPORT_NS_GPL_DEV_PM_OPS().

..

> +++ b/drivers/mfd/cs42l43.h

> +#include <linux/mfd/cs42l43.h>

How is this header being used?
Wouldn't the forward declaration fulfill the need?

> +#include <linux/pm.h>
> +#include <linux/regmap.h>

> +#ifndef CS42L43_CORE_INT_H
> +#define CS42L43_CORE_INT_H

Why you don't guard other headers with this? It will slow down the build.

> +#define CS42L43_N_DEFAULTS 176
> +
> +extern const struct dev_pm_ops cs42l43_pm_ops;
> +extern const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS];


Missing forward declaration for

struct device;

> +bool cs42l43_readable_register(struct device *dev, unsigned int reg);
> +bool cs42l43_precious_register(struct device *dev, unsigned int reg);
> +bool cs42l43_volatile_register(struct device *dev, unsigned int reg);

> +int cs42l43_dev_probe(struct cs42l43 *cs42l43);
> +void cs42l43_dev_remove(struct cs42l43 *cs42l43);
> +
> +#endif /* CS42L43_CORE_INT_H */

..

> +#define CS42L43_MIXER_VOL_MASK 0x00FE0000
> +#define CS42L43_MIXER_VOL_SHIFT 17
> +#define CS42L43_MIXER_SRC_MASK 0x000001FF
> +#define CS42L43_MIXER_SRC_SHIFT 0

This header would benefit from bits.h... (the above is just a little example).

..

> +++ b/include/linux/mfd/cs42l43.h

> +#include <linux/device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/soundwire/sdw.h>

Missing forward declarations (instead of inclusions).

struct device;
struct gpio_desc;
struct sdw_slave;

Missing types.h.

..

> +#ifndef CS42L43_CORE_EXT_H
> +#define CS42L43_CORE_EXT_H

Same questions as per another header.

--
With Best Regards,
Andy Shevchenko