Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: cs35l56: Create debugfs files for factory calibration

Create debugfs files that can be used to perform factory calibration.

During manufacture, the production line must perform a factory calibration
of the amps. This patch adds this functionality via debugfs files.

As this is only needed during manufacture, there is no need for this to be
available in a normal system so a Kconfig item has been added to enable
this. The new Kconfig option is inside a sub-menu because items do not
group and indent if the parent is invisible or there are multiple parent
dependencies. Anyway the sub-menu reduces the clutter.

Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20251021105022.1013685-5-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Richard Fitzgerald and committed by
Mark Brown
191a27fa f7097161

+180
+15
sound/soc/codecs/Kconfig
··· 899 899 config SND_SOC_CS35L56_CAL_DEBUGFS_COMMON 900 900 bool 901 901 902 + menu "CS35L56 driver options" 903 + depends on SND_SOC_CS35L56 904 + 905 + config SND_SOC_CS35L56_CAL_DEBUGFS 906 + bool "CS35L56 create debugfs for factory calibration" 907 + default N 908 + depends on DEBUG_FS 909 + select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON 910 + help 911 + Create debugfs entries used during factory-line manufacture 912 + for factory calibration. 913 + 914 + If unsure select "N". 915 + endmenu 916 + 902 917 config SND_SOC_CS40L50 903 918 tristate "Cirrus Logic CS40L50 CODEC" 904 919 depends on MFD_CS40L50_CORE
+159
sound/soc/codecs/cs35l56.c
··· 10 10 #include <linux/completion.h> 11 11 #include <linux/debugfs.h> 12 12 #include <linux/delay.h> 13 + #include <linux/device.h> 13 14 #include <linux/err.h> 14 15 #include <linux/gpio/consumer.h> 15 16 #include <linux/interrupt.h> ··· 251 250 SND_SOC_DAPM_SIGGEN("VDDBMON ADC"), 252 251 SND_SOC_DAPM_SIGGEN("VBSTMON ADC"), 253 252 SND_SOC_DAPM_SIGGEN("TEMPMON ADC"), 253 + 254 + SND_SOC_DAPM_INPUT("Calibrate"), 254 255 }; 255 256 256 257 #define CS35L56_SRC_ROUTE(name) \ ··· 289 286 { "DSP1", NULL, "ASP1RX1" }, 290 287 { "DSP1", NULL, "ASP1RX2" }, 291 288 { "DSP1", NULL, "SDW1 Playback" }, 289 + { "DSP1", NULL, "Calibrate" }, 292 290 { "AMP", NULL, "DSP1" }, 293 291 { "SPK", NULL, "AMP" }, 294 292 ··· 878 874 pm_runtime_put_autosuspend(cs35l56->base.dev); 879 875 } 880 876 877 + static struct snd_soc_dapm_context *cs35l56_power_up_for_cal(struct cs35l56_private *cs35l56) 878 + { 879 + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component); 880 + int ret; 881 + 882 + ret = snd_soc_component_enable_pin(cs35l56->component, "Calibrate"); 883 + if (ret) 884 + return ERR_PTR(ret); 885 + 886 + snd_soc_dapm_sync(dapm); 887 + 888 + return dapm; 889 + } 890 + 891 + static void cs35l56_power_down_after_cal(struct cs35l56_private *cs35l56) 892 + { 893 + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component); 894 + 895 + snd_soc_component_disable_pin(cs35l56->component, "Calibrate"); 896 + snd_soc_dapm_sync(dapm); 897 + } 898 + 899 + static ssize_t cs35l56_debugfs_calibrate_write(struct file *file, 900 + const char __user *from, 901 + size_t count, loff_t *ppos) 902 + { 903 + struct cs35l56_base *cs35l56_base = file->private_data; 904 + struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base); 905 + struct snd_soc_dapm_context *dapm; 906 + ssize_t ret; 907 + 908 + dapm = cs35l56_power_up_for_cal(cs35l56); 909 + if (IS_ERR(dapm)) 910 + return PTR_ERR(dapm); 911 + 912 + snd_soc_dapm_mutex_lock(dapm); 913 + ret = cs35l56_calibrate_debugfs_write(&cs35l56->base, from, count, ppos); 914 + snd_soc_dapm_mutex_unlock(dapm); 915 + 916 + cs35l56_power_down_after_cal(cs35l56); 917 + 918 + return ret; 919 + } 920 + 921 + static ssize_t cs35l56_debugfs_cal_temperature_write(struct file *file, 922 + const char __user *from, 923 + size_t count, loff_t *ppos) 924 + { 925 + struct cs35l56_base *cs35l56_base = file->private_data; 926 + struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base); 927 + struct snd_soc_dapm_context *dapm; 928 + ssize_t ret; 929 + 930 + dapm = cs35l56_power_up_for_cal(cs35l56); 931 + if (IS_ERR(dapm)) 932 + return PTR_ERR(dapm); 933 + 934 + ret = cs35l56_cal_ambient_debugfs_write(&cs35l56->base, from, count, ppos); 935 + cs35l56_power_down_after_cal(cs35l56); 936 + 937 + return ret; 938 + } 939 + 940 + static ssize_t cs35l56_debugfs_cal_data_read(struct file *file, 941 + char __user *to, 942 + size_t count, loff_t *ppos) 943 + { 944 + struct cs35l56_base *cs35l56_base = file->private_data; 945 + struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base); 946 + struct snd_soc_dapm_context *dapm; 947 + ssize_t ret; 948 + 949 + dapm = cs35l56_power_up_for_cal(cs35l56); 950 + if (IS_ERR(dapm)) 951 + return PTR_ERR(dapm); 952 + 953 + ret = cs35l56_cal_data_debugfs_read(&cs35l56->base, to, count, ppos); 954 + cs35l56_power_down_after_cal(cs35l56); 955 + 956 + return ret; 957 + } 958 + 959 + static int cs35l56_new_cal_data_apply(struct cs35l56_private *cs35l56) 960 + { 961 + struct snd_soc_dapm_context *dapm; 962 + int ret; 963 + 964 + if (!cs35l56->base.cal_data_valid) 965 + return -ENXIO; 966 + 967 + if (cs35l56->base.secured) 968 + return -EACCES; 969 + 970 + dapm = cs35l56_power_up_for_cal(cs35l56); 971 + if (IS_ERR(dapm)) 972 + return PTR_ERR(dapm); 973 + 974 + snd_soc_dapm_mutex_lock(dapm); 975 + ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp, 976 + cs35l56->base.calibration_controls, 977 + &cs35l56->base.cal_data); 978 + if (ret == 0) 979 + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); 980 + else 981 + ret = -EIO; 982 + 983 + snd_soc_dapm_mutex_unlock(dapm); 984 + cs35l56_power_down_after_cal(cs35l56); 985 + 986 + return ret; 987 + } 988 + 989 + static ssize_t cs35l56_debugfs_cal_data_write(struct file *file, 990 + const char __user *from, 991 + size_t count, loff_t *ppos) 992 + { 993 + struct cs35l56_base *cs35l56_base = file->private_data; 994 + struct cs35l56_private *cs35l56 = cs35l56_private_from_base(cs35l56_base); 995 + int ret; 996 + 997 + ret = cs35l56_cal_data_debugfs_write(&cs35l56->base, from, count, ppos); 998 + if (ret == -ENODATA) 999 + return count; /* Ignore writes of empty cal blobs */ 1000 + else if (ret < 0) 1001 + return -EIO; 1002 + 1003 + ret = cs35l56_new_cal_data_apply(cs35l56); 1004 + if (ret) 1005 + return ret; 1006 + 1007 + return count; 1008 + } 1009 + 1010 + static const struct cs35l56_cal_debugfs_fops cs35l56_cal_debugfs_fops = { 1011 + .calibrate = { 1012 + .write = cs35l56_debugfs_calibrate_write, 1013 + }, 1014 + .cal_temperature = { 1015 + .write = cs35l56_debugfs_cal_temperature_write, 1016 + }, 1017 + .cal_data = { 1018 + .read = cs35l56_debugfs_cal_data_read, 1019 + .write = cs35l56_debugfs_cal_data_write, 1020 + }, 1021 + }; 1022 + 881 1023 static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) 882 1024 { 883 1025 if (cs35l56->dsp.fwf_suffix) ··· 1121 971 if (ret) 1122 972 return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n"); 1123 973 974 + ret = snd_soc_component_disable_pin(component, "Calibrate"); 975 + if (ret) 976 + return ret; 977 + 978 + if (IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_DEBUGFS)) 979 + cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_cal_debugfs_fops); 980 + 1124 981 queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); 1125 982 1126 983 return 0; ··· 1138 981 struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); 1139 982 1140 983 cancel_work_sync(&cs35l56->dsp_work); 984 + 985 + cs35l56_remove_cal_debugfs(&cs35l56->base); 1141 986 1142 987 if (cs35l56->dsp.cs_dsp.booted) 1143 988 wm_adsp_power_down(&cs35l56->dsp);
+6
sound/soc/codecs/cs35l56.h
··· 10 10 #define CS35L56_H 11 11 12 12 #include <linux/completion.h> 13 + #include <linux/container_of.h> 13 14 #include <linux/regulator/consumer.h> 14 15 #include <linux/pm_runtime.h> 15 16 #include <linux/workqueue.h> ··· 54 53 u8 sdw_link_num; 55 54 u8 sdw_unique_id; 56 55 }; 56 + 57 + static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_base *cs35l56_base) 58 + { 59 + return container_of(cs35l56_base, struct cs35l56_private, base); 60 + } 57 61 58 62 extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; 59 63