123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- /*
- * Extended support for Cirrus Logic Smart Amplifiers
- *
- * Copyright 2017 Cirrus Logic
- *
- * Author: David Rhodes <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <sound/cirrus/core.h>
- #include <sound/cirrus/big_data.h>
- #include <sound/cirrus/calibration.h>
- #include <sound/cirrus/power.h>
- #include "wm_adsp.h"
- #include <linux/firmware/cirrus/cs_dsp.h>
- #include <linux/firmware/cirrus/wmfw.h>
- struct class *cirrus_amp_class;
- EXPORT_SYMBOL_GPL(cirrus_amp_class);
- struct cirrus_amp_group *amp_group;
- static struct cirrus_cal_ops *cirrus_cal_ops[3] = {
- &cirrus_cspl_cal_ops,
- &cirrus_cspl_cal_ops,
- #if IS_ENABLED(CONFIG_SND_SOC_CS35L43)
- &cirrus_cs35l43_cal_ops
- #endif
- };
- struct cirrus_amp *cirrus_get_amp_from_suffix(const char *suffix)
- {
- int i;
- if (suffix == NULL)
- return NULL;
- if (amp_group == NULL || (amp_group->num_amps == 0))
- return NULL;
- pr_debug("%s: suffix = %s\n", __func__, suffix);
- for (i = 0; i < amp_group->num_amps; i++) {
- if (amp_group->amps[i].bd.bd_suffix) {
- pr_debug("%s: comparing %s & %s\n", __func__,
- amp_group->amps[i].bd.bd_suffix, suffix);
- if (!strcmp(amp_group->amps[i].bd.bd_suffix, suffix))
- return &_group->amps[i];
- }
- }
- for (i = 0; i < amp_group->num_amps; i++) {
- pr_debug("%s: comparing %s & %s\n", __func__,
- amp_group->amps[i].mfd_suffix, suffix);
- if (!strcmp(amp_group->amps[i].mfd_suffix, suffix))
- return &_group->amps[i];
- }
- return NULL;
- }
- EXPORT_SYMBOL_GPL(cirrus_get_amp_from_suffix);
- void cirrus_amp_register_i2c_error_callback(const char *suffix, void *func)
- {
- struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
- if (amp)
- amp->i2c_callback = func;
- }
- EXPORT_SYMBOL_GPL(cirrus_amp_register_i2c_error_callback);
- void cirrus_amp_register_error_callback(const char *suffix, void *func)
- {
- struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
- if (amp)
- amp->error_callback = func;
- }
- EXPORT_SYMBOL_GPL(cirrus_amp_register_error_callback);
- int cirrus_amp_add(const char *mfd_suffix, struct cirrus_amp_config cfg)
- {
- struct cirrus_amp *amp = cirrus_get_amp_from_suffix(mfd_suffix);
- if (amp) {
- dev_info(amp_group->cal_dev,
- "Amp added, suffix: %s dsp_part_name: %s\n",
- mfd_suffix, cfg.dsp_part_name);
- amp->component = cfg.component;
- amp->regmap = cfg.regmap;
- amp->dsp_part_name = cfg.dsp_part_name;
- amp->num_pre_configs = cfg.num_pre_configs;
- amp->num_post_configs = cfg.num_post_configs;
- amp->mbox_cmd = cfg.mbox_cmd;
- amp->mbox_sts = cfg.mbox_sts;
- amp->global_en = cfg.global_en;
- amp->global_en_mask = cfg.global_en_mask;
- amp->vimon_alg_id = cfg.vimon_alg_id;
- amp->pwr.target_temp = cfg.target_temp;
- amp->pwr.exit_temp = cfg.exit_temp;
- amp->perform_vimon_cal = cfg.perform_vimon_cal;
- amp->calibration_disable = cfg.calibration_disable;
- amp->cal_vpk_id = cfg.cal_vpk_id ? cfg.cal_vpk_id :
- CIRRUS_CAL_RTLOG_ID_V_PEAK;
- amp->cal_ipk_id = cfg.cal_ipk_id ? cfg.cal_ipk_id :
- CIRRUS_CAL_RTLOG_ID_I_PEAK;
- amp->halo_alg_id = cfg.halo_alg_id ? cfg.halo_alg_id :
- CIRRUS_AMP_ALG_ID_HALO;
- amp->bd.bd_alg_id = cfg.bd_alg_id ? cfg.bd_alg_id :
- CIRRUS_AMP_ALG_ID_CSPL;
- amp->bd.bd_prefix = cfg.bd_prefix ? cfg.bd_prefix :
- "BDLOG_";
- amp->cal_vsc_ub = cfg.cal_vsc_ub ? cfg.cal_vsc_ub :
- CIRRUS_CAL_VIMON_CAL_VSC_UB;
- amp->cal_vsc_lb = cfg.cal_vsc_lb ? cfg.cal_vsc_lb :
- CIRRUS_CAL_VIMON_CAL_VSC_LB;
- amp->cal_isc_ub = cfg.cal_isc_ub ? cfg.cal_isc_ub :
- CIRRUS_CAL_VIMON_CAL_ISC_UB;
- amp->cal_isc_lb = cfg.cal_isc_lb ? cfg.cal_isc_lb :
- CIRRUS_CAL_VIMON_CAL_ISC_LB;
- amp->bd.max_temp_limit = cfg.bd_max_temp ?
- cfg.bd_max_temp - 1 : 99;
- amp->default_redc = cfg.default_redc ? cfg.default_redc :
- CIRRUS_CAL_RDC_DEFAULT;
- amp->cal_ops = cfg.cal_ops_idx ? cirrus_cal_ops[cfg.cal_ops_idx] :
- cirrus_cal_ops[CIRRUS_CAL_CSPL_CAL_OPS_IDX];
- amp->pre_config = kcalloc(cfg.num_pre_configs,
- sizeof(struct reg_sequence),
- GFP_KERNEL);
- amp->post_config = kcalloc(cfg.num_post_configs,
- sizeof(struct reg_sequence),
- GFP_KERNEL);
- amp->amp_reinit = cfg.amp_reinit;
- amp->runtime_pm = cfg.runtime_pm;
- amp_group->pwr_enable |= cfg.pwr_enable;
- memcpy(amp->pre_config, cfg.pre_config,
- sizeof(struct reg_sequence) * cfg.num_pre_configs);
- memcpy(amp->post_config, cfg.post_config,
- sizeof(struct reg_sequence) * cfg.num_post_configs);
- } else {
- dev_err(amp_group->cal_dev,
- "No amp with suffix %s registered\n", mfd_suffix);
- return -EINVAL;
- }
- return 0;
- }
- EXPORT_SYMBOL_GPL(cirrus_amp_add);
- int cirrus_amp_read_ctl(struct cirrus_amp *amp, const char *name,
- int type, unsigned int id, unsigned int *value)
- {
- struct wm_adsp *dsp = snd_soc_component_get_drvdata(amp->component);
- unsigned int tmp;
- int ret = 0, retry = CIRRUS_AMP_CTL_RETRY;
- if (amp->component) {
- do {
- ret = wm_adsp_read_ctl(dsp, name, type, id, (void *)&tmp, 4);
- *value = (tmp & 0xff0000) >> 8 |
- (tmp & 0xff00) << 8 |
- (tmp & 0xff000000) >> 24;
- if (ret)
- dev_err(dsp->cs_dsp.dev, "%s: ret = %d\n", __func__, ret);
- } while (ret != 0 && ret != -EINVAL && retry-- > 0);
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(cirrus_amp_read_ctl);
- int cirrus_amp_write_ctl(struct cirrus_amp *amp, const char *name,
- int type, unsigned int id, unsigned int value)
- {
- struct wm_adsp *dsp = snd_soc_component_get_drvdata(amp->component);
- unsigned int tmp;
- int ret = 0, retry = CIRRUS_AMP_CTL_RETRY;
- tmp = (value & 0xff0000) >> 8 |
- (value & 0xff00) << 8 |
- (value & 0xff000000) >> 24 |
- (value & 0xff) << 24;
- if (amp->component) {
- do {
- ret = wm_adsp_write_ctl(dsp, name, type, id, (void *)&tmp, 4);
- if (ret && ret != -EINVAL)
- dev_err(dsp->cs_dsp.dev, "%s: ret = %d\n", __func__, ret);
- } while (ret != 0 && ret != -EINVAL && retry-- > 0);
- }
- return ret;
- }
- EXPORT_SYMBOL_GPL(cirrus_amp_write_ctl);
- static const struct of_device_id cirrus_amp_of_match[] = {
- { .compatible = "cirrus-amp", },
- {},
- };
- MODULE_DEVICE_TABLE(of, cirrus_amp_of_match);
- static int cirrus_amp_probe(struct platform_device *pdev)
- {
- struct device_node *np;
- struct device_node *amp_node;
- const char **mfd_suffixes;
- const char **bd_suffixes;
- bool v_val_separate[CIRRUS_MAX_AMPS];
- int ret = 0, num = 0, num_amps, i, j;
- cirrus_amp_class = class_create(THIS_MODULE, "cirrus");
- if (IS_ERR(cirrus_amp_class)) {
- ret = PTR_ERR(cirrus_amp_class);
- pr_err("%s: Unable to register cirrus_amp class (%d)",
- __func__, ret);
- return ret;
- }
- for_each_matching_node(np, cirrus_amp_of_match)
- num++;
- if (num != 1) {
- pr_info("%s: Exactly 1 OF entry is allowed (%d detected)\n",
- __func__, num);
- ret = -EINVAL;
- goto class_err;
- }
- np = of_find_matching_node(NULL, cirrus_amp_of_match);
- if (!np) {
- pr_err("%s: Device node required\n", __func__);
- ret = -ENODEV;
- goto class_err;
- }
- num_amps = of_count_phandle_with_args(np, "cirrus,amps", NULL);
- if (num_amps <= 0) {
- pr_err("%s: Failed to parse 'cirrus,amps'\n", __func__);
- ret = -ENODEV;
- goto class_err;
- }
- amp_group = kzalloc(sizeof(struct cirrus_amp_group) +
- sizeof(struct cirrus_amp) * num_amps,
- GFP_KERNEL);
- mfd_suffixes = kcalloc(num_amps, sizeof(char *), GFP_KERNEL);
- bd_suffixes = kcalloc(num_amps, sizeof(char *), GFP_KERNEL);
- amp_group->num_amps = num_amps;
- for (i = 0; i < num_amps; i++) {
- amp_node = of_parse_phandle(np, "cirrus,amps", i);
- if (IS_ERR(amp_node)) {
- pr_err("%s: Failed to parse 'cirrus,amps' (%d)\n",
- __func__, i);
- ret = PTR_ERR(amp_node);
- goto suffix_free;
- }
- pr_debug("%s: Found linked amp: %s\n", __func__,
- amp_node->full_name);
- ret = of_property_read_string(amp_node, "cirrus,mfd-suffix",
- &mfd_suffixes[i]);
- if (ret < 0) {
- pr_err("%s: No MFD suffix found for amp: %s\n",
- __func__, amp_node->full_name);
- of_node_put(amp_node);
- goto suffix_free;
- }
- ret = of_property_read_string(amp_node, "cirrus,bd-suffix",
- &bd_suffixes[i]);
- if (ret < 0)
- pr_debug("%s: No BD suffix found for amp: %s\n",
- __func__, amp_node->full_name);
- v_val_separate[i] = of_property_read_bool(amp_node,
- "cirrus,v-val_separate");
- of_node_put(amp_node);
- }
- for (i = 0; i < num_amps; i++) {
- for (j = 0; j < num_amps; j++) {
- if (i == j)
- continue;
- if (strcmp(mfd_suffixes[i], mfd_suffixes[j]) == 0) {
- pr_err("%s: MFD suffixes must be unique\n",
- __func__);
- pr_err("%s: Found duplicate suffix: %s\n",
- __func__, mfd_suffixes[i]);
- ret = -EINVAL;
- goto suffix_err;
- }
- /* bd_suffixes can be empty but must be unique */
- if (bd_suffixes[i] && bd_suffixes[j] &&
- (strcmp(bd_suffixes[i], bd_suffixes[j]) == 0)) {
- pr_err("%s: BD suffixes must be unique\n",
- __func__);
- pr_err("%s: Found duplicate suffix: %s\n",
- __func__, bd_suffixes[i]);
- ret = -EINVAL;
- goto suffix_err;
- }
- }
- pr_info("%s: Found MFD suffix: %s\n", __func__,
- mfd_suffixes[i]);
- amp_group->amps[i].mfd_suffix =
- kstrdup(mfd_suffixes[i], GFP_KERNEL);
- if (bd_suffixes[i]) {
- pr_info("%s: Found BD suffix: %s\n", __func__,
- bd_suffixes[i]);
- amp_group->amps[i].bd.bd_suffix =
- kstrdup(bd_suffixes[i], GFP_KERNEL);
- }
- if (v_val_separate[i])
- amp_group->amps[i].v_val_separate = true;
- }
- ret = cirrus_bd_init();
- if (ret < 0) {
- pr_err("%s: Error in BD init (%d)\n", __func__, ret);
- goto suffix_err;
- }
- ret = cirrus_cal_init();
- if (ret < 0) {
- pr_err("%s: Error in CAL init (%d)\n", __func__, ret);
- cirrus_bd_exit();
- goto suffix_err;
- }
- ret = cirrus_pwr_init();
- if (ret < 0) {
- pr_err("%s: Error in PWR init (%d)\n", __func__, ret);
- cirrus_bd_exit();
- cirrus_cal_exit();
- goto suffix_err;
- }
- ret = 0;
- goto suffix_free;
- suffix_err:
- for (i = 0; i < num_amps; i++) {
- kfree(amp_group->amps[i].mfd_suffix);
- kfree(amp_group->amps[i].bd.bd_suffix);
- }
- suffix_free:
- kfree(mfd_suffixes);
- kfree(bd_suffixes);
- if (ret < 0)
- kfree(amp_group);
- class_err:
- if (ret < 0)
- class_destroy(cirrus_amp_class);
- return ret;
- }
- static int cirrus_amp_remove(struct platform_device *pdev)
- {
- int i;
- cirrus_cal_exit();
- cirrus_bd_exit();
- cirrus_pwr_exit();
- for (i = 0; i < amp_group->num_amps; i++) {
- kfree(amp_group->amps[i].pre_config);
- kfree(amp_group->amps[i].post_config);
- }
- kfree(amp_group);
- class_destroy(cirrus_amp_class);
- return 0;
- }
- static struct platform_driver cirrus_amp_driver = {
- .driver = {
- .name = "cirrus-amp",
- .of_match_table = cirrus_amp_of_match,
- },
- .probe = cirrus_amp_probe,
- .remove = cirrus_amp_remove,
- };
- module_platform_driver(cirrus_amp_driver);
- MODULE_AUTHOR("David Rhodes <[email protected]>");
- MODULE_DESCRIPTION("Cirrus Amp driver");
- MODULE_LICENSE("GPL");
|