Path: blob/master/sound/soc/sdca/sdca_class_function.c
122918 views
// SPDX-License-Identifier: GPL-2.01// Copyright (C) 2025 Cirrus Logic, Inc. and2// Cirrus Logic International Semiconductor Ltd.34/*5* The MIPI SDCA specification is available for public downloads at6* https://www.mipi.org/mipi-sdca-v1-0-download7*/89#include <linux/auxiliary_bus.h>10#include <linux/cleanup.h>11#include <linux/minmax.h>12#include <linux/module.h>13#include <linux/pm.h>14#include <linux/pm_runtime.h>15#include <linux/soundwire/sdw.h>16#include <linux/soundwire/sdw_registers.h>17#include <sound/pcm.h>18#include <sound/sdca_asoc.h>19#include <sound/sdca_fdl.h>20#include <sound/sdca_function.h>21#include <sound/sdca_interrupts.h>22#include <sound/sdca_jack.h>23#include <sound/sdca_regmap.h>24#include <sound/sdw.h>25#include <sound/soc-component.h>26#include <sound/soc-dai.h>27#include <sound/soc.h>28#include "sdca_class.h"2930struct class_function_drv {31struct device *dev;32struct regmap *regmap;33struct sdca_class_drv *core;3435struct sdca_function_data *function;36bool suspended;37};3839static void class_function_regmap_lock(void *data)40{41struct mutex *lock = data;4243mutex_lock(lock);44}4546static void class_function_regmap_unlock(void *data)47{48struct mutex *lock = data;4950mutex_unlock(lock);51}5253static bool class_function_regmap_writeable(struct device *dev, unsigned int reg)54{55struct auxiliary_device *auxdev = to_auxiliary_dev(dev);56struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);5758return sdca_regmap_writeable(drv->function, reg);59}6061static bool class_function_regmap_readable(struct device *dev, unsigned int reg)62{63struct auxiliary_device *auxdev = to_auxiliary_dev(dev);64struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);6566return sdca_regmap_readable(drv->function, reg);67}6869static bool class_function_regmap_volatile(struct device *dev, unsigned int reg)70{71struct auxiliary_device *auxdev = to_auxiliary_dev(dev);72struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);7374return sdca_regmap_volatile(drv->function, reg);75}7677static const struct regmap_config class_function_regmap_config = {78.name = "sdca",79.reg_bits = 32,80.val_bits = 32,81.reg_format_endian = REGMAP_ENDIAN_LITTLE,82.val_format_endian = REGMAP_ENDIAN_LITTLE,8384.max_register = SDW_SDCA_MAX_REGISTER,85.readable_reg = class_function_regmap_readable,86.writeable_reg = class_function_regmap_writeable,87.volatile_reg = class_function_regmap_volatile,8889.cache_type = REGCACHE_MAPLE,9091.lock = class_function_regmap_lock,92.unlock = class_function_regmap_unlock,93};9495static int class_function_regmap_mbq_size(struct device *dev, unsigned int reg)96{97struct auxiliary_device *auxdev = to_auxiliary_dev(dev);98struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);99100return sdca_regmap_mbq_size(drv->function, reg);101}102103static bool class_function_regmap_deferrable(struct device *dev, unsigned int reg)104{105struct auxiliary_device *auxdev = to_auxiliary_dev(dev);106struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);107108return sdca_regmap_deferrable(drv->function, reg);109}110111static const struct regmap_sdw_mbq_cfg class_function_mbq_config = {112.mbq_size = class_function_regmap_mbq_size,113.deferrable = class_function_regmap_deferrable,114.retry_us = 1000,115.timeout_us = 10000,116};117118static int class_function_startup(struct snd_pcm_substream *substream,119struct snd_soc_dai *dai)120{121struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);122123return sdca_asoc_set_constraints(drv->dev, drv->regmap, drv->function,124substream, dai);125}126127static int class_function_sdw_add_peripheral(struct snd_pcm_substream *substream,128struct snd_pcm_hw_params *params,129struct snd_soc_dai *dai)130{131struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);132struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);133struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);134struct sdw_stream_config sconfig = {0};135struct sdw_port_config pconfig = {0};136int ret;137138if (!sdw_stream)139return -EINVAL;140141snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);142143/*144* FIXME: As also noted in sdca_asoc_get_port(), currently only145* a single unshared port is supported for each DAI.146*/147ret = sdca_asoc_get_port(drv->dev, drv->regmap, drv->function, dai);148if (ret < 0)149return ret;150151pconfig.num = ret;152153ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);154if (ret) {155dev_err(drv->dev, "failed to add sdw stream: %d\n", ret);156return ret;157}158159return sdca_asoc_hw_params(drv->dev, drv->regmap, drv->function,160substream, params, dai);161}162163static int class_function_sdw_remove_peripheral(struct snd_pcm_substream *substream,164struct snd_soc_dai *dai)165{166struct class_function_drv *drv = snd_soc_component_get_drvdata(dai->component);167struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);168struct sdw_slave *sdw = dev_to_sdw_dev(drv->dev->parent);169170if (!sdw_stream)171return -EINVAL;172173return sdw_stream_remove_slave(sdw, sdw_stream);174}175176static int class_function_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream,177int direction)178{179snd_soc_dai_dma_data_set(dai, direction, sdw_stream);180181return 0;182}183184static const struct snd_soc_dai_ops class_function_sdw_ops = {185.startup = class_function_startup,186.shutdown = sdca_asoc_free_constraints,187.set_stream = class_function_sdw_set_stream,188.hw_params = class_function_sdw_add_peripheral,189.hw_free = class_function_sdw_remove_peripheral,190};191192static int class_function_component_probe(struct snd_soc_component *component)193{194struct class_function_drv *drv = snd_soc_component_get_drvdata(component);195struct sdca_class_drv *core = drv->core;196197return sdca_irq_populate(drv->function, component, core->irq_info);198}199200static int class_function_set_jack(struct snd_soc_component *component,201struct snd_soc_jack *jack, void *d)202{203struct class_function_drv *drv = snd_soc_component_get_drvdata(component);204struct sdca_class_drv *core = drv->core;205206return sdca_jack_set_jack(core->irq_info, jack);207}208209static const struct snd_soc_component_driver class_function_component_drv = {210.probe = class_function_component_probe,211.endianness = 1,212};213214static int class_function_init_device(struct class_function_drv *drv,215unsigned int status)216{217int ret;218219if (!(status & SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET)) {220dev_dbg(drv->dev, "reset function device\n");221222ret = sdca_reset_function(drv->dev, drv->function, drv->regmap);223if (ret)224return ret;225}226227if (status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) {228dev_dbg(drv->dev, "write initialisation\n");229230ret = sdca_regmap_write_init(drv->dev, drv->core->dev_regmap,231drv->function);232if (ret)233return ret;234}235236return 0;237}238239static int class_function_boot(struct class_function_drv *drv)240{241unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,242SDCA_ENTITY_TYPE_ENTITY_0,243SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);244unsigned int val;245int ret;246247guard(mutex)(&drv->core->init_lock);248249ret = regmap_read(drv->regmap, reg, &val);250if (ret < 0) {251dev_err(drv->dev, "failed to read function status: %d\n", ret);252return ret;253}254255ret = class_function_init_device(drv, val);256if (ret)257return ret;258259/* Start FDL process */260ret = sdca_irq_populate_early(drv->dev, drv->regmap, drv->function,261drv->core->irq_info);262if (ret)263return ret;264265ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);266if (ret)267return ret;268269ret = sdca_regmap_write_defaults(drv->dev, drv->regmap, drv->function);270if (ret)271return ret;272273ret = regmap_write(drv->regmap, reg, 0xFF);274if (ret < 0) {275dev_err(drv->dev, "failed to clear function status: %d\n", ret);276return ret;277}278279return 0;280}281282static int class_function_probe(struct auxiliary_device *auxdev,283const struct auxiliary_device_id *aux_dev_id)284{285struct device *dev = &auxdev->dev;286struct sdca_class_drv *core = dev_get_drvdata(dev->parent);287struct sdca_device_data *data = &core->sdw->sdca_data;288struct sdca_function_desc *desc;289struct snd_soc_component_driver *cmp_drv;290struct snd_soc_dai_driver *dais;291struct class_function_drv *drv;292struct regmap_sdw_mbq_cfg *mbq_config;293struct regmap_config *config;294struct reg_default *defaults;295int ndefaults;296int num_dais;297int ret;298int i;299300drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);301if (!drv)302return -ENOMEM;303304cmp_drv = devm_kmemdup(dev, &class_function_component_drv, sizeof(*cmp_drv),305GFP_KERNEL);306if (!cmp_drv)307return -ENOMEM;308309config = devm_kmemdup(dev, &class_function_regmap_config, sizeof(*config),310GFP_KERNEL);311if (!config)312return -ENOMEM;313314mbq_config = devm_kmemdup(dev, &class_function_mbq_config, sizeof(*mbq_config),315GFP_KERNEL);316if (!mbq_config)317return -ENOMEM;318319drv->dev = dev;320drv->core = core;321322for (i = 0; i < data->num_functions; i++) {323desc = &data->function[i];324325if (desc->type == aux_dev_id->driver_data)326break;327}328if (i == core->sdw->sdca_data.num_functions) {329dev_err(dev, "failed to locate function\n");330return -EINVAL;331}332333drv->function = &core->functions[i];334335ret = sdca_parse_function(dev, core->sdw, desc, drv->function);336if (ret)337return ret;338339ndefaults = sdca_regmap_count_constants(dev, drv->function);340if (ndefaults < 0)341return ndefaults;342343defaults = devm_kcalloc(dev, ndefaults, sizeof(*defaults), GFP_KERNEL);344if (!defaults)345return -ENOMEM;346347ret = sdca_regmap_populate_constants(dev, drv->function, defaults);348if (ret < 0)349return ret;350351regcache_sort_defaults(defaults, ndefaults);352353auxiliary_set_drvdata(auxdev, drv);354355config->reg_defaults = defaults;356config->num_reg_defaults = ndefaults;357config->lock_arg = &core->regmap_lock;358359if (drv->function->busy_max_delay) {360mbq_config->timeout_us = drv->function->busy_max_delay;361mbq_config->retry_us = umax(drv->function->busy_max_delay / 10,362mbq_config->retry_us);363}364365drv->regmap = devm_regmap_init_sdw_mbq_cfg(dev, core->sdw, config, mbq_config);366if (IS_ERR(drv->regmap))367return dev_err_probe(dev, PTR_ERR(drv->regmap),368"failed to create regmap");369370if (desc->type == SDCA_FUNCTION_TYPE_UAJ)371cmp_drv->set_jack = class_function_set_jack;372373ret = sdca_asoc_populate_component(dev, drv->function, cmp_drv,374&dais, &num_dais,375&class_function_sdw_ops);376if (ret)377return ret;378379dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);380381pm_runtime_set_autosuspend_delay(dev, 200);382pm_runtime_use_autosuspend(dev);383pm_runtime_set_active(dev);384pm_runtime_get_noresume(dev);385386ret = devm_pm_runtime_enable(dev);387if (ret)388return ret;389390ret = class_function_boot(drv);391if (ret)392return ret;393394ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais);395if (ret)396return dev_err_probe(dev, ret, "failed to register component\n");397398pm_runtime_mark_last_busy(dev);399pm_runtime_put_autosuspend(dev);400401return 0;402}403404static int class_function_runtime_suspend(struct device *dev)405{406struct auxiliary_device *auxdev = to_auxiliary_dev(dev);407struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);408409/*410* Whilst the driver doesn't power the chip down here, going into411* runtime suspend means the driver can't be sure the bus won't412* power down which would prevent communication with the device.413*/414regcache_cache_only(drv->regmap, true);415416return 0;417}418419static int class_function_runtime_resume(struct device *dev)420{421struct auxiliary_device *auxdev = to_auxiliary_dev(dev);422struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);423int ret;424425guard(mutex)(&drv->core->init_lock);426427regcache_mark_dirty(drv->regmap);428regcache_cache_only(drv->regmap, false);429430if (drv->suspended) {431unsigned int reg = SDW_SDCA_CTL(drv->function->desc->adr,432SDCA_ENTITY_TYPE_ENTITY_0,433SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);434unsigned int val;435436ret = regmap_read(drv->regmap, reg, &val);437if (ret < 0) {438dev_err(drv->dev, "failed to read function status: %d\n", ret);439goto err;440}441442ret = class_function_init_device(drv, val);443if (ret)444goto err;445446sdca_irq_enable_early(drv->function, drv->core->irq_info);447448ret = sdca_fdl_sync(drv->dev, drv->function, drv->core->irq_info);449if (ret)450goto err;451452sdca_irq_enable(drv->function, drv->core->irq_info);453454ret = regmap_write(drv->regmap, reg, 0xFF);455if (ret < 0) {456dev_err(drv->dev, "failed to clear function status: %d\n", ret);457goto err;458}459460drv->suspended = false;461}462463ret = regcache_sync(drv->regmap);464if (ret) {465dev_err(drv->dev, "failed to restore register cache: %d\n", ret);466goto err;467}468469return 0;470471err:472regcache_cache_only(drv->regmap, true);473474return ret;475}476477static int class_function_suspend(struct device *dev)478{479struct auxiliary_device *auxdev = to_auxiliary_dev(dev);480struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);481int ret;482483drv->suspended = true;484485/* Ensure runtime resume runs on resume */486ret = pm_runtime_resume_and_get(dev);487if (ret) {488dev_err(dev, "failed to resume for suspend: %d\n", ret);489return ret;490}491492sdca_irq_disable(drv->function, drv->core->irq_info);493494ret = pm_runtime_force_suspend(dev);495if (ret) {496dev_err(dev, "failed to force suspend: %d\n", ret);497return ret;498}499500pm_runtime_put_noidle(dev);501502return 0;503}504505static int class_function_resume(struct device *dev)506{507int ret;508509ret = pm_runtime_force_resume(dev);510if (ret) {511dev_err(dev, "failed to force resume: %d\n", ret);512return ret;513}514515return 0;516}517518static const struct dev_pm_ops class_function_pm_ops = {519SYSTEM_SLEEP_PM_OPS(class_function_suspend, class_function_resume)520RUNTIME_PM_OPS(class_function_runtime_suspend,521class_function_runtime_resume, NULL)522};523524static const struct auxiliary_device_id class_function_id_table[] = {525{526.name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_AMP_NAME,527.driver_data = SDCA_FUNCTION_TYPE_SMART_AMP,528},529{530.name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SMART_MIC_NAME,531.driver_data = SDCA_FUNCTION_TYPE_SMART_MIC,532},533{534.name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_UAJ_NAME,535.driver_data = SDCA_FUNCTION_TYPE_UAJ,536},537{538.name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_HID_NAME,539.driver_data = SDCA_FUNCTION_TYPE_HID,540},541{},542};543MODULE_DEVICE_TABLE(auxiliary, class_function_id_table);544545static struct auxiliary_driver class_function_drv = {546.driver = {547.name = "sdca_function",548.pm = pm_ptr(&class_function_pm_ops),549},550551.probe = class_function_probe,552.id_table = class_function_id_table553};554module_auxiliary_driver(class_function_drv);555556MODULE_LICENSE("GPL");557MODULE_DESCRIPTION("SDCA Class Function Driver");558MODULE_IMPORT_NS("SND_SOC_SDCA");559560561