diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index c4aef14a3147..b2171472e6cb 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2401,6 +2401,11 @@ quirk_flags disabling GET_CUR instead of the whole mixer. The current volume will then be provided by the internal cache that stores the last set volume + * bit 31: ``playback_urb_fixup`` + Some devices show the stuttering at playback, and this quirk + works around it by enforcing the fixed max URBs (12) instead of + the dynamic calculation from the buffer size, and passing the + `URB_ISO_ASAP` URB flag. This module supports multiple devices, autoprobe and hotplugging. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9a7ac2eb9ce0..4f4552b1551b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1752,6 +1752,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * by the drivers/platform/x86/serial-multi-instantiate.c driver, which * knows which client device id to use for each resource. */ + {"AWDZ8399", }, {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 1a369334f9cb..3e89fcdd45f7 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -307,6 +307,15 @@ static void smi_remove(struct platform_device *pdev) smi_devs_unregister(smi); } +static const struct smi_node aw88399_hda = { + .instances = { + { "aw88399-hda", IRQ_RESOURCE_AUTO, 0 }, + { "aw88399-hda", IRQ_RESOURCE_AUTO, 0 }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + static const struct smi_node bsg1160_data = { .instances = { { "bmc150_accel", IRQ_RESOURCE_GPIO, 0 }, @@ -405,6 +414,7 @@ static const struct smi_node tas2781_hda = { * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). */ static const struct acpi_device_id smi_acpi_ids[] = { + { "AWDZ8399", (unsigned long)&aw88399_hda }, { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h index 69b404c354f5..69aa7ee15b91 100644 --- a/include/sound/ac97/codec.h +++ b/include/sound/ac97/codec.h @@ -33,7 +33,7 @@ struct ac97_id { }; /** - * ac97_codec_device - a ac97 codec + * struct ac97_codec_device - a ac97 codec * @dev: the core device * @vendor_id: the vendor_id of the codec, as sensed on the AC-link * @num: the codec number, 0 is primary, 1 is first slave, etc ... @@ -53,7 +53,7 @@ struct ac97_codec_device { }; /** - * ac97_codec_driver - a ac97 codec driver + * struct ac97_codec_driver - a ac97 codec driver * @driver: the device driver structure * @probe: the function called when a ac97_codec_device is matched * @remove: the function called when the device is unbound/removed diff --git a/include/sound/asequencer.h b/include/sound/asequencer.h index ddbb6bf801bb..efad366736a4 100644 --- a/include/sound/asequencer.h +++ b/include/sound/asequencer.h @@ -11,9 +11,6 @@ #include #include -/* helper macro */ -#define snd_seq_event_bounce_ext_data(ev) ((void*)((char *)(ev)->data.ext.ptr + sizeof(struct snd_seq_event_bounce))) - /* * type check macros */ diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h new file mode 100644 index 000000000000..a1e0de6be8ea --- /dev/null +++ b/include/sound/aw88399.h @@ -0,0 +1,621 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * linux/sound/aw88399.h -- Platform data for AW88399 + * + * Copyright (c) 2023 AWINIC Technology CO., LTD + * + * Author: Weidong Wang + */ + +#ifndef __SOUND_AW88399_H +#define __SOUND_AW88399_H + +#include +#include + +/* registers list */ +#define AW88399_ID_REG (0x00) +#define AW88399_SYSST_REG (0x01) +#define AW88399_SYSINT_REG (0x02) +#define AW88399_SYSINTM_REG (0x03) +#define AW88399_SYSCTRL_REG (0x04) +#define AW88399_SYSCTRL2_REG (0x05) +#define AW88399_I2SCTRL1_REG (0x06) +#define AW88399_I2SCTRL2_REG (0x07) +#define AW88399_I2SCTRL3_REG (0x08) +#define AW88399_DACCFG1_REG (0x09) +#define AW88399_DACCFG2_REG (0x0A) +#define AW88399_DACCFG3_REG (0x0B) +#define AW88399_DACCFG4_REG (0x0C) +#define AW88399_DACCFG5_REG (0x0D) +#define AW88399_DACCFG6_REG (0x0E) +#define AW88399_DACCFG7_REG (0x0F) +#define AW88399_MPDCFG1_REG (0x10) +#define AW88399_MPDCFG2_REG (0x11) +#define AW88399_MPDCFG3_REG (0x12) +#define AW88399_MPDCFG4_REG (0x13) +#define AW88399_PWMCTRL1_REG (0x14) +#define AW88399_PWMCTRL2_REG (0x15) +#define AW88399_PWMCTRL3_REG (0x16) +#define AW88399_I2SCFG1_REG (0x17) +#define AW88399_DBGCTRL_REG (0x18) +#define AW88399_HAGCST_REG (0x20) +#define AW88399_VBAT_REG (0x21) +#define AW88399_TEMP_REG (0x22) +#define AW88399_PVDD_REG (0x23) +#define AW88399_ISNDAT_REG (0x24) +#define AW88399_VSNDAT_REG (0x25) +#define AW88399_I2SINT_REG (0x26) +#define AW88399_I2SCAPCNT_REG (0x27) +#define AW88399_ANASTA1_REG (0x28) +#define AW88399_ANASTA2_REG (0x29) +#define AW88399_ANASTA3_REG (0x2A) +#define AW88399_TESTDET_REG (0x2B) +#define AW88399_DSMCFG1_REG (0x30) +#define AW88399_DSMCFG2_REG (0x31) +#define AW88399_DSMCFG3_REG (0x32) +#define AW88399_DSMCFG4_REG (0x33) +#define AW88399_DSMCFG5_REG (0x34) +#define AW88399_DSMCFG6_REG (0x35) +#define AW88399_DSMCFG7_REG (0x36) +#define AW88399_DSMCFG8_REG (0x37) +#define AW88399_TESTIN_REG (0x38) +#define AW88399_TESTOUT_REG (0x39) +#define AW88399_MEMTEST_REG (0x3A) +#define AW88399_VSNCTRL1_REG (0x3B) +#define AW88399_ISNCTRL1_REG (0x3C) +#define AW88399_ISNCTRL2_REG (0x3D) +#define AW88399_DSPMADD_REG (0x40) +#define AW88399_DSPMDAT_REG (0x41) +#define AW88399_WDT_REG (0x42) +#define AW88399_ACR1_REG (0x43) +#define AW88399_ACR2_REG (0x44) +#define AW88399_ASR1_REG (0x45) +#define AW88399_ASR2_REG (0x46) +#define AW88399_DSPCFG_REG (0x47) +#define AW88399_ASR3_REG (0x48) +#define AW88399_ASR4_REG (0x49) +#define AW88399_DSPVCALB_REG (0x4A) +#define AW88399_CRCCTRL_REG (0x4B) +#define AW88399_DSPDBG1_REG (0x4C) +#define AW88399_DSPDBG2_REG (0x4D) +#define AW88399_DSPDBG3_REG (0x4E) +#define AW88399_PLLCTRL1_REG (0x50) +#define AW88399_PLLCTRL2_REG (0x51) +#define AW88399_PLLCTRL3_REG (0x52) +#define AW88399_CDACTRL1_REG (0x53) +#define AW88399_CDACTRL2_REG (0x54) +#define AW88399_CDACTRL3_REG (0x55) +#define AW88399_SADCCTRL1_REG (0x56) +#define AW88399_SADCCTRL2_REG (0x57) +#define AW88399_BOPCTRL1_REG (0x58) +#define AW88399_BOPCTRL2_REG (0x5A) +#define AW88399_BOPCTRL3_REG (0x5B) +#define AW88399_BOPCTRL4_REG (0x5C) +#define AW88399_BOPCTRL5_REG (0x5D) +#define AW88399_BOPCTRL6_REG (0x5E) +#define AW88399_BOPCTRL7_REG (0x5F) +#define AW88399_BSTCTRL1_REG (0x60) +#define AW88399_BSTCTRL2_REG (0x61) +#define AW88399_BSTCTRL3_REG (0x62) +#define AW88399_BSTCTRL4_REG (0x63) +#define AW88399_BSTCTRL5_REG (0x64) +#define AW88399_BSTCTRL6_REG (0x65) +#define AW88399_BSTCTRL7_REG (0x66) +#define AW88399_BSTCTRL8_REG (0x67) +#define AW88399_BSTCTRL9_REG (0x68) +#define AW88399_BSTCTRL10_REG (0x69) +#define AW88399_CPCTRL_REG (0x6A) +#define AW88399_EFWH_REG (0x6C) +#define AW88399_EFWM2_REG (0x6D) +#define AW88399_EFWM1_REG (0x6E) +#define AW88399_EFWL_REG (0x6F) +#define AW88399_TESTCTRL1_REG (0x70) +#define AW88399_TESTCTRL2_REG (0x71) +#define AW88399_EFCTRL1_REG (0x72) +#define AW88399_EFCTRL2_REG (0x73) +#define AW88399_EFRH4_REG (0x74) +#define AW88399_EFRH3_REG (0x75) +#define AW88399_EFRH2_REG (0x76) +#define AW88399_EFRH1_REG (0x77) +#define AW88399_EFRL4_REG (0x78) +#define AW88399_EFRL3_REG (0x79) +#define AW88399_EFRL2_REG (0x7A) +#define AW88399_EFRL1_REG (0x7B) +#define AW88399_TM_REG (0x7C) +#define AW88399_TM2_REG (0x7D) + +#define AW88399_REG_MAX (0x7E) +#define AW88399_MUTE_VOL (1023) + +#define AW88399_DSP_CFG_ADDR (0x9B00) +#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) +#define AW88399_DSP_FW_ADDR (0x8980) +#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) +#define AW88399_DSP_ROM_CHECK_DATA (0x4638) + +#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) +#define AW88399_CALI_RE_HBITS_SHIFT (16) + +#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) +#define AW88399_CALI_RE_LBITS_SHIFT (0) + +#define AW88399_I2STXEN_START_BIT (9) +#define AW88399_I2STXEN_BITS_LEN (1) +#define AW88399_I2STXEN_MASK \ + (~(((1<> (shift)) +#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) +#define AW88399_CRC_CHECK_PASS_VAL (0x4) + +#define AW88399_CRC_CFG_BASE_ADDR (0xD80) +#define AW88399_CRC_FW_BASE_ADDR (0x4C0) +#define AW88399_ACF_FILE "aw88399_acf.bin" +#define AW88399_DEV_SYSST_CHECK_MAX (10) +#define AW88399_CHIP_ID 0x2183 + +#define AW88399_START_RETRIES (5) +#define AW88399_START_WORK_DELAY_MS (0) + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + +enum { + AW88399_DEV_VDSEL_DAC = 0, + AW88399_DEV_VDSEL_VSENSE = 32, +}; + +enum { + AW88399_DSP_CRC_NA = 0, + AW88399_DSP_CRC_OK = 1, +}; + +enum { + AW88399_DSP_FW_UPDATE_OFF = 0, + AW88399_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88399_FORCE_UPDATE_OFF = 0, + AW88399_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88399_1000_US = 1000, + AW88399_2000_US = 2000, + AW88399_3000_US = 3000, + AW88399_4000_US = 4000, +}; + +enum AW88399_DEV_STATUS { + AW88399_DEV_PW_OFF = 0, + AW88399_DEV_PW_ON, +}; + +enum AW88399_DEV_FW_STATUS { + AW88399_DEV_FW_FAILED = 0, + AW88399_DEV_FW_OK, +}; + +enum AW88399_DEV_MEMCLK { + AW88399_DEV_MEMCLK_OSC = 0, + AW88399_DEV_MEMCLK_PLL = 1, +}; + +enum AW88399_DEV_DSP_CFG { + AW88399_DEV_DSP_WORK = 0, + AW88399_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88399_NOT_RCV_MODE = 0, + AW88399_RCV_MODE = 1, +}; + +enum { + AW88399_SYNC_START = 0, + AW88399_ASYNC_START, +}; + +struct aw_device; +struct aw_container; +struct aw_cali_desc; +struct gpio_desc; +struct i2c_client; +struct regmap; + +struct aw88399 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + unsigned int check_val; + unsigned int crc_init_val; + unsigned int vcalb_init_val; + unsigned int dither_st; + bool bsts_unreliable; + bool fw_needs_reload; +}; + +int aw_dev_check_syspll(struct aw_device *aw_dev); +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable); +int aw_dev_get_dsp_status(struct aw_device *aw_dev); +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value); +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc); +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute); +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel); +void aw88399_hw_reset(struct aw88399 *aw88399); +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap); +extern const struct regmap_config aw88399_remap_config; +int aw88399_request_firmware_file(struct aw88399 *aw88399); +void aw88399_start(struct aw88399 *aw88399, bool sync_start); +void aw88399_startup_work(struct work_struct *work); +int aw88399_stop(struct aw_device *aw_dev); + +#endif /* __SOUND_AW88399_H */ diff --git a/include/sound/control.h b/include/sound/control.h index e07f6b960641..909db0d0485d 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -7,6 +7,7 @@ * Copyright (c) by Jaroslav Kysela */ +#include #include #include #include @@ -167,6 +168,8 @@ snd_ctl_find_id_mixer(struct snd_card *card, const char *name) int snd_ctl_create(struct snd_card *card); +extern struct rw_semaphore snd_ioctl_rwsem; + int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn); int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn); #ifdef CONFIG_COMPAT diff --git a/include/sound/core.h b/include/sound/core.h index 404785b7d885..2ca24ac7e37f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -108,8 +108,8 @@ struct snd_card { char longname[80]; /* name of this soundcard */ char irq_descr[32]; /* Interrupt description */ char mixername[80]; /* mixer name */ - char components[128]; /* card components delimited with - space */ + char *components; /* card components, space-delimited */ + unsigned int components_alloc_size; /* current allocation size of components */ struct module *module; /* top-level module */ void *private_data; /* private data for soundcard */ diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index b4e327877739..1d05f991f2ce 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -256,6 +256,7 @@ struct hda_codec { unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ + unsigned int acomp_requested_resume:1; /* resume requested by acomp */ unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ unsigned int ctl_dev_id:1; /* old control element id build behaviour */ unsigned int eld_jack_detect:1; /* Machine jack-detection by ELD */ diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h index 4c1b9bebbd60..e06e4847ed28 100644 --- a/include/sound/hda_regmap.h +++ b/include/sound/hda_regmap.h @@ -69,11 +69,14 @@ void snd_hdac_regmap_sync(struct hdac_device *codec); /** * snd_hdac_regmap_write - Write a verb with caching + * @codec: HD-audio codec base device * @nid: codec NID - * @reg: verb to write + * @verb: verb to write * @val: value to write * * For writing an amp value, use snd_hdac_regmap_update_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, @@ -85,13 +88,16 @@ snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, } /** - * snd_hda_regmap_update - Update a verb value with caching + * snd_hdac_regmap_update - Update a verb value with caching + * @codec: HD-audio codec * @nid: codec NID * @verb: verb to update * @mask: bit mask to update * @val: value to update * * For updating an amp value, use snd_hdac_regmap_update_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, @@ -104,12 +110,15 @@ snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, } /** - * snd_hda_regmap_read - Read a verb with caching + * snd_hdac_regmap_read - Read a verb with caching + * @codec: HD-audio codec * @nid: codec NID * @verb: verb to read * @val: pointer to store the value * * For reading an amp value, use snd_hda_regmap_get_amp(). + * + * Returns: %0 if successful or a negative error code. */ static inline int snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, @@ -125,12 +134,12 @@ snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * @val: the pointer to store the value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) * * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. - * Returns the value or a negative error. + * + * Returns: the value or a negative error. */ static inline int snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, @@ -148,13 +157,14 @@ snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, * @codec: HD-audio codec * @nid: NID to read the AMP value * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT + * @dir: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + * + * Returns: 0 if the value is unchanged, 1 if changed, or a negative error. */ static inline int snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, @@ -169,13 +179,12 @@ snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values * @codec: HD-audio codec * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * @val: the pointer to store the value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) * * Read stereo AMP values. The lower byte is left, the upper byte is right. - * Returns the value or a negative error. + * + * Returns: the value or a negative error. */ static inline int snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, @@ -192,14 +201,15 @@ snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value * @codec: HD-audio codec * @nid: NID to read the AMP value - * @direction: #HDA_INPUT or #HDA_OUTPUT + * @dir: #HDA_INPUT or #HDA_OUTPUT * @idx: the index value (only for input direction) * @mask: bit mask to set * @val: the bits value to set * * Update the stereo AMP value with a bit mask. * The lower byte is left, the upper byte is right. - * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + * + * Returns: 0 if the value is unchanged, 1 if changed, or a negative error. */ static inline int snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid, diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 76fc33dce537..02d8689354c4 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -145,61 +145,60 @@ struct snd_pcm_ops { #define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|\ SNDRV_PCM_RATE_705600|\ SNDRV_PCM_RATE_768000) -#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) -#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) -#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) -#define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) -#define SNDRV_PCM_FMTBIT_S16_BE _SNDRV_PCM_FMTBIT(S16_BE) -#define SNDRV_PCM_FMTBIT_U16_LE _SNDRV_PCM_FMTBIT(U16_LE) -#define SNDRV_PCM_FMTBIT_U16_BE _SNDRV_PCM_FMTBIT(U16_BE) -#define SNDRV_PCM_FMTBIT_S24_LE _SNDRV_PCM_FMTBIT(S24_LE) -#define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) -#define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) -#define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) +#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) // For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the // available bit count in most significant bit. It's for the case of so-called 'left-justified' or // `right-padding` sample which has less width than 32 bit. -#define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) -#define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) -#define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) -#define SNDRV_PCM_FMTBIT_U32_BE _SNDRV_PCM_FMTBIT(U32_BE) -#define SNDRV_PCM_FMTBIT_FLOAT_LE _SNDRV_PCM_FMTBIT(FLOAT_LE) -#define SNDRV_PCM_FMTBIT_FLOAT_BE _SNDRV_PCM_FMTBIT(FLOAT_BE) -#define SNDRV_PCM_FMTBIT_FLOAT64_LE _SNDRV_PCM_FMTBIT(FLOAT64_LE) -#define SNDRV_PCM_FMTBIT_FLOAT64_BE _SNDRV_PCM_FMTBIT(FLOAT64_BE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE) -#define SNDRV_PCM_FMTBIT_MU_LAW _SNDRV_PCM_FMTBIT(MU_LAW) -#define SNDRV_PCM_FMTBIT_A_LAW _SNDRV_PCM_FMTBIT(A_LAW) -#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) -#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) -#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) -#define SNDRV_PCM_FMTBIT_S20_LE _SNDRV_PCM_FMTBIT(S20_LE) -#define SNDRV_PCM_FMTBIT_U20_LE _SNDRV_PCM_FMTBIT(U20_LE) -#define SNDRV_PCM_FMTBIT_S20_BE _SNDRV_PCM_FMTBIT(S20_BE) -#define SNDRV_PCM_FMTBIT_U20_BE _SNDRV_PCM_FMTBIT(U20_BE) -#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) -#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) -#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) -#define SNDRV_PCM_FMTBIT_S24_3BE _SNDRV_PCM_FMTBIT(S24_3BE) -#define SNDRV_PCM_FMTBIT_U24_3BE _SNDRV_PCM_FMTBIT(U24_3BE) -#define SNDRV_PCM_FMTBIT_S20_3LE _SNDRV_PCM_FMTBIT(S20_3LE) -#define SNDRV_PCM_FMTBIT_U20_3LE _SNDRV_PCM_FMTBIT(U20_3LE) -#define SNDRV_PCM_FMTBIT_S20_3BE _SNDRV_PCM_FMTBIT(S20_3BE) -#define SNDRV_PCM_FMTBIT_U20_3BE _SNDRV_PCM_FMTBIT(U20_3BE) -#define SNDRV_PCM_FMTBIT_S18_3LE _SNDRV_PCM_FMTBIT(S18_3LE) -#define SNDRV_PCM_FMTBIT_U18_3LE _SNDRV_PCM_FMTBIT(U18_3LE) -#define SNDRV_PCM_FMTBIT_S18_3BE _SNDRV_PCM_FMTBIT(S18_3BE) -#define SNDRV_PCM_FMTBIT_U18_3BE _SNDRV_PCM_FMTBIT(U18_3BE) -#define SNDRV_PCM_FMTBIT_G723_24 _SNDRV_PCM_FMTBIT(G723_24) -#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) -#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) -#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) -#define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8) -#define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE) -#define SNDRV_PCM_FMTBIT_DSD_U32_LE _SNDRV_PCM_FMTBIT(DSD_U32_LE) -#define SNDRV_PCM_FMTBIT_DSD_U16_BE _SNDRV_PCM_FMTBIT(DSD_U16_BE) -#define SNDRV_PCM_FMTBIT_DSD_U32_BE _SNDRV_PCM_FMTBIT(DSD_U32_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_S20_LE (1ULL << SNDRV_PCM_FORMAT_S20_LE) +#define SNDRV_PCM_FMTBIT_U20_LE (1ULL << SNDRV_PCM_FORMAT_U20_LE) +#define SNDRV_PCM_FMTBIT_S20_BE (1ULL << SNDRV_PCM_FORMAT_S20_BE) +#define SNDRV_PCM_FMTBIT_U20_BE (1ULL << SNDRV_PCM_FORMAT_U20_BE) +#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) +#define SNDRV_PCM_FMTBIT_G723_24 (1ULL << SNDRV_PCM_FORMAT_G723_24) +#define SNDRV_PCM_FMTBIT_G723_24_1B (1ULL << SNDRV_PCM_FORMAT_G723_24_1B) +#define SNDRV_PCM_FMTBIT_G723_40 (1ULL << SNDRV_PCM_FORMAT_G723_40) +#define SNDRV_PCM_FMTBIT_G723_40_1B (1ULL << SNDRV_PCM_FORMAT_G723_40_1B) +#define SNDRV_PCM_FMTBIT_DSD_U8 (1ULL << SNDRV_PCM_FORMAT_DSD_U8) +#define SNDRV_PCM_FMTBIT_DSD_U16_LE (1ULL << SNDRV_PCM_FORMAT_DSD_U16_LE) +#define SNDRV_PCM_FMTBIT_DSD_U32_LE (1ULL << SNDRV_PCM_FORMAT_DSD_U32_LE) +#define SNDRV_PCM_FMTBIT_DSD_U16_BE (1ULL << SNDRV_PCM_FORMAT_DSD_U16_BE) +#define SNDRV_PCM_FMTBIT_DSD_U32_BE (1ULL << SNDRV_PCM_FORMAT_DSD_U32_BE) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE @@ -228,11 +227,10 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_U20 SNDRV_PCM_FMTBIT_U20_BE #endif -#define _SNDRV_PCM_SUBFMTBIT(fmt) BIT((__force int)SNDRV_PCM_SUBFORMAT_##fmt) -#define SNDRV_PCM_SUBFMTBIT_STD _SNDRV_PCM_SUBFMTBIT(STD) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX _SNDRV_PCM_SUBFMTBIT(MSBITS_MAX) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 _SNDRV_PCM_SUBFMTBIT(MSBITS_20) -#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 _SNDRV_PCM_SUBFMTBIT(MSBITS_24) +#define SNDRV_PCM_SUBFMTBIT_STD (1U << SNDRV_PCM_SUBFORMAT_STD) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_MAX (1U << SNDRV_PCM_SUBFORMAT_MSBITS_MAX) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_20 (1U << SNDRV_PCM_SUBFORMAT_MSBITS_20) +#define SNDRV_PCM_SUBFMTBIT_MSBITS_24 (1U << SNDRV_PCM_SUBFORMAT_MSBITS_24) struct snd_pcm_file { struct snd_pcm_substream *substream; @@ -1515,7 +1513,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, */ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) { - return 1ULL << (__force int) pcm_format; + return 1ULL << pcm_format; } /** @@ -1523,9 +1521,7 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) * @f: the iterator variable in snd_pcm_format_t type */ #define pcm_for_each_format(f) \ - for ((f) = SNDRV_PCM_FORMAT_FIRST; \ - (__force int)(f) <= (__force int)SNDRV_PCM_FORMAT_LAST; \ - (f) = (__force snd_pcm_format_t)((__force int)(f) + 1)) + for ((f) = SNDRV_PCM_FORMAT_FIRST; (f) <= SNDRV_PCM_FORMAT_LAST; (f)++) /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index fbf35df6e5cf..bc43955fcb76 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -71,7 +71,7 @@ static inline void snd_mask_set(struct snd_mask *mask, unsigned int val) static inline void snd_mask_set_format(struct snd_mask *mask, snd_pcm_format_t format) { - snd_mask_set(mask, (__force unsigned int)format); + snd_mask_set(mask, format); } static inline void snd_mask_reset(struct snd_mask *mask, unsigned int val) @@ -132,7 +132,7 @@ static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val) static inline int snd_mask_test_format(const struct snd_mask *mask, snd_pcm_format_t format) { - return snd_mask_test(mask, (__force unsigned int)format); + return snd_mask_test(mask, format); } static inline int snd_mask_single(const struct snd_mask *mask) @@ -302,8 +302,7 @@ static inline int snd_interval_eq(const struct snd_interval *i1, const struct sn */ static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_access_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_ACCESS)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_ACCESS)); } /** @@ -312,8 +311,7 @@ static inline snd_pcm_access_t params_access(const struct snd_pcm_hw_params *p) */ static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_format_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_FORMAT)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_FORMAT)); } /** @@ -323,8 +321,7 @@ static inline snd_pcm_format_t params_format(const struct snd_pcm_hw_params *p) static inline snd_pcm_subformat_t params_subformat(const struct snd_pcm_hw_params *p) { - return (__force snd_pcm_subformat_t)snd_mask_min(hw_param_mask_c(p, - SNDRV_PCM_HW_PARAM_SUBFORMAT)); + return snd_mask_min(hw_param_mask_c(p, SNDRV_PCM_HW_PARAM_SUBFORMAT)); } /** diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 6916f7133597..88a6159364d0 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -176,8 +176,9 @@ int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream); /* main midi functions */ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info); -int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile); +int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth); int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile); int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params *params); @@ -191,6 +192,15 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream, const unsigned char *buf, long count); +/* non-nested version */ +static inline int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, + int subdevice, + int mode, + struct snd_rawmidi_file *rfile) +{ + return snd_rawmidi_kernel_open_nested(rmidi, subdevice, mode, rfile, 0); +} + /* set up the tied devices */ static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1, struct snd_rawmidi *r2) diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h index 56a3f38df8c3..359cb363369d 100644 --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -46,7 +46,6 @@ struct snd_virmidi_dev { int client; /* created/attached client */ int port; /* created/attached port */ unsigned int flags; /* SNDRV_VIRMIDI_* */ - rwlock_t filelist_lock; struct rw_semaphore filelist_sem; struct list_head filelist; }; diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index a5c41f771e05..c2e3d079c51e 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -308,16 +308,6 @@ struct snd_seq_ump_event { }; }; -/* - * bounce event - stored as variable size data - */ -struct snd_seq_event_bounce { - int err; - struct snd_seq_event event; - /* external data follows here. */ -}; - - /* system information */ struct snd_seq_system_info { int queues; /* maximum queues count */ @@ -348,10 +338,10 @@ struct snd_seq_running_info { /* client types */ -typedef int __bitwise snd_seq_client_type_t; -#define NO_CLIENT ((__force snd_seq_client_type_t) 0) -#define USER_CLIENT ((__force snd_seq_client_type_t) 1) -#define KERNEL_CLIENT ((__force snd_seq_client_type_t) 2) +typedef int snd_seq_client_type_t; +#define NO_CLIENT 0 +#define USER_CLIENT 1 +#define KERNEL_CLIENT 2 /* event filter flags */ #define SNDRV_SEQ_FILTER_BROADCAST (1U<<0) /* accept broadcast messages */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index d3ce75ba938a..c11da9656e38 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -169,72 +169,72 @@ enum { SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, }; -typedef int __bitwise snd_pcm_access_t; -#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ((__force snd_pcm_access_t) 0) /* interleaved mmap */ -#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ((__force snd_pcm_access_t) 1) /* noninterleaved mmap */ -#define SNDRV_PCM_ACCESS_MMAP_COMPLEX ((__force snd_pcm_access_t) 2) /* complex mmap */ -#define SNDRV_PCM_ACCESS_RW_INTERLEAVED ((__force snd_pcm_access_t) 3) /* readi/writei */ -#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ((__force snd_pcm_access_t) 4) /* readn/writen */ +typedef int snd_pcm_access_t; +#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED 0 /* interleaved mmap */ +#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED 1 /* noninterleaved mmap */ +#define SNDRV_PCM_ACCESS_MMAP_COMPLEX 2 /* complex mmap */ +#define SNDRV_PCM_ACCESS_RW_INTERLEAVED 3 /* readi/writei */ +#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED 4 /* readn/writen */ #define SNDRV_PCM_ACCESS_LAST SNDRV_PCM_ACCESS_RW_NONINTERLEAVED -typedef int __bitwise snd_pcm_format_t; -#define SNDRV_PCM_FORMAT_S8 ((__force snd_pcm_format_t) 0) -#define SNDRV_PCM_FORMAT_U8 ((__force snd_pcm_format_t) 1) -#define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2) -#define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3) -#define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4) -#define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5) -#define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6) /* low three bytes */ -#define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */ -#define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */ -#define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */ +typedef int snd_pcm_format_t; +#define SNDRV_PCM_FORMAT_S8 0 +#define SNDRV_PCM_FORMAT_U8 1 +#define SNDRV_PCM_FORMAT_S16_LE 2 +#define SNDRV_PCM_FORMAT_S16_BE 3 +#define SNDRV_PCM_FORMAT_U16_LE 4 +#define SNDRV_PCM_FORMAT_U16_BE 5 +#define SNDRV_PCM_FORMAT_S24_LE 6 /* low three bytes */ +#define SNDRV_PCM_FORMAT_S24_BE 7 /* low three bytes */ +#define SNDRV_PCM_FORMAT_U24_LE 8 /* low three bytes */ +#define SNDRV_PCM_FORMAT_U24_BE 9 /* low three bytes */ /* * For S32/U32 formats, 'msbits' hardware parameter is often used to deliver information about the * available bit count in most significant bit. It's for the case of so-called 'left-justified' or * `right-padding` sample which has less width than 32 bit. */ -#define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) -#define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) -#define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) -#define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13) -#define SNDRV_PCM_FORMAT_FLOAT_LE ((__force snd_pcm_format_t) 14) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT_BE ((__force snd_pcm_format_t) 15) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT64_LE ((__force snd_pcm_format_t) 16) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_FLOAT64_BE ((__force snd_pcm_format_t) 17) /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ -#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18) /* IEC-958 subframe, Little Endian */ -#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19) /* IEC-958 subframe, Big Endian */ -#define SNDRV_PCM_FORMAT_MU_LAW ((__force snd_pcm_format_t) 20) -#define SNDRV_PCM_FORMAT_A_LAW ((__force snd_pcm_format_t) 21) -#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22) -#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23) -#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24) -#define SNDRV_PCM_FORMAT_S20_LE ((__force snd_pcm_format_t) 25) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_S20_BE ((__force snd_pcm_format_t) 26) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_U20_LE ((__force snd_pcm_format_t) 27) /* in four bytes, LSB justified */ -#define SNDRV_PCM_FORMAT_U20_BE ((__force snd_pcm_format_t) 28) /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_S32_LE 10 +#define SNDRV_PCM_FORMAT_S32_BE 11 +#define SNDRV_PCM_FORMAT_U32_LE 12 +#define SNDRV_PCM_FORMAT_U32_BE 13 +#define SNDRV_PCM_FORMAT_FLOAT_LE 14 /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT_BE 15 /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT64_LE 16 /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_FLOAT64_BE 17 /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE 18 /* IEC-958 subframe, Little Endian */ +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE 19 /* IEC-958 subframe, Big Endian */ +#define SNDRV_PCM_FORMAT_MU_LAW 20 +#define SNDRV_PCM_FORMAT_A_LAW 21 +#define SNDRV_PCM_FORMAT_IMA_ADPCM 22 +#define SNDRV_PCM_FORMAT_MPEG 23 +#define SNDRV_PCM_FORMAT_GSM 24 +#define SNDRV_PCM_FORMAT_S20_LE 25 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_S20_BE 26 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_U20_LE 27 /* in four bytes, LSB justified */ +#define SNDRV_PCM_FORMAT_U20_BE 28 /* in four bytes, LSB justified */ /* gap in the numbering for a future standard linear format */ -#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31) -#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U24_3LE ((__force snd_pcm_format_t) 34) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U24_3BE ((__force snd_pcm_format_t) 35) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S20_3LE ((__force snd_pcm_format_t) 36) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S20_3BE ((__force snd_pcm_format_t) 37) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U20_3LE ((__force snd_pcm_format_t) 38) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U20_3BE ((__force snd_pcm_format_t) 39) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S18_3LE ((__force snd_pcm_format_t) 40) /* in three bytes */ -#define SNDRV_PCM_FORMAT_S18_3BE ((__force snd_pcm_format_t) 41) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U18_3LE ((__force snd_pcm_format_t) 42) /* in three bytes */ -#define SNDRV_PCM_FORMAT_U18_3BE ((__force snd_pcm_format_t) 43) /* in three bytes */ -#define SNDRV_PCM_FORMAT_G723_24 ((__force snd_pcm_format_t) 44) /* 8 samples in 3 bytes */ -#define SNDRV_PCM_FORMAT_G723_24_1B ((__force snd_pcm_format_t) 45) /* 1 sample in 1 byte */ -#define SNDRV_PCM_FORMAT_G723_40 ((__force snd_pcm_format_t) 46) /* 8 Samples in 5 bytes */ -#define SNDRV_PCM_FORMAT_G723_40_1B ((__force snd_pcm_format_t) 47) /* 1 sample in 1 byte */ -#define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */ -#define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */ -#define SNDRV_PCM_FORMAT_DSD_U32_LE ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */ -#define SNDRV_PCM_FORMAT_DSD_U16_BE ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */ -#define SNDRV_PCM_FORMAT_DSD_U32_BE ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */ +#define SNDRV_PCM_FORMAT_SPECIAL 31 +#define SNDRV_PCM_FORMAT_S24_3LE 32 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S24_3BE 33 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U24_3LE 34 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U24_3BE 35 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S20_3LE 36 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S20_3BE 37 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U20_3LE 38 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U20_3BE 39 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S18_3LE 40 /* in three bytes */ +#define SNDRV_PCM_FORMAT_S18_3BE 41 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U18_3LE 42 /* in three bytes */ +#define SNDRV_PCM_FORMAT_U18_3BE 43 /* in three bytes */ +#define SNDRV_PCM_FORMAT_G723_24 44 /* 8 samples in 3 bytes */ +#define SNDRV_PCM_FORMAT_G723_24_1B 45 /* 1 sample in 1 byte */ +#define SNDRV_PCM_FORMAT_G723_40 46 /* 8 Samples in 5 bytes */ +#define SNDRV_PCM_FORMAT_G723_40_1B 47 /* 1 sample in 1 byte */ +#define SNDRV_PCM_FORMAT_DSD_U8 48 /* DSD, 1-byte samples DSD (x8) */ +#define SNDRV_PCM_FORMAT_DSD_U16_LE 49 /* DSD, 2-byte samples DSD (x16), little endian */ +#define SNDRV_PCM_FORMAT_DSD_U32_LE 50 /* DSD, 4-byte samples DSD (x32), little endian */ +#define SNDRV_PCM_FORMAT_DSD_U16_BE 51 /* DSD, 2-byte samples DSD (x16), big endian */ +#define SNDRV_PCM_FORMAT_DSD_U32_BE 52 /* DSD, 4-byte samples DSD (x32), big endian */ #define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_BE #define SNDRV_PCM_FORMAT_FIRST SNDRV_PCM_FORMAT_S8 @@ -265,11 +265,11 @@ typedef int __bitwise snd_pcm_format_t; #define SNDRV_PCM_FORMAT_U20 SNDRV_PCM_FORMAT_U20_BE #endif -typedef int __bitwise snd_pcm_subformat_t; -#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX ((__force snd_pcm_subformat_t) 1) -#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((__force snd_pcm_subformat_t) 2) -#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((__force snd_pcm_subformat_t) 3) +typedef int snd_pcm_subformat_t; +#define SNDRV_PCM_SUBFORMAT_STD 0 +#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX 1 +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 2 +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 3 #define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ @@ -303,16 +303,16 @@ typedef int __bitwise snd_pcm_subformat_t; #define __SND_STRUCT_TIME64 #endif -typedef int __bitwise snd_pcm_state_t; -#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ -#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ -#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2) /* stream is ready to start */ -#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3) /* stream is running */ -#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4) /* stream reached an xrun */ -#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5) /* stream is draining */ -#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6) /* stream is paused */ -#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7) /* hardware is suspended */ -#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8) /* hardware is disconnected */ +typedef int snd_pcm_state_t; +#define SNDRV_PCM_STATE_OPEN 0 /* stream is open */ +#define SNDRV_PCM_STATE_SETUP 1 /* stream has a setup */ +#define SNDRV_PCM_STATE_PREPARED 2 /* stream is ready to start */ +#define SNDRV_PCM_STATE_RUNNING 3 /* stream is running */ +#define SNDRV_PCM_STATE_XRUN 4 /* stream reached an xrun */ +#define SNDRV_PCM_STATE_DRAINING 5 /* stream is draining */ +#define SNDRV_PCM_STATE_PAUSED 6 /* stream is paused */ +#define SNDRV_PCM_STATE_SUSPENDED 7 /* hardware is suspended */ +#define SNDRV_PCM_STATE_DISCONNECTED 8 /* hardware is disconnected */ #define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED enum { @@ -1058,7 +1058,7 @@ struct snd_timer_tread { * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) struct snd_ctl_card_info { int card; /* card number */ @@ -1072,24 +1072,43 @@ struct snd_ctl_card_info { unsigned char components[128]; /* card components / fine identification, delimited with one space (AC97 etc..) */ }; -typedef int __bitwise snd_ctl_elem_type_t; -#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) /* invalid */ -#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) /* boolean type */ -#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2) /* integer type */ -#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3) /* enumerated type */ -#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4) /* byte array */ -#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5) /* IEC958 (S/PDIF) setup */ -#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6) /* 64-bit integer type */ +/* + * Card components can exceed the fixed 128 bytes in snd_ctl_card_info. + * Use SNDRV_CTL_IOCTL_CARD_BYTES with type SND_CTL_CARD_BTYPE_COMPONENTS + * to retrieve the full string. + */ + +/* Type values for struct snd_ctl_card_bytes::type */ +enum { + SND_CTL_CARD_BTYPE_COMPONENTS = 1, /* full card components string */ +}; + +struct snd_ctl_card_bytes { + __u32 type; /* SND_CTL_CARD_BTYPE_* */ + __u32 data_allocated; /* size of @data buffer in bytes */ + __u32 data_len; /* in/out: actual data length in bytes */ + __u32 reserved; /* explicit pad */ + __u64 data; /* user buffer (pointer stored as __u64) */ +}; + +typedef int snd_ctl_elem_type_t; +#define SNDRV_CTL_ELEM_TYPE_NONE 0 /* invalid */ +#define SNDRV_CTL_ELEM_TYPE_BOOLEAN 1 /* boolean type */ +#define SNDRV_CTL_ELEM_TYPE_INTEGER 2 /* integer type */ +#define SNDRV_CTL_ELEM_TYPE_ENUMERATED 3 /* enumerated type */ +#define SNDRV_CTL_ELEM_TYPE_BYTES 4 /* byte array */ +#define SNDRV_CTL_ELEM_TYPE_IEC958 5 /* IEC958 (S/PDIF) setup */ +#define SNDRV_CTL_ELEM_TYPE_INTEGER64 6 /* 64-bit integer type */ #define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64 -typedef int __bitwise snd_ctl_elem_iface_t; -#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0) /* global control */ -#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1) /* hardware dependent device */ -#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2) /* virtual mixer device */ -#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3) /* PCM device */ -#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4) /* RawMidi device */ -#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5) /* timer device */ -#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6) /* sequencer client */ +typedef int snd_ctl_elem_iface_t; +#define SNDRV_CTL_ELEM_IFACE_CARD 0 /* global control */ +#define SNDRV_CTL_ELEM_IFACE_HWDEP 1 /* hardware dependent device */ +#define SNDRV_CTL_ELEM_IFACE_MIXER 2 /* virtual mixer device */ +#define SNDRV_CTL_ELEM_IFACE_PCM 3 /* PCM device */ +#define SNDRV_CTL_ELEM_IFACE_RAWMIDI 4 /* RawMidi device */ +#define SNDRV_CTL_ELEM_IFACE_TIMER 5 /* timer device */ +#define SNDRV_CTL_ELEM_IFACE_SEQUENCER 6 /* sequencer client */ #define SNDRV_CTL_ELEM_IFACE_LAST SNDRV_CTL_ELEM_IFACE_SEQUENCER #define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) @@ -1198,6 +1217,7 @@ struct snd_ctl_tlv { #define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int) #define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info) +#define SNDRV_CTL_IOCTL_CARD_BYTES _IOWR('U', 0x02, struct snd_ctl_card_bytes) #define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list) #define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info) #define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value) diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 1e86872c151f..1235d89d2bff 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -79,11 +79,12 @@ struct snd_firewire_event_motu_register_dsp_change { * * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE. * @message_count: The number of messages. + * @messages: Array of @message_count messages * @messages.message: The messages expressing hardware knob operation. * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous - * transaction was sent to deliver the message. It has 16 bit unsigned integer + * transaction was sent to deliver the message. It has 16-bit unsigned integer * value. The higher 3 bits of value expresses the lower three bits of second - * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle + * field in the format of CYCLE_TIME, up to 7. The remaining 13 bits express cycle * field up to 7999. * * The structure expresses message transmitted by Fireface 400 when operating hardware knob. @@ -191,8 +192,9 @@ struct snd_firewire_motu_register_dsp_meter { #define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_ALIGNED_INPUT_COUNT (SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT + 2) /** - * snd_firewire_motu_register_dsp_parameter - the container for parameters of DSP controlled - * by register access. + * struct snd_firewire_motu_register_dsp_parameter - the container for parameters + * of DSP controlled by register access. + * @mixer: aggregate of @mixer.source and @mixer.output * @mixer.source.gain: The gain of source to mixer. * @mixer.source.pan: The L/R balance of source to mixer. * @mixer.source.flag: The flag of source to mixer, including mute, solo. @@ -200,21 +202,25 @@ struct snd_firewire_motu_register_dsp_meter { * Audio Express. * @mixer.source.paired_width: The width of paired source to mixer, only for 4 pre and * Audio Express. + * @mixer.output: FIXME * @mixer.output.paired_volume: The volume of paired output from mixer. * @mixer.output.paired_flag: The flag of paired output from mixer. + * @output: output parameters * @output.main_paired_volume: The volume of paired main output. * @output.hp_paired_volume: The volume of paired hp output. * @output.hp_paired_assignment: The source assigned to paired hp output. - * @output.reserved: Padding for 32 bit alignment for future extension. + * @output.reserved: Padding for 32-bit alignment for future extension. + * @line_input: line input parameters * @line_input.boost_flag: The flags of boost for line inputs, only for 828mk2 and Traveler. * @line_input.nominal_level_flag: The flags of nominal level for line inputs, only for 828mk2 and * Traveler. - * @line_input.reserved: Padding for 32 bit alignment for future extension. + * @line_input.reserved: Padding for 32-bit alignment for future extension. + * @input: input parameters * @input.gain_and_invert: The value including gain and invert for input, only for Ultralite, 4 pre * and Audio Express. * @input.flag: The flag of input; e.g. jack detection, phantom power, and pad, only for Ultralite, * 4 pre and Audio express. - * @reserved: Padding so that the size of structure is kept to 512 byte, but for future extension. + * @reserved: Padding so that the size of structure is kept to 512 bytes, but for future extension. * * The structure expresses the set of parameters for DSP controlled by register access. */ @@ -272,8 +278,8 @@ struct snd_firewire_motu_register_dsp_parameter { * controlled by command * @data: Signal level meters. The mapping between position and signal channel is model-dependent. * - * The structure expresses the part of DSP status for hardware meter. The 32 bit storage is - * estimated to include IEEE 764 32 bit single precision floating point (binary32) value. It is + * The structure expresses the part of DSP status for hardware meter. The 32-bit storage is + * estimated to include IEEE 764 32-bit single precision floating point (binary32) value. It is * expected to be linear value (not logarithm) for audio signal level between 0.0 and +1.0. */ struct snd_firewire_motu_command_dsp_meter { diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index e394205f469b..234549e17351 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -693,7 +693,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct snd_card *card; struct atmel_ac97c *chip; - struct resource *regs; + void __iomem *regs; struct clk *pclk; static const struct snd_ac97_bus_ops ops = { .write = atmel_ac97c_write, @@ -702,42 +702,34 @@ static int atmel_ac97c_probe(struct platform_device *pdev) int retval; int irq; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_dbg(&pdev->dev, "no memory resource\n"); - return -ENXIO; - } + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_dbg(&pdev->dev, "could not get irq: %d\n", irq); + if (irq < 0) return irq; - } - pclk = clk_get(&pdev->dev, "ac97_clk"); + pclk = devm_clk_get_enabled(&pdev->dev, "ac97_clk"); if (IS_ERR(pclk)) { dev_dbg(&pdev->dev, "no peripheral clock\n"); return PTR_ERR(pclk); } - retval = clk_prepare_enable(pclk); - if (retval) - goto err_prepare_enable; - retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, + retval = snd_devm_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(struct atmel_ac97c), &card); if (retval) { dev_dbg(&pdev->dev, "could not create sound card device\n"); - goto err_snd_card_new; + return retval; } chip = get_chip(card); - retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); - if (retval) { - dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); - goto err_request_irq; - } + retval = devm_request_irq(&pdev->dev, irq, atmel_ac97c_interrupt, 0, "AC97C", chip); + if (retval) + return retval; + chip->irq = irq; spin_lock_init(&chip->lock); @@ -749,13 +741,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) chip->card = card; chip->pclk = pclk; chip->pdev = pdev; - chip->regs = ioremap(regs->start, resource_size(regs)); - - if (!chip->regs) { - dev_dbg(&pdev->dev, "could not remap register memory\n"); - retval = -ENOMEM; - goto err_ioremap; - } + chip->regs = regs; chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH); if (IS_ERR(chip->reset_pin)) @@ -770,25 +756,25 @@ static int atmel_ac97c_probe(struct platform_device *pdev) retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); if (retval) { dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); - goto err_ac97_bus; + return retval; } retval = atmel_ac97c_mixer_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); - goto err_ac97_bus; + return retval; } retval = atmel_ac97c_pcm_new(chip); if (retval) { dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); - goto err_ac97_bus; + return retval; } retval = snd_card_register(card); if (retval) { dev_dbg(&pdev->dev, "could not register sound card\n"); - goto err_ac97_bus; + return retval; } platform_set_drvdata(pdev, card); @@ -797,18 +783,6 @@ static int atmel_ac97c_probe(struct platform_device *pdev) chip->regs, irq); return 0; - -err_ac97_bus: - iounmap(chip->regs); -err_ioremap: - free_irq(irq, chip); -err_request_irq: - snd_card_free(card); -err_snd_card_new: - clk_disable_unprepare(pclk); -err_prepare_enable: - clk_put(pclk); - return retval; } static int atmel_ac97c_suspend(struct device *pdev) @@ -839,13 +813,6 @@ static void atmel_ac97c_remove(struct platform_device *pdev) ac97c_writel(chip, CAMR, 0); ac97c_writel(chip, COMR, 0); ac97c_writel(chip, MR, 0); - - clk_disable_unprepare(chip->pclk); - clk_put(chip->pclk); - iounmap(chip->regs); - free_irq(chip->irq, chip); - - snd_card_free(card); } static struct platform_driver atmel_ac97c_driver = { diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index ea699491f0c3..23d62fede06e 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1393,17 +1393,17 @@ static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd, #endif static const struct file_operations snd_compr_file_ops = { - .owner = THIS_MODULE, - .open = snd_compr_open, - .release = snd_compr_free, - .write = snd_compr_write, - .read = snd_compr_read, - .unlocked_ioctl = snd_compr_ioctl, + .owner = THIS_MODULE, + .open = snd_compr_open, + .release = snd_compr_free, + .write = snd_compr_write, + .read = snd_compr_read, + .unlocked_ioctl = snd_compr_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = snd_compr_ioctl_compat, + .compat_ioctl = snd_compr_ioctl_compat, #endif - .mmap = snd_compr_mmap, - .poll = snd_compr_poll, + .mmap = snd_compr_mmap, + .poll = snd_compr_poll, }; static int snd_compress_dev_register(struct snd_device *device) diff --git a/sound/core/control.c b/sound/core/control.c index 7a8dc506221e..78ce7bc936d2 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -38,7 +38,7 @@ struct snd_kctl_ioctl { snd_kctl_ioctl_func_t fioctl; }; -static DECLARE_RWSEM(snd_ioctl_rwsem); +DECLARE_RWSEM(snd_ioctl_rwsem); static DECLARE_RWSEM(snd_ctl_layer_rwsem); static LIST_HEAD(snd_control_ioctls); #ifdef CONFIG_COMPAT @@ -55,9 +55,7 @@ static int snd_ctl_open(struct inode *inode, struct file *file) struct snd_ctl_file *ctl; int i, err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL); if (!card) { @@ -65,12 +63,10 @@ static int snd_ctl_open(struct inode *inode, struct file *file) goto __error1; } err = snd_card_file_add(card, file); - if (err < 0) { - err = -ENODEV; + if (err < 0) goto __error1; - } if (!try_module_get(card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); @@ -876,23 +872,81 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, { struct snd_ctl_card_info *info __free(kfree) = kzalloc(sizeof(*info), GFP_KERNEL); + ssize_t n; if (! info) return -ENOMEM; + + static_assert(sizeof(info->components) >= 2); + scoped_guard(rwsem_read, &snd_ioctl_rwsem) { + const char *components = card->components; + + if (!components) + components = ""; + info->card = card->number; strscpy(info->id, card->id, sizeof(info->id)); strscpy(info->driver, card->driver, sizeof(info->driver)); strscpy(info->name, card->shortname, sizeof(info->name)); strscpy(info->longname, card->longname, sizeof(info->longname)); strscpy(info->mixername, card->mixername, sizeof(info->mixername)); - strscpy(info->components, card->components, sizeof(info->components)); + n = strscpy(info->components, components, sizeof(info->components)); + if (n < 0) // mark the truncation with '>' before NULL terminator + info->components[sizeof(info->components) - 2] = '>'; } if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) return -EFAULT; return 0; } +static int snd_ctl_card_bytes(struct snd_card *card, + struct snd_ctl_card_bytes *info, + unsigned int __user *data_len_out) +{ + unsigned int data_len; + + switch (info->type) { + case SND_CTL_CARD_BTYPE_COMPONENTS: + scoped_guard(rwsem_read, &snd_ioctl_rwsem) { + const char *components = card->components; + + if (!components) + components = ""; + + data_len = strlen(components) + 1; + + if (!info->data || info->data_allocated == 0) + break; + + if (info->data_allocated < data_len) + return -ENOMEM; + + if (copy_to_user(u64_to_user_ptr(info->data), components, data_len)) + return -EFAULT; + } + break; + default: + return -EINVAL; + } + + if (put_user(data_len, data_len_out)) + return -EFAULT; + + return 0; +} + +static int snd_ctl_card_bytes_user(struct snd_card *card, + struct snd_ctl_card_bytes __user *_info) +{ + struct snd_ctl_card_bytes info; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + + return snd_ctl_card_bytes(card, &info, &_info->data_len); +} + static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list *list) { @@ -1990,6 +2044,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; case SNDRV_CTL_IOCTL_CARD_INFO: return snd_ctl_card_info(card, ctl, cmd, argp); + case SNDRV_CTL_IOCTL_CARD_BYTES: + return snd_ctl_card_bytes_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_LIST: return snd_ctl_elem_list_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: @@ -2339,16 +2395,15 @@ EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer); * INIT PART */ -static const struct file_operations snd_ctl_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_ctl_read, - .open = snd_ctl_open, - .release = snd_ctl_release, - .poll = snd_ctl_poll, - .unlocked_ioctl = snd_ctl_ioctl, - .compat_ioctl = snd_ctl_ioctl_compat, - .fasync = snd_ctl_fasync, +static const struct file_operations snd_ctl_f_ops = { + .owner = THIS_MODULE, + .read = snd_ctl_read, + .open = snd_ctl_open, + .release = snd_ctl_release, + .poll = snd_ctl_poll, + .unlocked_ioctl = snd_ctl_ioctl, + .compat_ioctl = snd_ctl_ioctl_compat, + .fasync = snd_ctl_fasync, }; /* call lops under rwsems; called from snd_ctl_dev_*() below() */ diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 16bc80555f26..87f7ad5ab739 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -226,8 +226,8 @@ static int copy_ctl_value_from_user(struct snd_card *card, if (type < 0) return type; - if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN || - type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { s32 __user *intp = valuep; int val; @@ -236,7 +236,7 @@ static int copy_ctl_value_from_user(struct snd_card *card, data->value.integer.value[i] = val; } } else { - size = get_elem_size((__force snd_ctl_elem_type_t)type, count); + size = get_elem_size(type, count); if (size < 0) { dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); return -EINVAL; @@ -259,8 +259,8 @@ static int copy_ctl_value_to_user(void __user *userdata, struct snd_ctl_elem_value32 __user *data32 = userdata; int i, size; - if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN || - type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + type == SNDRV_CTL_ELEM_TYPE_INTEGER) { for (i = 0; i < count; i++) { s32 __user *intp = valuep; int val; @@ -269,7 +269,7 @@ static int copy_ctl_value_to_user(void __user *userdata, return -EFAULT; } } else { - size = get_elem_size((__force snd_ctl_elem_type_t)type, count); + size = get_elem_size(type, count); if (copy_to_user(valuep, data->value.bytes.data, size)) return -EFAULT; } @@ -417,7 +417,7 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, break; } return snd_ctl_elem_add(file, data, replace); -} +} enum { SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32), @@ -446,6 +446,7 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns switch (cmd) { case SNDRV_CTL_IOCTL_PVERSION: case SNDRV_CTL_IOCTL_CARD_INFO: + case SNDRV_CTL_IOCTL_CARD_BYTES: case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS: case SNDRV_CTL_IOCTL_POWER: case SNDRV_CTL_IOCTL_POWER_STATE: diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 973d11732bfd..c6de5345e946 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -87,7 +87,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file) if (!try_module_get(hw->card->module)) { snd_card_unref(hw->card); - return -EFAULT; + return -ENODEV; } init_waitqueue_entry(&wait, current); @@ -323,18 +323,17 @@ static int snd_hwdep_control_ioctl(struct snd_card *card, */ -static const struct file_operations snd_hwdep_f_ops = -{ - .owner = THIS_MODULE, - .llseek = snd_hwdep_llseek, - .read = snd_hwdep_read, - .write = snd_hwdep_write, - .open = snd_hwdep_open, - .release = snd_hwdep_release, - .poll = snd_hwdep_poll, - .unlocked_ioctl = snd_hwdep_ioctl, - .compat_ioctl = snd_hwdep_ioctl_compat, - .mmap = snd_hwdep_mmap, +static const struct file_operations snd_hwdep_f_ops = { + .owner = THIS_MODULE, + .llseek = snd_hwdep_llseek, + .read = snd_hwdep_read, + .write = snd_hwdep_write, + .open = snd_hwdep_open, + .release = snd_hwdep_release, + .poll = snd_hwdep_poll, + .unlocked_ioctl = snd_hwdep_ioctl, + .compat_ioctl = snd_hwdep_ioctl_compat, + .mmap = snd_hwdep_mmap, }; static void snd_hwdep_free(struct snd_hwdep *hwdep) diff --git a/sound/core/info.c b/sound/core/info.c index 54834dbe6b59..3b8ccda04e1a 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -78,7 +78,7 @@ static int alloc_info_private(struct snd_info_entry *entry, if (!entry || !entry->p) return -ENODEV; if (!try_module_get(entry->module)) - return -EFAULT; + return -ENODEV; data = kzalloc_obj(*data); if (!data) { module_put(entry->module); diff --git a/sound/core/init.c b/sound/core/init.c index 56dde5bd73c4..2f7f83a7611b 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -466,20 +466,19 @@ static int snd_disconnect_fasync(int fd, struct file *file, int on) return -ENODEV; } -static const struct file_operations snd_shutdown_f_ops = -{ - .owner = THIS_MODULE, - .llseek = snd_disconnect_llseek, - .read = snd_disconnect_read, - .write = snd_disconnect_write, - .release = snd_disconnect_release, - .poll = snd_disconnect_poll, - .unlocked_ioctl = snd_disconnect_ioctl, +static const struct file_operations snd_shutdown_f_ops = { + .owner = THIS_MODULE, + .llseek = snd_disconnect_llseek, + .read = snd_disconnect_read, + .write = snd_disconnect_write, + .release = snd_disconnect_release, + .poll = snd_disconnect_poll, + .unlocked_ioctl = snd_disconnect_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = snd_disconnect_ioctl, + .compat_ioctl = snd_disconnect_ioctl, #endif - .mmap = snd_disconnect_mmap, - .fasync = snd_disconnect_fasync + .mmap = snd_disconnect_mmap, + .fasync = snd_disconnect_fasync }; /** @@ -584,12 +583,17 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); static int snd_card_do_free(struct snd_card *card) { + bool managed = card->managed; + card->releasing = true; #if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); #endif snd_device_free_all(card); + kfree(card->components); + card->components = NULL; + card->components_alloc_size = 0; if (card->private_free) card->private_free(card); #ifdef CONFIG_SND_CTL_DEBUG @@ -601,7 +605,7 @@ static int snd_card_do_free(struct snd_card *card) } if (card->release_completion) complete(card->release_completion); - if (!card->managed) + if (!managed) kfree(card); return 0; } @@ -722,7 +726,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, int len, loops; bool is_default = false; char *id; - + copy_valid_id_string(card, src, nid); id = card->id; @@ -1031,21 +1035,44 @@ int __init snd_card_info_init(void) * * Return: Zero otherwise a negative error code. */ - + int snd_component_add(struct snd_card *card, const char *component) { char *ptr; int len = strlen(component); + unsigned int cur_len, need_len; - ptr = strstr(card->components, component); - if (ptr != NULL) { - if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ - return 1; + guard(rwsem_write)(&snd_ioctl_rwsem); + + if (card->components) { + ptr = strstr(card->components, component); + if (ptr) { + if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ + return 1; + } + cur_len = strlen(card->components) + 1; + } else { + cur_len = 0; } - if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { + + need_len = cur_len + len + 1; + if (need_len > 512) { snd_BUG(); return -ENOMEM; } + + if (need_len > card->components_alloc_size) { + unsigned int new_alloc = roundup(need_len, 32); + + ptr = krealloc(card->components, new_alloc, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + if (!card->components) + ptr[0] = '\0'; + card->components = ptr; + card->components_alloc_size = new_alloc; + } + if (card->components[0] != '\0') strcat(card->components, " "); strcat(card->components, component); diff --git a/sound/core/jack.c b/sound/core/jack.c index 96e0733ede77..0f04018181ac 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -303,41 +303,41 @@ static ssize_t jack_type_read(struct file *file, } static const struct file_operations jack_type_fops = { - .open = simple_open, - .read = jack_type_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_type_read, + .llseek = default_llseek, }; #endif static const struct file_operations sw_inject_enable_fops = { - .open = simple_open, - .read = sw_inject_enable_read, - .write = sw_inject_enable_write, - .llseek = default_llseek, + .open = simple_open, + .read = sw_inject_enable_read, + .write = sw_inject_enable_write, + .llseek = default_llseek, }; static const struct file_operations jackin_inject_fops = { - .open = simple_open, - .write = jackin_inject_write, - .llseek = default_llseek, + .open = simple_open, + .write = jackin_inject_write, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_id_fops = { - .open = simple_open, - .read = jack_kctl_id_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_id_read, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_mask_bits_fops = { - .open = simple_open, - .read = jack_kctl_mask_bits_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_mask_bits_read, + .llseek = default_llseek, }; static const struct file_operations jack_kctl_status_fops = { - .open = simple_open, - .read = jack_kctl_status_read, - .llseek = default_llseek, + .open = simple_open, + .read = jack_kctl_status_read, + .llseek = default_llseek, }; static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 591cff800329..ff9d7fd60a7e 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -30,9 +30,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) struct snd_mixer_oss_file *fmixer; int err; - err = nonseekable_open(inode, file); - if (err < 0) - return err; + nonseekable_open(inode, file); card = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_MIXER); @@ -60,7 +58,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file) kfree(fmixer); snd_card_file_remove(card, file); snd_card_unref(card); - return -EFAULT; + return -ENODEV; } snd_card_unref(card); return 0; @@ -407,13 +405,12 @@ static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd, * REGISTRATION PART */ -static const struct file_operations snd_mixer_oss_f_ops = -{ - .owner = THIS_MODULE, - .open = snd_mixer_oss_open, - .release = snd_mixer_oss_release, - .unlocked_ioctl = snd_mixer_oss_ioctl, - .compat_ioctl = snd_mixer_oss_ioctl_compat, +static const struct file_operations snd_mixer_oss_f_ops = { + .owner = THIS_MODULE, + .open = snd_mixer_oss_open, + .release = snd_mixer_oss_release, + .unlocked_ioctl = snd_mixer_oss_ioctl, + .compat_ioctl = snd_mixer_oss_ioctl_compat, }; /* diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 9826d9a0be6d..bd19328dca9e 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -879,11 +879,11 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { @@ -909,7 +909,7 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, sformat_mask); - if ((__force int)sformat < 0 || + if (sformat < 0 || !snd_mask_test_format(sformat_mask, sformat)) { pcm_for_each_format(sformat) { if (snd_mask_test_format(sformat_mask, sformat) && @@ -921,7 +921,7 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) goto failure; } format_found: - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); if (err < 0) goto failure; @@ -930,9 +930,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); + snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -1875,7 +1875,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); for (fmt = 0; fmt < 32; ++fmt) { if (snd_mask_test(format_mask, fmt)) { - int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt); + int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f; } @@ -2495,9 +2495,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) int nonblock; wait_queue_entry_t wait; - err = nonseekable_open(inode, file); - if (err < 0) - return err; + nonseekable_open(inode, file); pcm = snd_lookup_oss_minor_data(iminor(inode), SNDRV_OSS_DEVICE_TYPE_PCM); @@ -2509,7 +2507,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file) if (err < 0) goto __error1; if (!try_module_get(pcm->card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } if (snd_task_name(current, task_name, sizeof(task_name)) < 0) { @@ -3129,17 +3127,16 @@ static inline void snd_pcm_oss_proc_done(struct snd_pcm *pcm) * ENTRY functions */ -static const struct file_operations snd_pcm_oss_f_reg = -{ - .owner = THIS_MODULE, - .read = snd_pcm_oss_read, - .write = snd_pcm_oss_write, - .open = snd_pcm_oss_open, - .release = snd_pcm_oss_release, - .poll = snd_pcm_oss_poll, - .unlocked_ioctl = snd_pcm_oss_ioctl, - .compat_ioctl = snd_pcm_oss_ioctl_compat, - .mmap = snd_pcm_oss_mmap, +static const struct file_operations snd_pcm_oss_f_reg = { + .owner = THIS_MODULE, + .read = snd_pcm_oss_read, + .write = snd_pcm_oss_write, + .open = snd_pcm_oss_open, + .release = snd_pcm_oss_release, + .poll = snd_pcm_oss_poll, + .unlocked_ioctl = snd_pcm_oss_ioctl, + .compat_ioctl = snd_pcm_oss_ioctl_compat, + .mmap = snd_pcm_oss_mmap, }; static void register_oss_dsp(struct snd_pcm *pcm, int index) diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 5f4d6945a7df..acf5ca663ba8 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -272,13 +272,13 @@ static int snd_pcm_plug_formats(const struct snd_mask *mask, SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & lower_32_bits(linfmts)) formats.bits[0] |= lower_32_bits(linfmts); if (formats.bits[1] & upper_32_bits(linfmts)) formats.bits[1] |= upper_32_bits(linfmts); - return snd_mask_test(&formats, (__force int)format); + return snd_mask_test(&formats, format); } static const snd_pcm_format_t preferred_formats[] = { @@ -307,20 +307,20 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, { int i; - if (snd_mask_test(format_mask, (__force int)format)) + if (snd_mask_test(format_mask, format)) return format; if (!snd_pcm_plug_formats(format_mask, format)) - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; + snd_pcm_format_t best_format = -1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, (__force int)f)) + if (!snd_mask_test(format_mask, f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -334,21 +334,21 @@ snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, best = badness; } } - if ((__force int)best_format >= 0) + if (best_format >= 0) return best_format; else - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { snd_pcm_format_t format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, (__force int)format1)) + if (snd_mask_test(format_mask, format1)) return format1; } fallthrough; default: - return (__force snd_pcm_format_t)-EINVAL; + return -EINVAL; } } } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index bfedf571e021..41c2cab7a52c 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -211,11 +211,9 @@ static const char * const snd_pcm_format_names[] = { */ const char *snd_pcm_format_name(snd_pcm_format_t format) { - unsigned int format_num = (__force unsigned int)format; - - if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num]) + if (format >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format]) return "Unknown"; - return snd_pcm_format_names[format_num]; + return snd_pcm_format_names[format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -275,12 +273,12 @@ static const char *snd_pcm_stream_name(int stream) static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[(__force int)access]; + return snd_pcm_access_names[access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[(__force int)subformat]; + return snd_pcm_subformat_names[subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) @@ -290,7 +288,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode) static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[(__force int)state]; + return snd_pcm_state_names[state]; } #if IS_ENABLED(CONFIG_SND_PCM_OSS) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 180b6b64a448..13de3b02aa34 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -24,15 +24,12 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ -#define INT __force int - static bool valid_format(snd_pcm_format_t format) { - return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST; + return format >= 0 && format <= SNDRV_PCM_FORMAT_LAST; } -static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { +static const struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -251,7 +248,7 @@ int snd_pcm_format_signed(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].signd; + val = pcm_formats[format].signd; if (val < 0) return -EINVAL; return val; @@ -300,7 +297,7 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].le; + val = pcm_formats[format].le; if (val < 0) return -EINVAL; return val; @@ -337,7 +334,7 @@ int snd_pcm_format_width(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].width; + val = pcm_formats[format].width; if (!val) return -EINVAL; return val; @@ -356,7 +353,7 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) int val; if (!valid_format(format)) return -EINVAL; - val = pcm_formats[(INT)format].phys; + val = pcm_formats[format].phys; if (!val) return -EINVAL; return val; @@ -390,9 +387,9 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { if (!valid_format(format)) return NULL; - if (! pcm_formats[(INT)format].phys) + if (! pcm_formats[format].phys) return NULL; - return pcm_formats[(INT)format].silence; + return pcm_formats[format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -416,12 +413,12 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[(INT)format].phys; /* physical width */ + width = pcm_formats[format].phys; /* physical width */ if (!width) return -EINVAL; - pat = pcm_formats[(INT)format].silence; + pat = pcm_formats[format].silence; /* signed or 1 byte data */ - if (pcm_formats[(INT)format].signd == 1 || width <= 8) { + if (pcm_formats[format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index c9d0c2bb55b2..4a5057e7629d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -257,7 +257,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, } /* macro for simplified cast */ -#define PARAM_MASK_BIT(b) (1U << (__force int)(b)) +#define PARAM_MASK_BIT(b) (1U << (b)) static bool hw_support_mmap(struct snd_pcm_substream *substream) { @@ -489,7 +489,7 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, params->msbits = snd_interval_value(i); m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); if (snd_mask_single(m)) { - snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m); + snd_pcm_format_t format = snd_mask_min(m); params->msbits = snd_pcm_format_width(format); } } @@ -497,13 +497,13 @@ static int fixup_unreferenced_params(struct snd_pcm_substream *substream, if (params->msbits) { m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); if (snd_mask_single(m)) { - snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m); + snd_pcm_format_t format = snd_mask_min(m); if (snd_pcm_format_linear(format) && snd_pcm_format_width(format) != params->msbits) { m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT); snd_mask_reset(m_rw, - (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + SNDRV_PCM_SUBFORMAT_MSBITS_MAX); if (snd_mask_empty(m_rw)) return -EINVAL; } @@ -1252,7 +1252,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) runtime->trigger_master = NULL; } -#define ACTION_ARG_IGNORE (__force snd_pcm_state_t)0 +#define ACTION_ARG_IGNORE 0 struct action_ops { int (*pre_action)(struct snd_pcm_substream *substream, @@ -1635,7 +1635,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun); /* * pause callbacks: pass boolean (to start pause or resume) as state argument */ -#define pause_pushed(state) (__force bool)(state) +#define pause_pushed(state) (bool)(state) static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, snd_pcm_state_t state) @@ -1707,14 +1707,14 @@ static const struct action_ops snd_pcm_action_pause = { static int snd_pcm_pause(struct snd_pcm_substream *substream, bool push) { return snd_pcm_action(&snd_pcm_action_pause, substream, - (__force snd_pcm_state_t)push); + (snd_pcm_state_t)push); } static int snd_pcm_pause_lock_irq(struct snd_pcm_substream *substream, bool push) { return snd_pcm_action_lock_irq(&snd_pcm_action_pause, substream, - (__force snd_pcm_state_t)push); + (snd_pcm_state_t)push); } #ifdef CONFIG_PM @@ -1982,7 +1982,7 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, snd_pcm_state_t state) { snd_pcm_state_t cur_state = snd_pcm_get_state(substream); - int f_flags = (__force int)state; + int f_flags = state; if (cur_state == SNDRV_PCM_STATE_OPEN || cur_state == SNDRV_PCM_STATE_DISCONNECTED) @@ -2050,7 +2050,7 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, - (__force snd_pcm_state_t)f_flags); + (snd_pcm_state_t)f_flags); } /* @@ -2461,7 +2461,7 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, if (bits <= 0) continue; /* ignore invalid formats */ if ((unsigned)bits < i->min || (unsigned)bits > i->max) - snd_mask_reset(&m, (__force unsigned)k); + snd_mask_reset(&m, k); } return snd_mask_refine(mask, &m); } @@ -2543,16 +2543,16 @@ static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params, snd_mask_none(&m); /* All PCMs support at least the default STD subformat. */ - snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD); + snd_mask_set(&m, SNDRV_PCM_SUBFORMAT_STD); pcm_for_each_format(f) { - if (!snd_mask_test(fmask, (__force unsigned)f)) + if (!snd_mask_test(fmask, f)) continue; if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats) m.bits[0] |= *subformats; else if (snd_pcm_format_linear(f)) - snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX); + snd_mask_set(&m, SNDRV_PCM_SUBFORMAT_MSBITS_MAX); } return snd_mask_refine(sfmask, &m); @@ -2880,9 +2880,10 @@ static int snd_pcm_open_file(struct file *file, static int snd_pcm_playback_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - int err = nonseekable_open(inode, file); - if (err < 0) - return err; + int err; + + nonseekable_open(inode, file); + pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK); err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); @@ -2894,9 +2895,10 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file) static int snd_pcm_capture_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - int err = nonseekable_open(inode, file); - if (err < 0) - return err; + int err; + + nonseekable_open(inode, file); + pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_CAPTURE); err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); @@ -2918,7 +2920,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) if (err < 0) goto __error1; if (!try_module_get(pcm->card->module)) { - err = -EFAULT; + err = -ENODEV; goto __error2; } init_waitqueue_entry(&wait, current); @@ -3914,6 +3916,8 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf) if (substream == NULL) return VM_FAULT_SIGBUS; runtime = substream->runtime; + if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED) + return VM_FAULT_SIGBUS; offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) @@ -4239,29 +4243,29 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file, const struct file_operations snd_pcm_f_ops[2] = { { - .owner = THIS_MODULE, - .write = snd_pcm_write, - .write_iter = snd_pcm_writev, - .open = snd_pcm_playback_open, - .release = snd_pcm_release, - .poll = snd_pcm_poll, - .unlocked_ioctl = snd_pcm_ioctl, - .compat_ioctl = snd_pcm_ioctl_compat, - .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, - .get_unmapped_area = snd_pcm_get_unmapped_area, + .owner = THIS_MODULE, + .write = snd_pcm_write, + .write_iter = snd_pcm_writev, + .open = snd_pcm_playback_open, + .release = snd_pcm_release, + .poll = snd_pcm_poll, + .unlocked_ioctl = snd_pcm_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, }, { - .owner = THIS_MODULE, - .read = snd_pcm_read, - .read_iter = snd_pcm_readv, - .open = snd_pcm_capture_open, - .release = snd_pcm_release, - .poll = snd_pcm_poll, - .unlocked_ioctl = snd_pcm_ioctl, - .compat_ioctl = snd_pcm_ioctl_compat, - .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, - .get_unmapped_area = snd_pcm_get_unmapped_area, + .owner = THIS_MODULE, + .read = snd_pcm_read, + .read_iter = snd_pcm_readv, + .open = snd_pcm_capture_open, + .release = snd_pcm_release, + .poll = snd_pcm_poll, + .unlocked_ioctl = snd_pcm_ioctl, + .compat_ioctl = snd_pcm_ioctl_compat, + .mmap = snd_pcm_mmap, + .fasync = snd_pcm_fasync, + .get_unmapped_area = snd_pcm_get_unmapped_area, } }; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 4dfd9d53e6d3..bf504e27f73e 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -408,9 +408,10 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode, return 0; } -/* called from sound/core/seq/seq_midi.c */ -int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, - int mode, struct snd_rawmidi_file *rfile) +/* called from sound/core/seq/seq_midi.c and sound/core/ump.c */ +int snd_rawmidi_kernel_open_nested(struct snd_rawmidi *rmidi, int subdevice, + int mode, struct snd_rawmidi_file *rfile, + int depth) { int err; @@ -419,13 +420,14 @@ int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice, if (!try_module_get(rmidi->card->module)) return -ENXIO; - guard(mutex)(&rmidi->open_mutex); + mutex_lock_nested(&rmidi->open_mutex, depth); err = rawmidi_open_priv(rmidi, subdevice, mode, rfile); if (err < 0) module_put(rmidi->card->module); + mutex_unlock(&rmidi->open_mutex); return err; } -EXPORT_SYMBOL(snd_rawmidi_kernel_open); +EXPORT_SYMBOL(snd_rawmidi_kernel_open_nested); static int snd_rawmidi_open(struct inode *inode, struct file *file) { @@ -441,9 +443,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) return -EINVAL; /* invalid combination */ - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); if (maj == snd_major) { rmidi = snd_lookup_minor_data(iminor(inode), @@ -1784,14 +1784,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, */ static const struct file_operations snd_rawmidi_f_ops = { - .owner = THIS_MODULE, - .read = snd_rawmidi_read, - .write = snd_rawmidi_write, - .open = snd_rawmidi_open, - .release = snd_rawmidi_release, - .poll = snd_rawmidi_poll, - .unlocked_ioctl = snd_rawmidi_ioctl, - .compat_ioctl = snd_rawmidi_ioctl_compat, + .owner = THIS_MODULE, + .read = snd_rawmidi_read, + .write = snd_rawmidi_write, + .open = snd_rawmidi_open, + .release = snd_rawmidi_release, + .poll = snd_rawmidi_poll, + .unlocked_ioctl = snd_rawmidi_ioctl, + .compat_ioctl = snd_rawmidi_ioctl_compat, }; static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 021cd70f90db..2835576040ed 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -206,17 +206,16 @@ odev_poll(struct file *file, poll_table * wait) * registration of sequencer minor device */ -static const struct file_operations seq_oss_f_ops = -{ - .owner = THIS_MODULE, - .read = odev_read, - .write = odev_write, - .open = odev_open, - .release = odev_release, - .poll = odev_poll, - .unlocked_ioctl = odev_ioctl, - .compat_ioctl = odev_ioctl_compat, - .llseek = noop_llseek, +static const struct file_operations seq_oss_f_ops = { + .owner = THIS_MODULE, + .read = odev_read, + .write = odev_write, + .open = odev_open, + .release = odev_release, + .poll = odev_poll, + .unlocked_ioctl = odev_ioctl, + .compat_ioctl = odev_ioctl_compat, + .llseek = noop_llseek, }; static int __init diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 28782e1776fa..5b86e75c2658 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -59,7 +59,7 @@ static DEFINE_MUTEX(register_mutex); * client table */ static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; -static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static struct snd_seq_client __rcu *clienttab[SNDRV_SEQ_MAX_CLIENTS]; static struct snd_seq_usage client_usage; /* @@ -95,15 +95,23 @@ static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client) return snd_seq_total_cells(client->pool) > 0; } -/* return pointer to client structure for specified id */ -static struct snd_seq_client *clientptr(int clientid) +/* return pointer to client structure for specified id; call under RCU read-lock */ +static struct snd_seq_client *__clientptr(int clientid) { if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) { pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n", clientid); return NULL; } - return clienttab[clientid]; + return rcu_dereference_check(clienttab[clientid], + lockdep_is_held(&clients_lock)); +} + +/* return pointer to client structure for specified id */ +static struct snd_seq_client *clientptr(int clientid) +{ + guard(rcu)(); + return __clientptr(clientid); } static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) @@ -115,8 +123,8 @@ static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) clientid); return NULL; } - scoped_guard(spinlock_irqsave, &clients_lock) { - client = clientptr(clientid); + scoped_guard(rcu) { + client = __clientptr(clientid); if (client) return snd_seq_client_ref(client); if (clienttablock[clientid]) @@ -150,8 +158,8 @@ static struct snd_seq_client *client_use_ptr(int clientid, bool load_module) snd_seq_device_load_drivers(); } } - scoped_guard(spinlock_irqsave, &clients_lock) { - client = clientptr(clientid); + scoped_guard(rcu) { + client = __clientptr(clientid); if (client) return snd_seq_client_ref(client); } @@ -212,7 +220,6 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) } client->type = NO_CLIENT; snd_use_lock_init(&client->use_lock); - rwlock_init(&client->ports_lock); mutex_init(&client->ports_mutex); INIT_LIST_HEAD(&client->ports_list_head); mutex_init(&client->ioctl_mutex); @@ -224,14 +231,17 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize) for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN; c < SNDRV_SEQ_MAX_CLIENTS; c++) { - if (clienttab[c] || clienttablock[c]) + if (rcu_access_pointer(clienttab[c]) || clienttablock[c]) continue; - clienttab[client->number = c] = client; + client->number = c; + rcu_assign_pointer(clienttab[c], client); return client; } } else { - if (clienttab[client_index] == NULL && !clienttablock[client_index]) { - clienttab[client->number = client_index] = client; + if (rcu_access_pointer(clienttab[client_index]) == NULL && + !clienttablock[client_index]) { + client->number = client_index; + rcu_assign_pointer(clienttab[client_index], client); return client; } } @@ -249,10 +259,16 @@ static int seq_free_client1(struct snd_seq_client *client) return 0; scoped_guard(spinlock_irq, &clients_lock) { clienttablock[client->number] = 1; - clienttab[client->number] = NULL; + rcu_assign_pointer(clienttab[client->number], NULL); } snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); + /* the client has been unpublished from the table; wait for a grace + * period so that lockless readers (snd_seq_client_use_ptr()) that + * observed the old pointer can no longer take a new use_lock + * reference, then drain the outstanding references before freeing + */ + synchronize_rcu(); snd_use_lock_sync(&client->use_lock); if (client->pool) snd_seq_pool_delete(&client->pool); @@ -296,11 +312,8 @@ static int snd_seq_open(struct inode *inode, struct file *file) int c, mode; /* client id */ struct snd_seq_client *client; struct snd_seq_user_client *user; - int err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); scoped_guard(mutex, ®ister_mutex) { client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS); @@ -517,9 +530,8 @@ static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event) * Return the error event. * * If the receiver client is a user client, the original event is - * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. If - * the original event is also variable length, the external data is - * copied after the event record. + * encapsulated in SNDRV_SEQ_EVENT_BOUNCE as variable length event. The + * external data of a variable length event is not copied along. * If the receiver client is a kernel client, the original event is * quoted in SNDRV_SEQ_EVENT_KERNEL_ERROR, since this requires no extra * kmalloc. @@ -528,7 +540,7 @@ static int bounce_error_event(struct snd_seq_client *client, struct snd_seq_event *event, int err, int atomic, int hop) { - struct snd_seq_event bounce_ev; + struct snd_seq_event bounce_ev, quoted; int result; if (client == NULL || @@ -548,15 +560,19 @@ static int bounce_error_event(struct snd_seq_client *client, * For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the * original event embedded as variable-length data. This * avoids exposing data.quote.event (a kernel pointer) to - * userspace. The variable-length path in snd_seq_event_dup() - * copies the event data from data.ext.ptr into chained cells, - * and snd_seq_expand_var_event() copies only the data content - * -- never the pointer -- to userspace. + * userspace. Sanitise the embedded copy too - a queued + * variable-length event carries the address of its own + * extension cell, and the payload goes out verbatim. */ + quoted = *event; + if (snd_seq_ev_is_variable("ed)) { + quoted.data.ext.len &= ~SNDRV_SEQ_EXT_MASK; + quoted.data.ext.ptr = NULL; + } bounce_ev.type = SNDRV_SEQ_EVENT_BOUNCE; bounce_ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE; bounce_ev.data.ext.len = sizeof(struct snd_seq_event); - bounce_ev.data.ext.ptr = (char *)event; + bounce_ev.data.ext.ptr = (char *)"ed; } else { /* * For kernel clients, quote the event pointer directly. @@ -720,10 +736,11 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, /* lock list */ if (atomic) - read_lock(&grp->list_lock); + rcu_read_lock(); else down_read_nested(&grp->list_mutex, hop); - list_for_each_entry(subs, &grp->list_head, src_list) { + hlist_for_each_entry_rcu(subs, &grp->list_head, src_list, + lockdep_is_held(&grp->list_mutex)) { /* both ports ready? */ if (atomic_read(&subs->ref_count) != 2) continue; @@ -744,7 +761,7 @@ static int __deliver_to_subscribers(struct snd_seq_client *client, memcpy(event, &event_saved, saved_size); } if (atomic) - read_unlock(&grp->list_lock); + rcu_read_unlock(); else up_read(&grp->list_mutex); memcpy(event, &event_saved, saved_size); @@ -1941,7 +1958,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) { struct snd_seq_query_subs *subs = arg; struct snd_seq_port_subs_info *group; - struct list_head *p; + struct hlist_node *p; int i; struct snd_seq_client *cptr __free(snd_seq_client) = @@ -1968,15 +1985,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg) /* search for the subscriber */ subs->num_subs = group->count; i = 0; - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (i++ == subs->index) { /* found! */ struct snd_seq_subscribers *s; if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) { - s = list_entry(p, struct snd_seq_subscribers, src_list); + s = hlist_entry(p, struct snd_seq_subscribers, src_list); subs->addr = s->info.dest; } else { - s = list_entry(p, struct snd_seq_subscribers, dest_list); + s = hlist_entry(p, struct snd_seq_subscribers, dest_list); subs->addr = s->info.sender; } subs->flags = s->info.flags; @@ -2532,19 +2549,19 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg) { - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int count = 0; guard(rwsem_read)(&group->list_mutex); - if (list_empty(&group->list_head)) + if (hlist_empty(&group->list_head)) return; snd_iprintf(buffer, msg); - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (is_src) - s = list_entry(p, struct snd_seq_subscribers, src_list); + s = hlist_entry(p, struct snd_seq_subscribers, src_list); else - s = list_entry(p, struct snd_seq_subscribers, dest_list); + s = hlist_entry(p, struct snd_seq_subscribers, dest_list); if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", @@ -2666,16 +2683,15 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry, * REGISTRATION PART */ -static const struct file_operations snd_seq_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_seq_read, - .write = snd_seq_write, - .open = snd_seq_open, - .release = snd_seq_release, - .poll = snd_seq_poll, - .unlocked_ioctl = snd_seq_ioctl, - .compat_ioctl = snd_seq_ioctl_compat, +static const struct file_operations snd_seq_f_ops = { + .owner = THIS_MODULE, + .read = snd_seq_read, + .write = snd_seq_write, + .open = snd_seq_open, + .release = snd_seq_release, + .poll = snd_seq_poll, + .unlocked_ioctl = snd_seq_ioctl, + .compat_ioctl = snd_seq_ioctl_compat, }; static struct device *seq_dev; diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index feea8bb7d987..d7ffc5c1ed61 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -49,7 +49,6 @@ struct snd_seq_client { /* ports */ int num_ports; /* number of ports */ struct list_head ports_list_head; - rwlock_t ports_lock; struct mutex ports_mutex; struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 2eb12199c92f..a16a5debf339 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -42,9 +42,11 @@ struct seq_midisynth { struct snd_rawmidi *rmidi; int device; int subdevice; + struct snd_rawmidi_substream __rcu *input_substream; + snd_use_lock_t input_use_lock; /* in-flight event_input users */ struct snd_rawmidi_file input_rfile; - spinlock_t output_lock; /* protects output_rfile publication */ snd_use_lock_t output_use_lock; /* in-flight event_input users */ + struct snd_rawmidi_substream __rcu *output_substream; struct snd_rawmidi_file output_rfile; int seq_client; int seq_port; @@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) msynth = runtime->private_data; if (msynth == NULL) return; + + scoped_guard(rcu) { + if (rcu_dereference(msynth->input_substream) != substream) + return; + + snd_use_lock_use(&msynth->input_use_lock); + } + memset(&ev, 0, sizeof(ev)); while (runtime->avail > 0) { res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf)); @@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) memset(&ev, 0, sizeof(ev)); } } + + snd_use_lock_free(&msynth->input_use_lock); } static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count) @@ -134,8 +146,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct, if (snd_BUG_ON(!msynth)) return -EINVAL; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - substream = msynth->output_rfile.output; + scoped_guard(rcu) { + substream = rcu_dereference(msynth->output_substream); if (!substream) return -ENODEV; snd_use_lock_use(&msynth->output_use_lock); @@ -177,7 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; - spin_lock_init(&msynth->output_lock); + snd_use_lock_init(&msynth->input_use_lock); snd_use_lock_init(&msynth->output_use_lock); return 0; } @@ -188,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe int err; struct seq_midisynth *msynth = private_data; struct snd_rawmidi_runtime *runtime; + struct snd_rawmidi_file rfile = {}; struct snd_rawmidi_params params; /* open midi port */ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice, SNDRV_RAWMIDI_LFLG_INPUT, - &msynth->input_rfile); + &rfile); if (err < 0) { pr_debug("ALSA: seq_midi: midi input open failed!!!\n"); return err; } - runtime = msynth->input_rfile.input->runtime; + runtime = rfile.input->runtime; memset(¶ms, 0, sizeof(params)); params.avail_min = 1; params.buffer_size = input_buffer_size; - err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms); + err = snd_rawmidi_input_params(rfile.input, ¶ms); if (err < 0) { - snd_rawmidi_kernel_release(&msynth->input_rfile); + snd_rawmidi_kernel_release(&rfile); return err; } snd_midi_event_reset_encode(msynth->parser); runtime->event = snd_midi_input_event; runtime->private_data = msynth; + msynth->input_rfile = rfile; + rcu_assign_pointer(msynth->input_substream, rfile.input); snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0); return 0; } @@ -219,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri { int err; struct seq_midisynth *msynth = private_data; + struct snd_rawmidi_file rfile; - if (snd_BUG_ON(!msynth->input_rfile.input)) + rcu_assign_pointer(msynth->input_substream, NULL); + synchronize_rcu(); + snd_use_lock_sync(&msynth->input_use_lock); + + rfile = msynth->input_rfile; + msynth->input_rfile = (struct snd_rawmidi_file){}; + + if (snd_BUG_ON(!rfile.input)) return -EINVAL; - err = snd_rawmidi_kernel_release(&msynth->input_rfile); + + err = snd_rawmidi_kernel_release(&rfile); return err; } @@ -252,8 +276,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info return err; } snd_midi_event_reset_decode(msynth->parser); - scoped_guard(spinlock_irqsave, &msynth->output_lock) - msynth->output_rfile = rfile; + msynth->output_rfile = rfile; + rcu_assign_pointer(msynth->output_substream, rfile.output); return 0; } @@ -261,17 +285,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info) { struct seq_midisynth *msynth = private_data; - struct snd_rawmidi_file rfile = {}; + struct snd_rawmidi_file rfile; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - rfile = msynth->output_rfile; - msynth->output_rfile = (struct snd_rawmidi_file){}; - } + rcu_assign_pointer(msynth->output_substream, NULL); + synchronize_rcu(); + snd_use_lock_sync(&msynth->output_use_lock); + rfile = msynth->output_rfile; + msynth->output_rfile = (struct snd_rawmidi_file){}; if (snd_BUG_ON(!rfile.output)) return -EINVAL; - - snd_use_lock_sync(&msynth->output_use_lock); snd_rawmidi_drain_output(rfile.output); return snd_rawmidi_kernel_release(&rfile); } diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 6612e92d801f..eb67eb0eeb14 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -48,8 +48,8 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client, if (client == NULL) return NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if (port->addr.port == num) { if (port->closing) break; /* deleting now */ @@ -71,8 +71,8 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl num = pinfo->addr.port; found = NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) && !check_inactive) continue; /* skip inactive ports */ @@ -98,10 +98,9 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl /* initialize snd_seq_port_subs_info */ static void port_subs_info_init(struct snd_seq_port_subs_info *grp) { - INIT_LIST_HEAD(&grp->list_head); + INIT_HLIST_HEAD(&grp->list_head); grp->count = 0; grp->exclusive = 0; - rwlock_init(&grp->list_lock); init_rwsem(&grp->list_mutex); grp->open = NULL; grp->close = NULL; @@ -154,7 +153,6 @@ int snd_seq_insert_port(struct snd_seq_client *client, int port, num = max(port, 0); guard(mutex)(&client->ports_mutex); - guard(write_lock_irq)(&client->ports_lock); struct list_head *insert_before = &client->ports_list_head; list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) @@ -166,12 +164,13 @@ int snd_seq_insert_port(struct snd_seq_client *client, int port, if (port < 0) /* auto-probe mode */ num = p->addr.port + 1; } - /* insert the new port */ - list_add_tail(&new_port->list, insert_before); - client->num_ports++; + /* finish initializing the port before publishing it to RCU readers */ new_port->addr.port = num; /* store the port number in the port */ if (!new_port->name[0]) sprintf(new_port->name, "port-%d", num); + /* insert the new port */ + list_add_tail_rcu(&new_port->list, insert_before); + client->num_ports++; return num; } @@ -202,12 +201,12 @@ static void delete_and_unsubscribe_port(struct snd_seq_client *client, bool is_src, bool ack); static inline struct snd_seq_subscribers * -get_subscriber(struct list_head *p, bool is_src) +get_subscriber(struct hlist_node *p, bool is_src) { if (is_src) - return list_entry(p, struct snd_seq_subscribers, src_list); + return hlist_entry(p, struct snd_seq_subscribers, src_list); else - return list_entry(p, struct snd_seq_subscribers, dest_list); + return hlist_entry(p, struct snd_seq_subscribers, dest_list); } /* @@ -219,9 +218,9 @@ static void clear_subscriber_list(struct snd_seq_client *client, struct snd_seq_port_subs_info *grp, int is_src) { - struct list_head *p, *n; + struct hlist_node *p, *n; - list_for_each_safe(p, n, &grp->list_head) { + hlist_for_each_safe(p, n, &grp->list_head) { struct snd_seq_subscribers *subs; subs = get_subscriber(p, is_src); @@ -238,13 +237,13 @@ static void clear_subscriber_list(struct snd_seq_client *client, * remove the subscriber info */ if (atomic_dec_and_test(&subs->ref_count)) - kfree(subs); + kfree_rcu(subs, rcu); continue; } /* ok we got the connected port */ delete_and_unsubscribe_port(c, aport, subs, !is_src, true); - kfree(subs); + kfree_rcu(subs, rcu); } } @@ -254,7 +253,13 @@ static int port_delete(struct snd_seq_client *client, { /* set closing flag and wait for all port access are gone */ port->closing = 1; - snd_use_lock_sync(&port->use_lock); + /* the port has already been unlinked from the client's port list; + * wait for a grace period so that RCU readers still traversing the + * list can no longer take a new use_lock reference, then drain the + * outstanding references before freeing + */ + synchronize_rcu(); + snd_use_lock_sync(&port->use_lock); /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, true); @@ -277,11 +282,10 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) struct snd_seq_client_port *found = NULL, *p; scoped_guard(mutex, &client->ports_mutex) { - guard(write_lock_irq)(&client->ports_lock); list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port == port) { /* ok found. delete from the list at first */ - list_del(&p->list); + list_del_rcu(&p->list); client->num_ports--; found = p; break; @@ -297,26 +301,16 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port) /* delete the all ports belonging to the given client */ int snd_seq_delete_all_ports(struct snd_seq_client *client) { - struct list_head deleted_list; struct snd_seq_client_port *port, *tmp; - - /* move the port list to deleted_list, and - * clear the port list in the client data. + + /* unlink and delete each port; port_delete() waits for an RCU grace + * period before draining the port, so concurrent lockless readers can + * no longer take a new use_lock reference on it */ guard(mutex)(&client->ports_mutex); - scoped_guard(write_lock_irq, &client->ports_lock) { - if (!list_empty(&client->ports_list_head)) { - list_add(&deleted_list, &client->ports_list_head); - list_del_init(&client->ports_list_head); - } else { - INIT_LIST_HEAD(&deleted_list); - } - client->num_ports = 0; - } - - /* remove each port in deleted_list */ - list_for_each_entry_safe(port, tmp, &deleted_list, list) { - list_del(&port->list); + list_for_each_entry_safe(port, tmp, &client->ports_list_head, list) { + list_del_rcu(&port->list); + client->num_ports--; snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } @@ -499,20 +493,20 @@ static int check_and_subscribe_port(struct snd_seq_client *client, bool is_src, bool exclusive, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int err; grp = is_src ? &port->c_src : &port->c_dest; guard(rwsem_write)(&grp->list_mutex); if (exclusive) { - if (!list_empty(&grp->list_head)) + if (!hlist_empty(&grp->list_head)) return -EBUSY; } else { if (grp->exclusive) return -EBUSY; /* check whether already exists */ - list_for_each(p, &grp->list_head) { + hlist_for_each(p, &grp->list_head) { s = get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) return -EBUSY; @@ -526,11 +520,10 @@ static int check_and_subscribe_port(struct snd_seq_client *client, } /* add to list */ - guard(write_lock_irq)(&grp->list_lock); if (is_src) - list_add_tail(&subs->src_list, &grp->list_head); + hlist_add_tail_rcu(&subs->src_list, &grp->list_head); else - list_add_tail(&subs->dest_list, &grp->list_head); + hlist_add_tail_rcu(&subs->dest_list, &grp->list_head); grp->exclusive = exclusive; atomic_inc(&subs->ref_count); @@ -544,17 +537,15 @@ static void __delete_and_unsubscribe_port(struct snd_seq_client *client, bool is_src, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *list; + struct hlist_node *list; bool empty; grp = is_src ? &port->c_src : &port->c_dest; list = is_src ? &subs->src_list : &subs->dest_list; - scoped_guard(write_lock_irq, &grp->list_lock) { - empty = list_empty(list); - if (!empty) - list_del_init(list); - grp->exclusive = 0; - } + empty = hlist_unhashed(list); + if (!empty) + hlist_del_init_rcu(list); + grp->exclusive = 0; if (!empty) unsubscribe_port(client, port, grp, &subs->info, ack); @@ -590,8 +581,8 @@ int snd_seq_port_connect(struct snd_seq_client *connector, subs->info = *info; atomic_set(&subs->ref_count, 0); - INIT_LIST_HEAD(&subs->src_list); - INIT_LIST_HEAD(&subs->dest_list); + INIT_HLIST_NODE(&subs->src_list); + INIT_HLIST_NODE(&subs->dest_list); exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); @@ -612,7 +603,7 @@ int snd_seq_port_connect(struct snd_seq_client *connector, delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); error: - kfree(subs); + kfree_rcu(subs, rcu); return err; } @@ -633,7 +624,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, */ scoped_guard(rwsem_write, &dest->list_mutex) { /* look for the connection */ - list_for_each_entry(subs, &dest->list_head, dest_list) { + hlist_for_each_entry(subs, &dest->list_head, dest_list) { if (match_subs_info(info, &subs->info)) { __delete_and_unsubscribe_port(dest_client, dest_port, subs, false, @@ -648,7 +639,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector, delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number != src_client->number); - kfree(subs); + kfree_rcu(subs, rcu); return 0; } @@ -662,7 +653,7 @@ int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp, int err = -ENOENT; guard(rwsem_read)(&src_grp->list_mutex); - list_for_each_entry(s, &src_grp->list_head, src_list) { + hlist_for_each_entry(s, &src_grp->list_head, src_list) { if (addr_match(dest_addr, &s->info.dest)) { *subs = s->info; err = 0; diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h index b689c0f4867c..12ad86bf1489 100644 --- a/sound/core/seq/seq_ports.h +++ b/sound/core/seq/seq_ports.h @@ -28,17 +28,17 @@ struct snd_seq_subscribers { struct snd_seq_port_subscribe info; /* additional info */ - struct list_head src_list; /* link of sources */ - struct list_head dest_list; /* link of destinations */ + struct hlist_node src_list; /* link of sources */ + struct hlist_node dest_list; /* link of destinations */ atomic_t ref_count; + struct rcu_head rcu; /* for deferred free */ }; struct snd_seq_port_subs_info { - struct list_head list_head; /* list of subscribed ports */ + struct hlist_head list_head; /* list of subscribed ports */ unsigned int count; /* count of subscribers */ unsigned int exclusive: 1; /* exclusive mode */ struct rw_semaphore list_mutex; - rwlock_t list_lock; int (*open)(void *private_data, struct snd_seq_port_subscribe *info); int (*close)(void *private_data, struct snd_seq_port_subscribe *info); }; diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index ccd93599b493..4c1e81db376f 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -37,8 +37,11 @@ struct seq_ump_client { struct snd_ump_endpoint *ump; /* assigned endpoint */ int seq_client; /* sequencer client id */ int opened[2]; /* current opens for each direction */ - rwlock_t output_lock; /* protects out_rfile output access */ struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + /* RCU-protected shadow of out_rfile.output for the delivery hot path; + * out_rfile itself is only touched by open/close under open_mutex + */ + struct snd_rawmidi_substream __rcu *out_substream; struct seq_ump_input_buffer input; /* input parser context */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ struct work_struct group_notify_work; /* FB change notification */ @@ -89,8 +92,8 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, unsigned char type; int len; - guard(read_lock_irqsave)(&client->output_lock); - substream = client->out_rfile.output; + guard(rcu)(); + substream = rcu_dereference(client->out_substream); if (!substream) return -ENODEV; if (!snd_seq_ev_is_ump(ev)) @@ -108,19 +111,22 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, static int seq_ump_client_open(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - struct snd_rawmidi_file rfile = {}; int err; guard(mutex)(&ump->open_mutex); if (dir == STR_OUT && !client->opened[dir]) { + /* out_rfile is only accessed under open_mutex; the delivery + * path reads out_substream via RCU, so open into out_rfile + * directly and publish the substream afterwards + */ err = snd_rawmidi_kernel_open(&ump->core, 0, SNDRV_RAWMIDI_LFLG_OUTPUT | SNDRV_RAWMIDI_LFLG_APPEND, - &rfile); + &client->out_rfile); if (err < 0) return err; - scoped_guard(write_lock_irqsave, &client->output_lock) - client->out_rfile = rfile; + rcu_assign_pointer(client->out_substream, + client->out_rfile.output); } client->opened[dir]++; return 0; @@ -130,17 +136,18 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) static int seq_ump_client_close(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; - struct snd_rawmidi_file rfile = {}; guard(mutex)(&ump->open_mutex); if (!--client->opened[dir]) { - if (dir == STR_OUT) { - scoped_guard(write_lock_irqsave, &client->output_lock) { - rfile = client->out_rfile; - client->out_rfile = (struct snd_rawmidi_file){}; - } - if (rfile.rmidi) - snd_rawmidi_kernel_release(&rfile); + if (dir == STR_OUT && client->out_rfile.rmidi) { + rcu_assign_pointer(client->out_substream, NULL); + /* wait for a grace period so that no reader in the + * delivery path is still writing to the substream + * before it is released + */ + synchronize_rcu(); + snd_rawmidi_kernel_release(&client->out_rfile); + client->out_rfile = (struct snd_rawmidi_file){}; } } return 0; @@ -480,7 +487,6 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) INIT_WORK(&client->group_notify_work, handle_group_notify); client->ump = ump; - rwlock_init(&client->output_lock); client->seq_client = snd_seq_create_kernel_client(card, ump->core.device, diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 982828650d41..6208bf7f57bf 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -78,10 +78,11 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, int len; if (atomic) - read_lock(&rdev->filelist_lock); + rcu_read_lock(); else down_read(&rdev->filelist_sem); - list_for_each_entry(vmidi, &rdev->filelist, list) { + list_for_each_entry_rcu(vmidi, &rdev->filelist, list, + lockdep_is_held(&rdev->filelist_sem)) { if (!READ_ONCE(vmidi->trigger)) continue; if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { @@ -96,7 +97,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev, } } if (atomic) - read_unlock(&rdev->filelist_lock); + rcu_read_unlock(); else up_read(&rdev->filelist_sem); @@ -200,8 +201,7 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream) vmidi->port = rdev->port; runtime->private_data = vmidi; scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_add_tail(&vmidi->list, &rdev->filelist); + list_add_tail_rcu(&vmidi->list, &rdev->filelist); } vmidi->rdev = rdev; return 0; @@ -243,9 +243,13 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream) struct snd_virmidi *vmidi = substream->runtime->private_data; scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_del(&vmidi->list); + list_del_rcu(&vmidi->list); } + /* wait for a grace period so that lockless readers in the atomic + * delivery path (snd_virmidi_dev_receive_event()) are no longer + * traversing this entry before its parser and memory are freed + */ + synchronize_rcu(); snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; kfree(vmidi); @@ -508,7 +512,6 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi rdev->device = device; rdev->client = -1; init_rwsem(&rdev->filelist_sem); - rwlock_init(&rdev->filelist_lock); INIT_LIST_HEAD(&rdev->filelist); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; rmidi->private_data = rdev; diff --git a/sound/core/sound.c b/sound/core/sound.c index 8d05fe0d263b..24f40763a20c 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -167,11 +167,10 @@ static int snd_open(struct inode *inode, struct file *file) return err; } -static const struct file_operations snd_fops = -{ - .owner = THIS_MODULE, - .open = snd_open, - .llseek = noop_llseek, +static const struct file_operations snd_fops = { + .owner = THIS_MODULE, + .open = snd_open, + .llseek = noop_llseek, }; #ifdef CONFIG_SND_DYNAMIC_MINORS @@ -358,7 +357,7 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu struct snd_minor *mptr; guard(mutex)(&sound_mutex); - for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { + for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { mptr = snd_minors[minor]; if (!mptr) continue; @@ -395,15 +394,21 @@ int __init snd_minor_info_init(void) static int __init alsa_sound_init(void) { + int err; + snd_major = major; snd_ecards_limit = cards_limit; - if (register_chrdev(major, "alsa", &snd_fops)) { + + err = register_chrdev(major, "alsa", &snd_fops); + if (err < 0) { pr_err("ALSA core: unable to register native major device number %d\n", major); - return -EIO; + return err; } - if (snd_info_init() < 0) { + + err = snd_info_init(); + if (err < 0) { unregister_chrdev(major, "alsa"); - return -ENOMEM; + return err; } #ifdef CONFIG_SND_DEBUG diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c index 84e337ecbddd..0376112cc67e 100644 --- a/sound/core/sound_kunit.c +++ b/sound/core/sound_kunit.c @@ -17,8 +17,8 @@ .name = #fmt, \ } -#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1) -#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1 +#define WRONG_FORMAT_1 (SNDRV_PCM_FORMAT_LAST + 1) +#define WRONG_FORMAT_2 -1 #define VALID_NAME "ValidName" #define NAME_W_SPEC_CHARS "In%v@1id name" diff --git a/sound/core/timer.c b/sound/core/timer.c index 9407954034cf..f666f05e9d45 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1546,11 +1546,8 @@ static int realloc_user_queue(struct snd_timer_user *tu, int size) static int snd_timer_user_open(struct inode *inode, struct file *file) { struct snd_timer_user *tu; - int err; - err = stream_open(inode, file); - if (err < 0) - return err; + stream_open(inode, file); tu = kzalloc_obj(*tu); if (tu == NULL) @@ -2176,9 +2173,9 @@ static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned lon } static const struct file_operations snd_utimer_fops = { - .llseek = noop_llseek, - .release = snd_utimer_release, - .unlocked_ioctl = snd_utimer_ioctl, + .llseek = noop_llseek, + .release = snd_utimer_release, + .unlocked_ioctl = snd_utimer_ioctl, }; static int snd_utimer_start(struct snd_timer *t) @@ -2530,16 +2527,15 @@ static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) #define snd_timer_user_ioctl_compat NULL #endif -static const struct file_operations snd_timer_f_ops = -{ - .owner = THIS_MODULE, - .read = snd_timer_user_read, - .open = snd_timer_user_open, - .release = snd_timer_user_release, - .poll = snd_timer_user_poll, - .unlocked_ioctl = snd_timer_user_ioctl, - .compat_ioctl = snd_timer_user_ioctl_compat, - .fasync = snd_timer_user_fasync, +static const struct file_operations snd_timer_f_ops = { + .owner = THIS_MODULE, + .read = snd_timer_user_read, + .open = snd_timer_user_open, + .release = snd_timer_user_release, + .poll = snd_timer_user_poll, + .unlocked_ioctl = snd_timer_user_ioctl, + .compat_ioctl = snd_timer_user_ioctl_compat, + .fasync = snd_timer_user_fasync, }; /* unregister the system timer */ diff --git a/sound/core/ump.c b/sound/core/ump.c index 632c13baf21e..82ad155c56e6 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1157,10 +1157,11 @@ static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream) return -ENODEV; if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) { if (!ump->legacy_out_opens) { - err = snd_rawmidi_kernel_open(&ump->core, 0, - SNDRV_RAWMIDI_LFLG_OUTPUT | - SNDRV_RAWMIDI_LFLG_APPEND, - &ump->legacy_out_rfile); + err = snd_rawmidi_kernel_open_nested(&ump->core, 0, + SNDRV_RAWMIDI_LFLG_OUTPUT | + SNDRV_RAWMIDI_LFLG_APPEND, + &ump->legacy_out_rfile, + SINGLE_DEPTH_NESTING); if (err < 0) return err; } diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c index 0fe13d031656..85cc67de6330 100644 --- a/sound/core/ump_convert.c +++ b/sound/core/ump_convert.c @@ -258,6 +258,7 @@ static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt, else status = UMP_SYSEX_STATUS_CONTINUE; *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len); + data[1] = 0; offset = 8; for (i = 0; i < cvt->len; i++) { *data |= cvt->buf[i] << offset; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 06bfe09eae1a..4e3ea23ca913 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); module_param_array(pcm_notify, int, NULL, 0444); MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); module_param_array(timer_source, charp, NULL, 0444); -MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default]."); +MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default], 'hrtimer' for high-resolution timer."); #define NO_PITCH 100000 @@ -161,8 +162,11 @@ struct loopback_pcm { unsigned int period_size_frac; /* period size in jiffies ticks */ unsigned int last_drift; unsigned long last_jiffies; - /* If jiffies timer is used */ + /* If jiffies / hrtimer is used */ struct timer_list timer; +#ifdef CONFIG_HIGH_RES_TIMERS + struct hrtimer hrtimer; +#endif /* size of per channel buffer in case of non-interleaved access */ unsigned int channel_buf_n; @@ -232,6 +236,31 @@ static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +/* call in cable->lock */ +static int loopback_hrtimer_start(struct loopback_pcm *dpcm) +{ + unsigned long tick; + unsigned int rate_shift = get_rate_shift(dpcm); + + if (rate_shift != dpcm->pcm_rate_shift) { + dpcm->pcm_rate_shift = rate_shift; + dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); + } + if (dpcm->period_size_frac <= dpcm->irq_pos) { + dpcm->irq_pos %= dpcm->period_size_frac; + dpcm->period_update_pending = 1; + } + tick = dpcm->period_size_frac - dpcm->irq_pos; + tick = DIV_ROUND_UP(tick, dpcm->pcm_bps); + hrtimer_start(&dpcm->hrtimer, + ns_to_ktime(div_u64((u64)tick * NSEC_PER_SEC, HZ)), + HRTIMER_MODE_REL_SOFT); + + return 0; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_start(struct loopback_pcm *dpcm) { @@ -270,6 +299,16 @@ static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +/* call in cable->lock */ +static inline int loopback_hrtimer_stop(struct loopback_pcm *dpcm) +{ + hrtimer_try_to_cancel(&dpcm->hrtimer); + + return 0; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_stop(struct loopback_pcm *dpcm) { @@ -300,6 +339,15 @@ static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm) return 0; } +#ifdef CONFIG_HIGH_RES_TIMERS +static inline int loopback_hrtimer_stop_sync(struct loopback_pcm *dpcm) +{ + hrtimer_cancel(&dpcm->hrtimer); + + return 0; +} +#endif + /* call in loopback->cable_lock */ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) { @@ -734,6 +782,30 @@ static void loopback_jiffies_timer_function(struct timer_list *t) snd_pcm_period_elapsed(dpcm->substream); } +#ifdef CONFIG_HIGH_RES_TIMERS +static enum hrtimer_restart loopback_hrtimer_function(struct hrtimer *t) +{ + struct loopback_pcm *dpcm = container_of(t, struct loopback_pcm, hrtimer); + bool period_elapsed = false; + + scoped_guard(spinlock_irqsave, &dpcm->cable->lock) { + if (loopback_jiffies_timer_pos_update(dpcm->cable) & + (1 << dpcm->substream->stream)) { + loopback_hrtimer_start(dpcm); + if (dpcm->period_update_pending) { + dpcm->period_update_pending = 0; + period_elapsed = true; + } + } + } + + if (period_elapsed) + snd_pcm_period_elapsed(dpcm->substream); + + return HRTIMER_NORESTART; +} +#endif + /* call in cable->lock */ static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime, unsigned long resolution) @@ -907,6 +979,21 @@ static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm, snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); } +#ifdef CONFIG_HIGH_RES_TIMERS +static void loopback_hrtimer_dpcm_info(struct loopback_pcm *dpcm, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, " update_pending:\t%u\n", + dpcm->period_update_pending); + snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); + snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); + snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", + dpcm->last_jiffies, jiffies); + snd_iprintf(buffer, " timer_expires:\t%llu\n", + ktime_to_ns(hrtimer_get_expires(&dpcm->hrtimer))); +} +#endif + static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm, struct snd_info_buffer *buffer) { @@ -1097,6 +1184,26 @@ static const struct loopback_ops loopback_jiffies_timer_ops = { .dpcm_info = loopback_jiffies_timer_dpcm_info, }; +#ifdef CONFIG_HIGH_RES_TIMERS +static int loopback_hrtimer_open(struct loopback_pcm *dpcm) +{ + hrtimer_setup(&dpcm->hrtimer, loopback_hrtimer_function, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); + + return 0; +} + +static const struct loopback_ops loopback_hrtimer_ops = { + .open = loopback_hrtimer_open, + .start = loopback_hrtimer_start, + .stop = loopback_hrtimer_stop, + .stop_sync = loopback_hrtimer_stop_sync, + .close_substream = loopback_hrtimer_stop_sync, + .pos_update = loopback_jiffies_timer_pos_update, + .dpcm_info = loopback_hrtimer_dpcm_info, +}; +#endif + static int loopback_parse_timer_id(const char *str, struct snd_timer_id *tid) { @@ -1274,7 +1381,12 @@ static int loopback_open(struct snd_pcm_substream *substream) spin_lock_init(&cable->lock); snd_refcount_init(&cable->stop_count); cable->hw = loopback_pcm_hardware; - if (loopback->timer_source) +#ifdef CONFIG_HIGH_RES_TIMERS + if (loopback->timer_source && !strcmp(loopback->timer_source, "hrtimer")) + cable->ops = &loopback_hrtimer_ops; + else +#endif + if (loopback->timer_source && loopback->timer_source[0]) cable->ops = &loopback_snd_timer_ops; else cable->ops = &loopback_jiffies_timer_ops; @@ -1491,7 +1603,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST; + uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; uinfo->value.integer.step = 1; return 0; } @@ -1502,7 +1614,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol, struct loopback *loopback = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = - (__force int)loopback->setup[kcontrol->id.subdevice] + loopback->setup[kcontrol->id.subdevice] [kcontrol->id.device].format; return 0; } @@ -1556,7 +1668,7 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol, static int loopback_access_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - const char * const texts[] = {"Interleaved", "Non-interleaved"}; + static const char * const texts[] = {"Interleaved", "Non-interleaved"}; return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } @@ -1802,6 +1914,12 @@ static int loopback_probe(struct platform_device *devptr) int dev = devptr->id; int err; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct loopback), &card); if (err < 0) diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 7283f0f18813..022247354732 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -100,6 +100,7 @@ struct dummy_timer_ops { int (*prepare)(struct snd_pcm_substream *); int (*start)(struct snd_pcm_substream *); int (*stop)(struct snd_pcm_substream *); + int (*sync_stop)(struct snd_pcm_substream *); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); }; @@ -285,6 +286,14 @@ static int dummy_systimer_stop(struct snd_pcm_substream *substream) return 0; } +static int dummy_systimer_sync_stop(struct snd_pcm_substream *substream) +{ + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + + timer_delete_sync(&dpcm->timer); + return 0; +} + static int dummy_systimer_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -341,7 +350,10 @@ static int dummy_systimer_create(struct snd_pcm_substream *substream) static void dummy_systimer_free(struct snd_pcm_substream *substream) { - kfree(substream->runtime->private_data); + struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; + + timer_shutdown_sync(&dpcm->timer); + kfree(dpcm); } static const struct dummy_timer_ops dummy_systimer_ops = { @@ -350,6 +362,7 @@ static const struct dummy_timer_ops dummy_systimer_ops = { .prepare = dummy_systimer_prepare, .start = dummy_systimer_start, .stop = dummy_systimer_stop, + .sync_stop = dummy_systimer_sync_stop, .pointer = dummy_systimer_pointer, }; @@ -407,9 +420,12 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) return 0; } -static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) +static int dummy_hrtimer_sync_stop(struct snd_pcm_substream *substream) { + struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; + hrtimer_cancel(&dpcm->timer); + return 0; } static snd_pcm_uframes_t @@ -435,7 +451,6 @@ static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) long sec; unsigned long nsecs; - dummy_hrtimer_sync(dpcm); period = runtime->period_size; rate = runtime->rate; sec = period / rate; @@ -463,7 +478,7 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream) static void dummy_hrtimer_free(struct snd_pcm_substream *substream) { struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; - dummy_hrtimer_sync(dpcm); + kfree(dpcm); } @@ -473,6 +488,7 @@ static const struct dummy_timer_ops dummy_hrtimer_ops = { .prepare = dummy_hrtimer_prepare, .start = dummy_hrtimer_start, .stop = dummy_hrtimer_stop, + .sync_stop = dummy_hrtimer_sync_stop, .pointer = dummy_hrtimer_pointer, }; @@ -495,6 +511,13 @@ static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return -EINVAL; } +static int dummy_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + if (get_dummy_ops(substream)->sync_stop) + return get_dummy_ops(substream)->sync_stop(substream); + return 0; +} + static int dummy_pcm_prepare(struct snd_pcm_substream *substream) { return get_dummy_ops(substream)->prepare(substream); @@ -646,6 +669,7 @@ static const struct snd_pcm_ops dummy_pcm_ops = { .hw_params = dummy_pcm_hw_params, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, + .sync_stop = dummy_pcm_sync_stop, .pointer = dummy_pcm_pointer, }; @@ -655,6 +679,7 @@ static const struct snd_pcm_ops dummy_pcm_ops_no_buf = { .hw_params = dummy_pcm_hw_params, .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, + .sync_stop = dummy_pcm_sync_stop, .pointer = dummy_pcm_pointer, .copy = dummy_pcm_copy, .fill_silence = dummy_pcm_silence, @@ -1017,6 +1042,12 @@ static int snd_dummy_probe(struct platform_device *devptr) int idx, err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_dummy), &card); if (err < 0) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index c217c427bf1e..b93291ad067a 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platform_device *devptr) int err; struct snd_card *card; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (port[dev] == SNDRV_AUTO_PORT) { dev_err(&devptr->dev, "specify port\n"); return -EINVAL; diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 36e9eab204ca..cefaa00b83e7 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -900,6 +900,12 @@ static int snd_mts64_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index dcc0899cfb99..03ed3c3c27fb 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -697,6 +697,12 @@ static int snd_portman_probe(struct platform_device *pdev) p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); + if (dev < 0) { + dev_warn(&pdev->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 3c28961091b1..cc9c325df910 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -846,6 +846,12 @@ static int snd_serial_probe(struct platform_device *devptr) int err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + switch (adaptor[dev]) { case SNDRV_SERIAL_SOUNDCANVAS: ins[dev] = 1; diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index a204f42d1026..70e16105f1c4 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) int idx, err; int dev = devptr->id; + if (dev < 0 || dev >= SNDRV_CARDS) { + dev_warn(&devptr->dev, + "Invalid card index %d, using default 0\n", dev); + dev = 0; + } + err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_card_virmidi), &card); if (err < 0) diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c index 3fe11983d6ca..0f8fb62f021d 100644 --- a/sound/hda/codecs/ca0132.c +++ b/sound/hda/codecs/ca0132.c @@ -5788,7 +5788,8 @@ static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS; if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS) uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1; - sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx); + snprintf(namestr, sizeof(namestr), "%d %s", + (uinfo->value.enumerated.item * 10), sfx); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5840,9 +5841,9 @@ static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; - sprintf(namestr, "%s %s", - ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, - sfx); + snprintf(namestr, sizeof(namestr), "%s %s", + ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, + sfx); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -5894,8 +5895,8 @@ static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; - sprintf(namestr, "%s", - ae5_filter_presets[uinfo->value.enumerated.item].name); + snprintf(namestr, sizeof(namestr), "%s", + ae5_filter_presets[uinfo->value.enumerated.item].name); strscpy(uinfo->value.enumerated.name, namestr); return 0; } @@ -6632,7 +6633,7 @@ static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid, struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); - sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "FX: %s %s Volume", pfx, dirstr[dir]); knew.tlv.c = NULL; @@ -6671,9 +6672,9 @@ static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, * prefix to OutFX or InFX enable controls. */ if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID)) - sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "FX: %s %s Switch", pfx, dirstr[dir]); else - sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]); + snprintf(namestr, sizeof(namestr), "%s %s Switch", pfx, dirstr[dir]); return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec)); } @@ -8530,10 +8531,9 @@ static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k) static bool ca0132_download_dsp_images(struct hda_codec *codec) { - bool dsp_loaded = false; struct ca0132_spec *spec = codec->spec; const struct dsp_image_seg *dsp_os_image; - const struct firmware *fw_entry = NULL; + const struct firmware *fw_entry __free(firmware) = NULL; /* * Alternate firmwares for different variants. The Recon3Di apparently * can use the default firmware, but I'll leave the option in case @@ -8573,15 +8573,10 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) { codec_err(codec, "ca0132 DSP load image failed\n"); - goto exit_download; + return false; } - dsp_loaded = dspload_wait_loaded(codec); - -exit_download: - release_firmware(fw_entry); - - return dsp_loaded; + return dspload_wait_loaded(codec); } static void ca0132_download_dsp(struct hda_codec *codec) @@ -9634,6 +9629,7 @@ static void ca0132_free(struct hda_codec *codec) #endif kfree(spec->spec_init_verbs); kfree(codec->spec); + codec->spec = NULL; } static void dbpro_free(struct hda_codec *codec) @@ -9644,6 +9640,7 @@ static void dbpro_free(struct hda_codec *codec) kfree(spec->spec_init_verbs); kfree(codec->spec); + codec->spec = NULL; } static void ca0132_config(struct hda_codec *codec) diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c index eeb9ca38d2d8..8a74862d8a81 100644 --- a/sound/hda/codecs/cm9825.c +++ b/sound/hda/codecs/cm9825.c @@ -15,7 +15,8 @@ enum { QUIRK_CM_STD = 0x0, - QUIRK_GENE_TWL7_SSID = 0x160dc000 + QUIRK_GENE_TWL7_SSID = 0x160dc000, + QUIRK_IBP_SSID = 0x15bd3275 }; /* CM9825 Offset Definitions */ @@ -40,13 +41,19 @@ enum { #define CM9825_VERB_SET_GAD 0x7b4 #define CM9825_VERB_SET_TMOD 0x7b5 #define CM9825_VERB_SET_SNR 0x7b6 +#define CM9825_VERB_SET_OMTP 0x7ef +#define CM9825_VERB_READ_OMTP 0xfec struct cmi_spec { struct hda_gen_spec gen; const struct hda_verb *chip_d0_verbs; const struct hda_verb *chip_d3_verbs; + const struct hda_verb *chip_playback_start_verbs; + const struct hda_verb *chip_playback_stop_verbs; const struct hda_verb *chip_hp_present_verbs; const struct hda_verb *chip_hp_remove_verbs; + const struct hda_verb *chip_lineout_present_verbs; + const struct hda_verb *chip_lineout_remove_verbs; struct hda_codec *codec; struct delayed_work unsol_inputs_work; struct delayed_work unsol_lineout_work; @@ -193,6 +200,94 @@ static const struct hda_verb cm9825_gene_twl7_playback_stop_verbs[] = { {} }; +/* + * To save power, AD/CLK is turned off. + */ +static const struct hda_verb cm9825_ibp_d3_verbs[] = { + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_PLL, 0x01}, + {0x43, CM9825_VERB_SET_NEG, 0xc2}, + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_VNEG, 0x50}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {0x43, CM9825_VERB_SET_PDNEG, 0x04}, + {0x43, CM9825_VERB_SET_CDALR, 0xf6}, + {0x43, CM9825_VERB_SET_OTP, 0xcd}, + {} +}; + +/* + * D0 configuration: enable PLL/CLK/ADC/DAC and optimize performance + */ +static const struct hda_verb cm9825_ibp_d0_verbs[] = { + {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + {0x43, CM9825_VERB_SET_SNR, 0x38}, + {0x43, CM9825_VERB_SET_PLL, 0x00}, + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {0x43, CM9825_VERB_SET_VNEG, 0x56}, + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_DACTRL, 0x00}, + {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, + {0x43, CM9825_VERB_SET_CDALR, 0xf4}, + {0x43, CM9825_VERB_SET_OTP, 0xcd}, + {0x43, CM9825_VERB_SET_MTCBA, 0x61}, + {0x43, CM9825_VERB_SET_OCP, 0x33}, + {0x43, CM9825_VERB_SET_GAD, 0x07}, + {0x43, CM9825_VERB_SET_TMOD, 0x26}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE | 0xa0, 0x2d}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE | 0x90, 0x2d}, + {0x43, CM9825_VERB_SET_HPF_1, 0x40}, + {0x43, CM9825_VERB_SET_HPF_2, 0x40}, + {} +}; + +/* + * Enable mbias, ADC. + */ +static const struct hda_verb cm9825_ibp_lineout_present_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x8c}, + {0x43, CM9825_VERB_SET_MBIAS, 0x10}, + {} +}; + +/* + * Disable mbias, ADC. + */ +static const struct hda_verb cm9825_ibp_lineout_remove_verbs[] = { + {0x43, CM9825_VERB_SET_ADCL, 0x00}, + {0x43, CM9825_VERB_SET_MBIAS, 0x00}, + {} +}; + +/* + * Turn on the DAC (widget NID 0x43) in the playback path by writing + * a sequence of vendor-specific verbs. + */ +static const struct hda_verb cm9825_ibp_playback_start_verbs[] = { + {0x43, CM9825_VERB_SET_DACL, 0xaa}, + {0x43, CM9825_VERB_SET_D2S, 0xf2}, + {0x43, CM9825_VERB_SET_VDO, 0xc4}, + {0x43, CM9825_VERB_SET_SNR, 0x30}, + {} +}; + +/* + * Shut down the playback path. The order differs slightly from the + * enable sequence, likely to avoid audible pop noise when powering + * down the output stage. + */ +static const struct hda_verb cm9825_ibp_playback_stop_verbs[] = { + {0x43, CM9825_VERB_SET_VDO, 0xc0}, + {0x43, CM9825_VERB_SET_DACL, 0x02}, + {0x43, CM9825_VERB_SET_D2S, 0x62}, + {0x43, CM9825_VERB_SET_VDO, 0x80}, + {0x43, CM9825_VERB_SET_SNR, 0x38}, + {} +}; + static void cm9825_update_jk_plug_status(struct hda_codec *codec, hda_nid_t nid) { struct cmi_spec *spec = codec->spec; @@ -232,6 +327,23 @@ static void cm9825_unsol_lineout_delayed(struct work_struct *work) struct cmi_spec *spec = container_of(to_delayed_work(work), struct cmi_spec, unsol_lineout_work); + hda_nid_t line_out_pin = spec->gen.autocfg.line_out_pins[0]; + bool line_out_jack_plugin = false; + + line_out_jack_plugin = snd_hda_jack_detect(spec->codec, line_out_pin); + + codec_dbg(spec->codec, "lineout_jack_plugin %d, lineout_pin 0x%X\n", + (int)line_out_jack_plugin, line_out_pin); + + if (!line_out_jack_plugin) { + /* Jack plugout */ + snd_hda_sequence_write(spec->codec, + spec->chip_lineout_remove_verbs); + } else { + /* Jack plugin */ + snd_hda_sequence_write(spec->codec, + spec->chip_lineout_present_verbs); + } cm9825_update_jk_plug_status(spec->codec, spec->gen.autocfg.line_out_pins[0]); @@ -362,12 +474,10 @@ static void cm9825_playback_pcm_hook(struct hda_pcm_stream *hinfo, switch (action) { case HDA_GEN_PCM_ACT_PREPARE: - snd_hda_sequence_write(spec->codec, - cm9825_gene_twl7_playback_start_verbs); + snd_hda_sequence_write(codec, spec->chip_playback_start_verbs); break; case HDA_GEN_PCM_ACT_CLEANUP: - snd_hda_sequence_write(spec->codec, - cm9825_gene_twl7_playback_stop_verbs); + snd_hda_sequence_write(codec, spec->chip_playback_stop_verbs); break; default: return; @@ -471,7 +581,8 @@ static int cm9825_resume(struct hda_codec *codec) if (codec->core.subsystem_id == QUIRK_CM_STD) cm9825_cm_std_resume(codec); - else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID) { + else if (codec->core.subsystem_id == QUIRK_GENE_TWL7_SSID || + codec->core.subsystem_id == QUIRK_IBP_SSID) { snd_hda_codec_init(codec); snd_hda_sequence_write(codec, spec->chip_d0_verbs); } @@ -487,6 +598,7 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) struct cmi_spec *spec; struct auto_pin_cfg *cfg; int err = 0; + unsigned int val; spec = kzalloc_obj(*spec); if (spec == NULL) @@ -523,9 +635,33 @@ static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->chip_d0_verbs = cm9825_gene_twl7_d0_verbs; spec->chip_d3_verbs = cm9825_gene_twl7_d3_verbs; spec->gen.pcm_playback_hook = cm9825_playback_pcm_hook; + spec->chip_playback_start_verbs = + cm9825_gene_twl7_playback_start_verbs; + spec->chip_playback_stop_verbs = + cm9825_gene_twl7_playback_stop_verbs; /* Internal fixed device, Rear, Mic-in, 3.5mm */ snd_hda_codec_set_pincfg(codec, 0x37, 0x24A70100); break; + case QUIRK_IBP_SSID: + snd_hda_codec_set_name(codec, "CM9825 IBP"); + spec->chip_d0_verbs = cm9825_ibp_d0_verbs; + spec->chip_d3_verbs = cm9825_ibp_d3_verbs; + spec->gen.pcm_playback_hook = cm9825_playback_pcm_hook; + spec->chip_lineout_present_verbs = + cm9825_ibp_lineout_present_verbs; + spec->chip_lineout_remove_verbs = + cm9825_ibp_lineout_remove_verbs; + spec->chip_playback_start_verbs = + cm9825_ibp_playback_start_verbs; + spec->chip_playback_stop_verbs = cm9825_ibp_playback_stop_verbs; + + /* OMTP */ + val = + snd_hda_codec_read(codec, 0x46, 0, CM9825_VERB_READ_OMTP, + 0x0); + snd_hda_codec_write(codec, 0x46, 0, CM9825_VERB_SET_OMTP, + (val >> 24) & 0x7f); + break; default: err = -ENXIO; break; diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index 40da2832ba66..c517334b56fd 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -292,6 +292,7 @@ enum { CXT_PINCFG_TOP_SPEAKER, CXT_FIXUP_HP_A_U, CXT_FIXUP_ACER_SWIFT_HP, + CXT_FIXUP_HUAWEI_MATEBOOK_HP, }; /* for hda_fixup_thinkpad_acpi() */ @@ -451,7 +452,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec) if (!spec->dc_enable) { /* disable DC bias path and pin for port F */ update_mic_pin(codec, 0x1e, 0); - snd_hda_activate_path(codec, spec->dc_mode_path, false, false); + if (spec->dc_mode_path) + snd_hda_activate_path(codec, spec->dc_mode_path, false, false); /* update port B (ext mic) and C (int mic) */ /* OLPC defers mic widget control until when capture is @@ -487,7 +489,8 @@ static void olpc_xo_update_mic_pins(struct hda_codec *codec) update_mic_pin(codec, 0x1b, 0); /* enable DC bias path and pin */ update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0); - snd_hda_activate_path(codec, spec->dc_mode_path, true, false); + if (spec->dc_mode_path) + snd_hda_activate_path(codec, spec->dc_mode_path, true, false); } } @@ -1033,6 +1036,13 @@ static const struct hda_fixup cxt_fixups[] = { { } }, }, + [CXT_FIXUP_HUAWEI_MATEBOOK_HP] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03211020 }, /* Headphone */ + { } + }, + }, }; static const struct hda_quirk cxt5045_fixups[] = { @@ -1097,6 +1107,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO), + SND_PCI_QUIRK(0x103c, 0x837b, "HP ProBook 440 G5", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO), SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK), SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK), @@ -1133,6 +1144,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI), + SND_PCI_QUIRK(0x19e5, 0x3289, "Huawei Matebook", CXT_FIXUP_HUAWEI_MATEBOOK_HP), SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004), SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205), SND_PCI_QUIRK(0x1d05, 0x3012, "MECHREVO Wujie 15X Pro", CXT_FIXUP_HEADSET_MIC), diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c index 660a9f2c0ded..6120f797b45a 100644 --- a/sound/hda/codecs/generic.c +++ b/sound/hda/codecs/generic.c @@ -2695,30 +2695,13 @@ static const struct snd_kcontrol_new out_jack_mode_enum = { .put = out_jack_mode_put, }; -static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx) -{ - struct hda_gen_spec *spec = codec->spec; - const struct snd_kcontrol_new *kctl; - int i; - - snd_array_for_each(&spec->kctls, i, kctl) { - if (!strcmp(kctl->name, name) && kctl->index == idx) - return true; - } - return false; -} - static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin, char *name, size_t name_len) { struct hda_gen_spec *spec = codec->spec; - int idx = 0; - snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx); - strlcat(name, " Jack Mode", name_len); - - for (; find_kctl_name(codec, name, idx); idx++) - ; + snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len); + hda_append_suffix(name, " Jack Mode", name_len); } static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin) @@ -5695,7 +5678,7 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, break; } } - strlcat(str, sfx, len); + hda_append_suffix(str, sfx, len); } /* copy PCM stream info from @default_str, and override non-NULL entries diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c index a4a2f2f0540c..053c41d98028 100644 --- a/sound/hda/codecs/hdmi/hdmi.c +++ b/sound/hda/codecs/hdmi/hdmi.c @@ -2236,8 +2236,10 @@ void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id) /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ - if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) + if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND) { + codec->acomp_requested_resume = 1; return; + } snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id); } diff --git a/sound/hda/codecs/helpers/razer_blade16_2025.c b/sound/hda/codecs/helpers/razer_blade16_2025.c new file mode 100644 index 000000000000..1f2e06d8fc13 --- /dev/null +++ b/sound/hda/codecs/helpers/razer_blade16_2025.c @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Razer Blade 16 (2025) external smart-amplifier programming and fixup, + * to be included from the codec driver. + * + * The internal speakers are driven by an external smart amplifier whose + * crossover, voicing and protection live in a DSP reached over a vendor + * coef mailbox on NID 0x20. These tables hold the programming captured + * from the Windows driver, reduced to the NID-0x20 traffic this machine + * needs (everything else the HDA parser rebuilds). + * + * Each entry is either a plain codec coef write or a mailbox write: + * strobe == 0: coef[addr] = lo + * strobe != 0: amp[addr] = (hi << 16) | lo, with the bank selected on + * port 0x89 and the write committed by the strobe opcode. + */ + +struct alc298_razer_blade16_2025_op { + u16 bank; /* mailbox bank (port 0x89); unused for a plain coef */ + u16 addr; /* mailbox register, or plain coef index */ + u16 hi; /* mailbox high word; unused for a plain coef */ + u16 lo; /* mailbox low word, or plain coef value */ + u16 strobe; /* mailbox commit opcode; 0 marks a plain coef write */ +}; + +/* program the external DSP at init; the amp is woken/parked separately */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_init[] = { + { 0x0000, 0xf102, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xf103, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc000, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xea00, 0x0000, 0x0047, 0xb031 }, + { 0x0000, 0xf20d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xf212, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xc001, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc003, 0x0000, 0x0022, 0xb031 }, + { 0x0000, 0xc004, 0x0000, 0x0044, 0xb031 }, + { 0x0000, 0xc005, 0x0000, 0x0044, 0xb031 }, + { 0x0000, 0xc007, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xc00e, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xf223, 0x0000, 0x007f, 0xb031 }, + { 0x0000, 0xf224, 0x0000, 0x00db, 0xb031 }, + { 0x0000, 0xf225, 0x0000, 0x00ee, 0xb031 }, + { 0x0000, 0xf226, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xf227, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xf21a, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xf242, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc120, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc125, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xc321, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc200, 0x0000, 0x00d8, 0xb031 }, + { 0x0000, 0xc201, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xc202, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xc400, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc401, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xc402, 0x0000, 0x00e0, 0xb031 }, + { 0x0000, 0xc403, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc404, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xc406, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc407, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc408, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xc300, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc125, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdf00, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xdf5f, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xdf60, 0x0000, 0x00a7, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x0084, 0xb031 }, + { 0x0000, 0xc206, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xf10a, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f4, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00e0, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f4, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0065, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f7, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xf104, 0x0000, 0x00f7, 0xb031 }, + { 0x0000, 0xf105, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xf10b, 0x0000, 0x005c, 0xb031 }, + { 0x0000, 0xe706, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xe707, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xe806, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xe807, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xce04, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xce05, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce06, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xce07, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xcf04, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xcf05, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xcf06, 0x0000, 0x0031, 0xb031 }, + { 0x0000, 0xcf07, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xc130, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xe000, 0x0000, 0x00a8, 0xb031 }, + { 0x4100, 0x1888, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xea00, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xed00, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xcc2c, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc2d, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xcc2e, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xcc2f, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcd2c, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd2d, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xcd2e, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xcd2f, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcc24, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc25, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xcc26, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xcc27, 0x0000, 0x0085, 0xb031 }, + { 0x0000, 0xcd24, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd25, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xcd26, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xcd27, 0x0000, 0x0085, 0xb031 }, + { 0x0000, 0xcc20, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc21, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc22, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcd20, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd21, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd22, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xcc16, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xcc17, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd16, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xcd17, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc29, 0x0000, 0x005d, 0xb031 }, + { 0x0000, 0xcc2a, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xcd29, 0x0000, 0x005d, 0xb031 }, + { 0x0000, 0xcd2a, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xcc31, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xcc32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc33, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc34, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd31, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xcd32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd33, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd34, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc36, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xcc37, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc38, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc39, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd36, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xcd37, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd38, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcd39, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xcc09, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc0a, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xcc0b, 0x0000, 0x007c, 0xb031 }, + { 0x0000, 0xcc0c, 0x0000, 0x005b, 0xb031 }, + { 0x0000, 0xcd09, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd0a, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xcd0b, 0x0000, 0x007c, 0xb031 }, + { 0x0000, 0xcd0c, 0x0000, 0x005b, 0xb031 }, + { 0x0000, 0xcc0e, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcc0f, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xcc10, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xcc11, 0x0000, 0x002d, 0xb031 }, + { 0x0000, 0xcd0e, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcd0f, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xcd10, 0x0000, 0x003e, 0xb031 }, + { 0x0000, 0xcd11, 0x0000, 0x002d, 0xb031 }, + { 0x0000, 0xccd6, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccd7, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdd6, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcdd7, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xccd8, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccd9, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdd8, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcdd9, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xccda, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xccdb, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xcdda, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xcddb, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xc320, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xc321, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xe604, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xdd00, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xdc19, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdc1a, 0x0000, 0x006a, 0xb031 }, + { 0x0000, 0xdc1b, 0x0000, 0x00aa, 0xb031 }, + { 0x0000, 0xdc1c, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xdc1d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdc1e, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xdc1f, 0x0000, 0x0062, 0xb031 }, + { 0x0000, 0xdc20, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xde19, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xde1a, 0x0000, 0x006a, 0xb031 }, + { 0x0000, 0xde1b, 0x0000, 0x00aa, 0xb031 }, + { 0x0000, 0xde1c, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xde1d, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xde1e, 0x0000, 0x0027, 0xb031 }, + { 0x0000, 0xde1f, 0x0000, 0x0062, 0xb031 }, + { 0x0000, 0xde20, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xdb32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdd32, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdb33, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xdd33, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xdb34, 0x0000, 0x001a, 0xb031 }, + { 0x0000, 0xdd34, 0x0000, 0x001a, 0xb031 }, + { 0x0000, 0xdb15, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdd15, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdb17, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xdd17, 0x0000, 0x003f, 0xb031 }, + { 0x0000, 0xdb94, 0x0000, 0x0070, 0xb031 }, + { 0x0000, 0xdd94, 0x0000, 0x0070, 0xb031 }, + { 0x0000, 0xdb19, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdd19, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x001c, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xdb04, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xdb05, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdd04, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xdd05, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xdbbb, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xdbbc, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xdbbd, 0x0000, 0x00f3, 0xb031 }, + { 0x0000, 0xdbbe, 0x0000, 0x00cf, 0xb031 }, + { 0x0000, 0xddbb, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xddbc, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xddbd, 0x0000, 0x00f3, 0xb031 }, + { 0x0000, 0xddbe, 0x0000, 0x00cf, 0xb031 }, + { 0x0000, 0xdb01, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xdd01, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xdb08, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdd08, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xdc52, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xde52, 0x0000, 0x00ef, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xdf0a, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xdf0b, 0x0000, 0x007f, 0xb031 }, + { 0x0000, 0xc851, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc951, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdf01, 0x0000, 0x0073, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xebcb, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb10, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb11, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xeb12, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xeb13, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xeb14, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb15, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xeb16, 0x0000, 0x0069, 0xb031 }, + { 0x0000, 0xeb21, 0x0000, 0x0011, 0xb031 }, + { 0x0000, 0xeb22, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xeb23, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb24, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb25, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xeb26, 0x0000, 0x0055, 0xb031 }, + { 0x0000, 0xeb27, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb28, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb29, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xeb2a, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xeb2b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xeb30, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb31, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xeb32, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xeb33, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb34, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb35, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xeb36, 0x0000, 0x0024, 0xb031 }, + { 0x0000, 0xeb37, 0x0000, 0x0068, 0xb031 }, + { 0x0000, 0xeb40, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb41, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb42, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xeb43, 0x0000, 0x00e8, 0xb031 }, + { 0x0000, 0xeb44, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb45, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xeb46, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xeb47, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb48, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb49, 0x0000, 0x00c5, 0xb031 }, + { 0x0000, 0xeb4a, 0x0000, 0x0032, 0xb031 }, + { 0x0000, 0xeb4b, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeb50, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb51, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xeb52, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xeb53, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb54, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb55, 0x0000, 0x007d, 0xb031 }, + { 0x0000, 0xeb56, 0x0000, 0x00d9, 0xb031 }, + { 0x0000, 0xeb57, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xeb60, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xeb61, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xeb62, 0x0000, 0x00b5, 0xb031 }, + { 0x0000, 0xeb63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xeb64, 0x0000, 0x00fa, 0xb031 }, + { 0x0000, 0xeb65, 0x0000, 0x0086, 0xb031 }, + { 0x0000, 0xeb66, 0x0000, 0x00a8, 0xb031 }, + { 0x0000, 0xeb67, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xeb68, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xeb69, 0x0000, 0x009a, 0xb031 }, + { 0x0000, 0xeb6a, 0x0000, 0x002f, 0xb031 }, + { 0x0000, 0xeb6b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xebb6, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeb70, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb71, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xeb72, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xeb73, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb74, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xeb75, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xeb76, 0x0000, 0x002c, 0xb031 }, + { 0x0000, 0xeb77, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xeb80, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb81, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb82, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xeb83, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb84, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xeb85, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xeb86, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xeb87, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xeb88, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xeb89, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xeb8a, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xeb8b, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xebb8, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeb90, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xeb91, 0x0000, 0x008b, 0xb031 }, + { 0x0000, 0xeb92, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xeb93, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xeb94, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xeb95, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xeb96, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xeb97, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xeba0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeba1, 0x0000, 0x0091, 0xb031 }, + { 0x0000, 0xeba2, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xeba3, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeba4, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xeba5, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeba6, 0x0000, 0x0093, 0xb031 }, + { 0x0000, 0xeba7, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xeba8, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xeba9, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xebaa, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xebab, 0x0000, 0x006c, 0xb031 }, + { 0x0000, 0xebc3, 0x0000, 0x00ff, 0xb031 }, + { 0x0000, 0xebc4, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xeb00, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xeb01, 0x0000, 0x00a9, 0xb031 }, + { 0x0000, 0xeb02, 0x0000, 0x00df, 0xb031 }, + { 0x0000, 0xeb03, 0x0000, 0x007b, 0xb031 }, + { 0x0000, 0xeb05, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xeb06, 0x0000, 0x005a, 0xb031 }, + { 0x0000, 0xeb07, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xebc0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xd0cb, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xd0b0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd010, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd011, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd012, 0x0000, 0x0092, 0xb031 }, + { 0x0000, 0xd013, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd014, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd015, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xd016, 0x0000, 0x0072, 0xb031 }, + { 0x0000, 0xd017, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xd020, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd021, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd022, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd023, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd024, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd025, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd026, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xd027, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xd028, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd029, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd02a, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd02b, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c3, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c4, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd0c0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xce6a, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xce63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce64, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xdb04, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xdb05, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xcc00, 0x0000, 0x00c1, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca62, 0x0000, 0x0077, 0xb031 }, + { 0x0000, 0xca65, 0x0000, 0x0082, 0xb031 }, + { 0x0000, 0xca68, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xca75, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xca7b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xca00, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xca02, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc860, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc861, 0x0000, 0x00e5, 0xb031 }, + { 0x0000, 0xc862, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xc863, 0x0000, 0x00c6, 0xb031 }, + { 0x0000, 0xc864, 0x0000, 0x00ba, 0xb031 }, + { 0x0000, 0xc865, 0x0000, 0x0061, 0xb031 }, + { 0x0000, 0xc867, 0x0000, 0x0017, 0xb031 }, + { 0x0000, 0xc868, 0x0000, 0x00ed, 0xb031 }, + { 0x0000, 0xc869, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc86a, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xc86b, 0x0000, 0x00e2, 0xb031 }, + { 0x0000, 0xc875, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xc87b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xc836, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc8a0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc833, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc834, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc83f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc83e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc83a, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xc83b, 0x0000, 0x0089, 0xb031 }, + { 0x0000, 0xc830, 0x0000, 0x0013, 0xb031 }, + { 0x0000, 0xc826, 0x0000, 0x00d1, 0xb031 }, + { 0x0000, 0xc890, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc823, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc824, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xc82f, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xc82e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc820, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc816, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc880, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xc813, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc814, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc81f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc81e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc81a, 0x0000, 0x0066, 0xb031 }, + { 0x0000, 0xc81b, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc810, 0x0000, 0x000d, 0xb031 }, + { 0x0000, 0xc846, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc8d0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc844, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc84f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc84e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc84a, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc84b, 0x0000, 0x0056, 0xb031 }, + { 0x0000, 0xc800, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xc851, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc500, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc501, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc502, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc503, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc504, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc505, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc506, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc507, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc510, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc511, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc512, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc513, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc514, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc515, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc516, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc517, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc518, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc541, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd250, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xd251, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd264, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0xf800, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xecb0, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec10, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec11, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xec12, 0x0000, 0x00ab, 0xb031 }, + { 0x0000, 0xec13, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xec14, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec15, 0x0000, 0x004c, 0xb031 }, + { 0x0000, 0xec16, 0x0000, 0x0069, 0xb031 }, + { 0x0000, 0xec21, 0x0000, 0x0011, 0xb031 }, + { 0x0000, 0xec22, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xec23, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xec24, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec25, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xec26, 0x0000, 0x0055, 0xb031 }, + { 0x0000, 0xec27, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec28, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec29, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xec2a, 0x0000, 0x0051, 0xb031 }, + { 0x0000, 0xec2b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xecb2, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec30, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec31, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xec32, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xec33, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec34, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec35, 0x0000, 0x00bb, 0xb031 }, + { 0x0000, 0xec36, 0x0000, 0x0024, 0xb031 }, + { 0x0000, 0xec37, 0x0000, 0x0068, 0xb031 }, + { 0x0000, 0xec40, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec41, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec42, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xec43, 0x0000, 0x00e8, 0xb031 }, + { 0x0000, 0xec44, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec45, 0x0000, 0x004f, 0xb031 }, + { 0x0000, 0xec46, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xec47, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec48, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec49, 0x0000, 0x00c5, 0xb031 }, + { 0x0000, 0xec4a, 0x0000, 0x0032, 0xb031 }, + { 0x0000, 0xec4b, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xecb4, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec50, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec51, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xec52, 0x0000, 0x00b4, 0xb031 }, + { 0x0000, 0xec53, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec54, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec55, 0x0000, 0x007d, 0xb031 }, + { 0x0000, 0xec56, 0x0000, 0x00d9, 0xb031 }, + { 0x0000, 0xec57, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xec60, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xec61, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xec62, 0x0000, 0x00b5, 0xb031 }, + { 0x0000, 0xec63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xec64, 0x0000, 0x00fa, 0xb031 }, + { 0x0000, 0xec65, 0x0000, 0x0086, 0xb031 }, + { 0x0000, 0xec66, 0x0000, 0x00a8, 0xb031 }, + { 0x0000, 0xec67, 0x0000, 0x0040, 0xb031 }, + { 0x0000, 0xec68, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xec69, 0x0000, 0x009a, 0xb031 }, + { 0x0000, 0xec6a, 0x0000, 0x002f, 0xb031 }, + { 0x0000, 0xec6b, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xecb6, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xec70, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec71, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xec72, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xec73, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec74, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xec75, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xec76, 0x0000, 0x002c, 0xb031 }, + { 0x0000, 0xec77, 0x0000, 0x0050, 0xb031 }, + { 0x0000, 0xec80, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec81, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec82, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xec83, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec84, 0x0000, 0x00f5, 0xb031 }, + { 0x0000, 0xec85, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xec86, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xec87, 0x0000, 0x00d0, 0xb031 }, + { 0x0000, 0xec88, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xec89, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xec8a, 0x0000, 0x0016, 0xb031 }, + { 0x0000, 0xec8b, 0x0000, 0x0028, 0xb031 }, + { 0x0000, 0xecb8, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xec90, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xec91, 0x0000, 0x008b, 0xb031 }, + { 0x0000, 0xec92, 0x0000, 0x00bd, 0xb031 }, + { 0x0000, 0xec93, 0x0000, 0x0064, 0xb031 }, + { 0x0000, 0xec94, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xec95, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xec96, 0x0000, 0x00e7, 0xb031 }, + { 0x0000, 0xec97, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xeca0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeca1, 0x0000, 0x0091, 0xb031 }, + { 0x0000, 0xeca2, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xeca3, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xeca4, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xeca5, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xeca6, 0x0000, 0x0093, 0xb031 }, + { 0x0000, 0xeca7, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xeca8, 0x0000, 0x0003, 0xb031 }, + { 0x0000, 0xeca9, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xecaa, 0x0000, 0x0099, 0xb031 }, + { 0x0000, 0xecab, 0x0000, 0x006c, 0xb031 }, + { 0x0000, 0xebc3, 0x0000, 0x00ff, 0xb031 }, + { 0x0000, 0xebc4, 0x0000, 0x00fc, 0xb031 }, + { 0x0000, 0xec00, 0x0000, 0x0005, 0xb031 }, + { 0x0000, 0xec01, 0x0000, 0x00a9, 0xb031 }, + { 0x0000, 0xec02, 0x0000, 0x00df, 0xb031 }, + { 0x0000, 0xec03, 0x0000, 0x007b, 0xb031 }, + { 0x0000, 0xec05, 0x0000, 0x0079, 0xb031 }, + { 0x0000, 0xec06, 0x0000, 0x005a, 0xb031 }, + { 0x0000, 0xec07, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xebc0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xd1b0, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd110, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd111, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd112, 0x0000, 0x0092, 0xb031 }, + { 0x0000, 0xd113, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd114, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd115, 0x0000, 0x00f6, 0xb031 }, + { 0x0000, 0xd116, 0x0000, 0x0072, 0xb031 }, + { 0x0000, 0xd117, 0x0000, 0x00b0, 0xb031 }, + { 0x0000, 0xd120, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd121, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd122, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd123, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd124, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd125, 0x0000, 0x0009, 0xb031 }, + { 0x0000, 0xd126, 0x0000, 0x0090, 0xb031 }, + { 0x0000, 0xd127, 0x0000, 0x0020, 0xb031 }, + { 0x0000, 0xd128, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xd129, 0x0000, 0x00fb, 0xb031 }, + { 0x0000, 0xd12a, 0x0000, 0x0037, 0xb031 }, + { 0x0000, 0xd12b, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c3, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd0c4, 0x0000, 0x000c, 0xb031 }, + { 0x0000, 0xd0c0, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xce63, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xce64, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xdd04, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xdd05, 0x0000, 0x0006, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x000f, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca82, 0x0000, 0x0077, 0xb031 }, + { 0x0000, 0xca85, 0x0000, 0x0082, 0xb031 }, + { 0x0000, 0xca88, 0x0000, 0x00c2, 0xb031 }, + { 0x0000, 0xca95, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xca9b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xca10, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xca12, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc960, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc961, 0x0000, 0x00e5, 0xb031 }, + { 0x0000, 0xc962, 0x0000, 0x00eb, 0xb031 }, + { 0x0000, 0xc963, 0x0000, 0x00c6, 0xb031 }, + { 0x0000, 0xc964, 0x0000, 0x00ba, 0xb031 }, + { 0x0000, 0xc965, 0x0000, 0x0061, 0xb031 }, + { 0x0000, 0xc967, 0x0000, 0x0017, 0xb031 }, + { 0x0000, 0xc968, 0x0000, 0x00ed, 0xb031 }, + { 0x0000, 0xc969, 0x0000, 0x003c, 0xb031 }, + { 0x0000, 0xc96a, 0x0000, 0x008a, 0xb031 }, + { 0x0000, 0xc96b, 0x0000, 0x00e2, 0xb031 }, + { 0x0000, 0xc975, 0x0000, 0x003a, 0xb031 }, + { 0x0000, 0xc97b, 0x0000, 0x006f, 0xb031 }, + { 0x0000, 0xc936, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc9a0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc933, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xc934, 0x0000, 0x0002, 0xb031 }, + { 0x0000, 0xc93f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc93e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc93a, 0x0000, 0x0076, 0xb031 }, + { 0x0000, 0xc93b, 0x0000, 0x0089, 0xb031 }, + { 0x0000, 0xc930, 0x0000, 0x0013, 0xb031 }, + { 0x0000, 0xc926, 0x0000, 0x00d1, 0xb031 }, + { 0x0000, 0xc990, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc923, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc924, 0x0000, 0x0004, 0xb031 }, + { 0x0000, 0xc92f, 0x0000, 0x0018, 0xb031 }, + { 0x0000, 0xc92e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc920, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc916, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc980, 0x0000, 0x00e1, 0xb031 }, + { 0x0000, 0xc913, 0x0000, 0x0000, 0xb031 }, + { 0x0000, 0xc914, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc91f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc91e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc91a, 0x0000, 0x0066, 0xb031 }, + { 0x0000, 0xc91b, 0x0000, 0x0078, 0xb031 }, + { 0x0000, 0xc910, 0x0000, 0x000d, 0xb031 }, + { 0x0000, 0xc946, 0x0000, 0x00f9, 0xb031 }, + { 0x0000, 0xc9d0, 0x0000, 0x00f1, 0xb031 }, + { 0x0000, 0xc944, 0x0000, 0x0001, 0xb031 }, + { 0x0000, 0xc94f, 0x0000, 0x0014, 0xb031 }, + { 0x0000, 0xc94e, 0x0000, 0x0094, 0xb031 }, + { 0x0000, 0xc94a, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc94b, 0x0000, 0x0056, 0xb031 }, + { 0x0000, 0xc951, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xc600, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc601, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc602, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc603, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc604, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc605, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc606, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc607, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc610, 0x0000, 0x0007, 0xb031 }, + { 0x0000, 0xc611, 0x0000, 0x00da, 0xb031 }, + { 0x0000, 0xc612, 0x0000, 0x000e, 0xb031 }, + { 0x0000, 0xc613, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc614, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xc615, 0x0000, 0x0026, 0xb031 }, + { 0x0000, 0xc616, 0x0000, 0x004a, 0xb031 }, + { 0x0000, 0xc617, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xc618, 0x0000, 0x0008, 0xb031 }, + { 0x0000, 0xc541, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xc802, 0x0000, 0x00d2, 0xb031 }, + { 0x0000, 0xc902, 0x0000, 0x00d2, 0xb031 }, + { 0x0000, 0xc800, 0x0000, 0x00f0, 0xb031 }, + { 0x0000, 0xd250, 0x0000, 0x0087, 0xb031 }, + { 0x0000, 0xd252, 0x0000, 0x0030, 0xb031 }, + { 0x0000, 0xd264, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xce60, 0x0000, 0x00e3, 0xb031 }, + { 0x0000, 0xdf00, 0x0000, 0x0010, 0xb031 }, + { 0x0000, 0xdbb5, 0x0041, 0xd27f, 0xb037 }, + { 0x0000, 0xddb5, 0x0040, 0x6e3b, 0xb037 }, + { 0x0000, 0xdb93, 0x0000, 0x009e, 0xb033 }, + { 0x0000, 0xdd93, 0x0000, 0x009e, 0xb033 }, + { 0x0000, 0xdb12, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xdd12, 0x0000, 0x00c0, 0xb031 }, + { 0x0000, 0xdb08, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdd08, 0x0000, 0x0080, 0xb031 }, + { 0x0000, 0xdb00, 0x0000, 0x00cc, 0xb031 }, + { 0x0000, 0xc203, 0x0000, 0x009c, 0xb031 }, + { 0x0000, 0x0010, 0x0000, 0x0f21, 0x0000 }, +}; + +/* wake the amp (ea00=0x43, c121=0x0b, f109=0xe0) */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_wake[] = { + { 0x0000, 0xea00, 0x0000, 0x0043, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000b, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00e0, 0xb031 }, +}; + +/* park the amp (ea00=0x47, c121=0x0a, f109=0xa0) */ +static const struct alc298_razer_blade16_2025_op alc298_razer_blade16_2025_amp_sleep[] = { + { 0x0000, 0xea00, 0x0000, 0x0047, 0xb031 }, + { 0x0000, 0xc121, 0x0000, 0x000a, 0xb031 }, + { 0x0000, 0xf109, 0x0000, 0x00a0, 0xb031 }, +}; + +/* + * Razer Blade 16 (2025): Realtek ALC298 driving an external smart amp. + * Crossover, voicing and speaker protection live in an external DSP + * reached over a vendor-coef mailbox on NID 0x20; the captured DSP + * programming is in the tables above. NID 0x14, the BIOS-disabled + * tweeter pin, is re-exposed via a pin-config override so the parser + * drives it as a normal internal speaker (shared volume/mute, auto-mute + * on headphone insertion). + * + * alc298_razer_blade16_2025_apply() runs one op-table: a plain coef write + * (strobe == 0) goes through alc_write_coef_idx(), while a mailbox op + * selects a bank and streams four PROC_COEF words (addr/hi/lo/commit) + * into a single coef index (0x23) on NID 0x20 to reach the external DSP + * -- that one-index/many-words form is why it cannot be expressed as an + * alc_process_coef_fw() table (one value per index). + */ +static void alc298_razer_blade16_2025_apply(struct hda_codec *codec, + const struct alc298_razer_blade16_2025_op *seq, + int num) +{ + int i; + + for (i = 0; i < num; i++) { + if (!seq[i].strobe) { + alc_write_coef_idx(codec, seq[i].addr, seq[i].lo); + continue; + } + alc_write_coef_idx(codec, 0x89, seq[i].bank); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x23); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].addr); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].hi); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].lo); + snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, seq[i].strobe); + } +} + +static void alc298_razer_blade16_2025_pcm_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + /* power the external amp only while a stream is running */ + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_wake, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_wake)); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_sleep, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_sleep)); + break; + } +} + +static void alc298_fixup_razer_blade16_2025(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* + * Pin every output to a fixed converter so the routing does not + * rely on the generic parser's DAC-allocation heuristics: without + * a fixed assignment the parser brings up only one speaker pin and + * leaves the other silent, and the allocation could change between + * kernel versions. Both speaker pins share DAC 0x03 through mixer + * 0x0d: that is the only converter that drives the woofer (NID + * 0x17) on this machine -- feeding it from DAC 0x02 or the digital + * converter 0x06 leaves the woofer silent. Both pins carry the + * same stereo stream and the external amp does the crossover. + * Headphone NID 0x21 keeps DAC 0x02. + */ + static const hda_nid_t preferred_pairs[] = { + 0x21, 0x02, + 0x14, 0x03, + 0x17, 0x03, + 0 + }; + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.preferred_dacs = preferred_pairs; + break; + case HDA_FIXUP_ACT_PROBE: + spec->gen.pcm_playback_hook = alc298_razer_blade16_2025_pcm_hook; + break; + case HDA_FIXUP_ACT_INIT: + /* + * Wake the amp, program its DSP at boot and on resume, then + * park it. It is woken again for playback and parked + * afterwards from the pcm_playback_hook. + */ + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_wake, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_wake)); + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_init, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_init)); + alc298_razer_blade16_2025_apply(codec, alc298_razer_blade16_2025_amp_sleep, + ARRAY_SIZE(alc298_razer_blade16_2025_amp_sleep)); + break; + } +} diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 48a2e7f2ebb1..3f0e276fc606 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -296,7 +296,8 @@ static void alc282_init(struct hda_codec *codec) msleep(100); /* Headphone capless set to normal mode */ - alc_write_coef_idx(codec, 0x78, coef78); + if (coef78 != -1) + alc_write_coef_idx(codec, 0x78, coef78); } static void alc282_shutup(struct hda_codec *codec) @@ -333,7 +334,8 @@ static void alc282_shutup(struct hda_codec *codec) alc_auto_setup_eapd(codec, false); alc_shutup_pins(codec); - alc_write_coef_idx(codec, 0x78, coef78); + if (coef78 != -1) + alc_write_coef_idx(codec, 0x78, coef78); } static const struct coef_fw alc283_coefs[] = { @@ -585,15 +587,19 @@ static void alc285_hp_init(struct hda_codec *codec) alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */ val = alc_read_coefex_idx(codec, 0x58, 0x00); - for (i = 0; i < 20 && val & 0x8000; i++) { + for (i = 0; i < 20 && val != -1 && val & 0x8000; i++) { msleep(50); val = alc_read_coefex_idx(codec, 0x58, 0x00); } /* Wait for depop procedure finish */ - alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ - alc_update_coef_idx(codec, 0x38, 1<<4, coef38); - alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); - alc_update_coef_idx(codec, 0x36, 3<<13, coef36); + if (val != -1) + alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */ + if (coef38 != -1) + alc_update_coef_idx(codec, 0x38, 1<<4, coef38); + if (coef0d != -1) + alc_update_coef_idx(codec, 0x0d, 0x110, coef0d); + if (coef36 != -1) + alc_update_coef_idx(codec, 0x36, 3<<13, coef36); msleep(50); alc_update_coef_idx(codec, 0x4a, 1<<15, 0); @@ -858,7 +864,7 @@ static void alc294_hp_init(struct hda_codec *codec) /* Wait for depop procedure finish */ val = alc_read_coefex_idx(codec, 0x58, 0x01); - for (i = 0; i < 20 && val & 0x0080; i++) { + for (i = 0; i < 20 && val != -1 && val & 0x0080; i++) { msleep(50); val = alc_read_coefex_idx(codec, 0x58, 0x01); } @@ -1392,6 +1398,15 @@ static void alc245_fixup_hp_gpio_led(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0, 0x04); } +static void alc245_fixup_minisforum_jack_detect(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + if (action == HDA_FIXUP_ACT_INIT) + /* Clear Reset HP JD while preserving the other coefficient bits. */ + alc_update_coef_idx(codec, 0x4a, BIT(15), 0); +} + /* turn on/off mic-mute LED per capture hook via VREF change */ static int vref_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -2557,6 +2572,13 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec, } } +static void alc285_lenovo_dac_rename(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_BUILD) + rename_ctl(codec, "Line Out Playback Volume", + "Headphone Playback Volume"); +} static void alc290_fixup_mono_speakers(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3262,6 +3284,36 @@ static void find_cirrus_companion_amps(struct hda_codec *cdc) comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count); } +static void aw88399_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + comp_generic_fixup(cdc, action, "i2c", "AWDZ8399", "-%s:00-aw88399-hda.%d", 2); +} + +static void alc287_fixup_legion_16iax10h_aw88399(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* + * Force DAC 0x02 for the bass speaker 0x17, as the default 0x06 lacks volume controls. + */ + static const hda_nid_t conn[] = { 0x02 }; + struct alc_spec *spec = codec->spec; + + alc269_fixup_limit_int_mic_boost(codec, fix, action); + alc_fixup_headset_mode_no_hp_mic(codec, fix, action); + alc_fixup_headset_jack(codec, fix, action); + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.suppress_auto_mic = 1; + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + break; + case HDA_FIXUP_ACT_BUILD: + spec->gen.multiout.max_channels = 2; + spec->gen.pcm_rec[0]->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 2; + break; + } +} + static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) { comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2); @@ -3357,6 +3409,9 @@ static void alc287_fixup_acer_micmute_led(struct hda_codec *codec, /* for alc285_fixup_ideapad_s740_coef() */ #include "../helpers/ideapad_s740.c" +/* for alc298_fixup_razer_blade16_2025() */ +#include "../helpers/razer_blade16_2025.c" + static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = { WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000), @@ -3524,6 +3579,16 @@ static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec, } } +static void alc_fixup_yoga_pro7_audio(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ + alc285_fixup_thinkpad_x1_gen7(codec, fix, action); + + /* Limit the internal mic boost to 0 or 1 to avoid noise */ + alc269_fixup_limit_int_mic_boost(codec, fix, action); +} + static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3929,6 +3994,7 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, + ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ASUS_ZENBOOK, ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -3997,6 +4063,7 @@ enum { ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_FIXUP_DISABLE_DAC3, ALC285_FIXUP_SPEAKER2_TO_DAC1, + ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1, ALC285_FIXUP_ASUS_HEADSET_MIC, ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS, @@ -4066,7 +4133,6 @@ enum { ALC294_FIXUP_ASUS_ALLY, ALC294_FIXUP_ASUS_ALLY_PINS, ALC294_FIXUP_ASUS_ALLY_VERBS, - ALC294_FIXUP_ASUS_ALLY_SPEAKER, ALC294_FIXUP_ASUS_HPE, ALC294_FIXUP_ASUS_COEF_1B, ALC294_FIXUP_ASUS_GX502_HP, @@ -4091,6 +4157,8 @@ enum { ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, ALC298_FIXUP_LG_GRAM_STYLE_14, + ALC298_FIXUP_RAZER_BLADE16_2025_PINS, + ALC298_FIXUP_RAZER_BLADE16_2025, ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, @@ -4108,7 +4176,6 @@ enum { ALC269_FIXUP_LEMOTE_A190X, ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC233_FIXUP_INTEL_NUC8_DMIC, - ALC233_FIXUP_INTEL_NUC8_BOOST, ALC256_FIXUP_INTEL_NUC10, ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, @@ -4139,6 +4206,7 @@ enum { ALC298_FIXUP_LENOVO_C940_DUET7, ALC287_FIXUP_LENOVO_YOGA_BOOK_9I, ALC287_FIXUP_LENOVO_YOGA_PRO7, + ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK, ALC287_FIXUP_13S_GEN2_SPEAKERS, ALC256_FIXUP_SET_COEF_DEFAULTS, ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, @@ -4183,7 +4251,6 @@ enum { ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, ALC2XX_FIXUP_HEADSET_MIC, ALC289_FIXUP_DELL_CS35L41_SPI_2, - ALC294_FIXUP_CS35L41_I2C_2, ALC256_FIXUP_ACER_SFG16_MICMUTE_LED, ALC256_FIXUP_HEADPHONE_AMP_VOL, ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX, @@ -4197,6 +4264,7 @@ enum { ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318, ALC256_FIXUP_CHROME_BOOK, ALC245_FIXUP_CLEVO_NOISY_MIC, + ALC245_FIXUP_MINISFORUM_JACK_DETECT, ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE, ALC233_FIXUP_MEDION_MTL_SPK, ALC233_FIXUP_STARLABS_STARFIGHTER, @@ -4206,7 +4274,6 @@ enum { ALC274_FIXUP_HP_AIO_BIND_DACS, ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2, ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC, - ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1, ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC, ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK, ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE, @@ -4226,6 +4293,10 @@ enum { ALC236_FIXUP_DELL_HP_POP_NOISE, ALC274_FIXUP_HP_89E9_GPIO, ALC274_FIXUP_HP_VERBS, + ALC287_FIXUP_AW88399_I2C_2, + ALC287_FIXUP_LENOVO_LEGION_AW88399, + ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET, + ALC285_LENOVO_DAC_RENAME, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -4263,6 +4334,20 @@ static void alc287_fixup_lenovo_yoga_book_9i(struct hda_codec *codec, } static const struct hda_fixup alc269_fixups[] = { + [ALC298_FIXUP_RAZER_BLADE16_2025_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90170121 }, /* tweeter as internal speaker, seq 1 */ + { 0x17, 0x90170120 }, /* woofer as internal speaker, seq 0 */ + { } + }, + .chained = true, + .chain_id = ALC298_FIXUP_RAZER_BLADE16_2025, + }, + [ALC298_FIXUP_RAZER_BLADE16_2025] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc298_fixup_razer_blade16_2025, + }, [ALC233_FIXUP_WUJIE_SPEAKERS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -4703,6 +4788,10 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC271_FIXUP_DMIC, }, [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost + }, + [ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, .chained = true, @@ -5078,11 +5167,7 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic, .chained = true, - .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST, - }, - [ALC233_FIXUP_INTEL_NUC8_BOOST] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_limit_int_mic_boost + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, }, [ALC255_FIXUP_DELL_SPK_NOISE] = { .type = HDA_FIXUP_FUNC, @@ -5143,6 +5228,10 @@ static const struct hda_fixup alc269_fixups[] = { [ALC285_FIXUP_SPEAKER2_TO_DAC1] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_speaker2_to_dac1, + }, + [ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_fixup_speaker2_to_dac1, .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, @@ -5662,11 +5751,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER - }, - [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, + .chain_id = ALC285_FIXUP_SPEAKER2_TO_DAC1 }, [ALC285_FIXUP_THINKPAD_X1_GEN7] = { .type = HDA_FIXUP_FUNC, @@ -6209,11 +6294,20 @@ static const struct hda_fixup alc269_fixups[] = { }, [ALC287_FIXUP_LENOVO_YOGA_PRO7] = { .type = HDA_FIXUP_FUNC, - /* Reuse the DAC routing selected for ThinkPad X1 Gen7 */ - .v.func = alc285_fixup_thinkpad_x1_gen7, + .v.func = alc_fixup_yoga_pro7_audio, .chained = true, .chain_id = ALC269_FIXUP_LENOVO_XPAD_ACPI, }, + [ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + /* Same as ALC285_FIXUP_THINKPAD_HEADSET_JACK, except that the + * mute LEDs are driven through ideapad_laptop rather than + * thinkpad_acpi. + */ + .chain_id = ALC287_FIXUP_LENOVO_YOGA_PRO7, + }, [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_shutup, @@ -6653,10 +6747,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC289_FIXUP_DUAL_SPK }, - [ALC294_FIXUP_CS35L41_I2C_2] = { - .type = HDA_FIXUP_FUNC, - .v.func = cs35l41_fixup_i2c_two, - }, [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc256_fixup_acer_sfg16_micmute_led, @@ -6729,6 +6819,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE, }, + [ALC245_FIXUP_MINISFORUM_JACK_DETECT] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc245_fixup_minisforum_jack_detect, + }, [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -6749,6 +6843,8 @@ static const struct hda_fixup alc269_fixups[] = { [ALC233_FIXUP_STARLABS_STARFIGHTER] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_fixup_starlabs_starfighter, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, }, [ALC294_FIXUP_BASS_SPEAKER_15] = { .type = HDA_FIXUP_FUNC, @@ -6776,11 +6872,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1 - }, - [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc285_fixup_speaker2_to_dac1, + .chain_id = ALC285_FIXUP_SPEAKER2_TO_DAC1 }, [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = { .type = HDA_FIXUP_FUNC, @@ -6898,6 +6990,26 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_HP_89E9_GPIO, }, + [ALC287_FIXUP_AW88399_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = aw88399_fixup_i2c_two, + }, + [ALC287_FIXUP_LENOVO_LEGION_AW88399] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16iax10h_aw88399, + .chained = true, + .chain_id = ALC287_FIXUP_AW88399_I2C_2, + }, + [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, + }, + [ALC285_LENOVO_DAC_RENAME] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc285_lenovo_dac_rename, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -6953,6 +7065,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x166c, "Acer Predator PH16-71", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), @@ -6961,6 +7074,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1025, 0x1909, "Acer Nitro ANV16-42", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), @@ -7145,10 +7259,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), + SND_PCI_QUIRK(0x103c, 0x85f3, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x864f, "HP Laptop 15-dy0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), @@ -7259,6 +7375,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a0e, "HP Pavilion Laptop 15-eh2xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8a1f, "HP Laptop 14s-dr5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), @@ -7350,7 +7467,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), - SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX), SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS), SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), @@ -7442,6 +7559,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8e0d, "HP EliteBook 6 G1a 14 (AD3Q9ET#UUG)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2), @@ -7501,6 +7619,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f57, "HP Trekker G7JC", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8f7e, "HP Messi", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8f94, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x103c, 0x8f95, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED_INVERTED), SND_PCI_QUIRK(0x1043, 0x1024, "ASUS Zephyrus G14 2025", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), @@ -7508,6 +7627,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), + SND_PCI_QUIRK(0x1043, 0x1044, "ASUS GA403UM", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), @@ -7515,16 +7635,15 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x10c4, "ASUS Vivobook S14 S5406SA", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), - HDA_CODEC_QUIRK(0x1043, 0x1204, "ASUS Strix G16 G615JMR", ALC287_FIXUP_TXNW2781_I2C_ASUS), - SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - HDA_CODEC_QUIRK(0x1043, 0x1214, "ASUS ROG Strix G615LP", ALC287_FIXUP_TXNW2781_I2C_ASUS), - SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TXNW2781_I2C_ASUS), + SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C_ASUS), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), @@ -7573,7 +7692,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401), @@ -7584,7 +7703,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19f4, "ASUS UM3405GA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC294_FIXUP_ASUS_SPI_HEADSET_MIC), - SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), @@ -7613,6 +7732,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15), SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1e3e, "ASUS VivoBook M515DA/X515DAP", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC), @@ -7624,8 +7744,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RC72LA", ALC287_FIXUP_ASUS_ALLY_X), SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2), - HDA_CODEC_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), - SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1), SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2), @@ -7836,7 +7955,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), @@ -7852,7 +7971,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), - SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x225c, "Lenovo ThinkPad X1 Carbon 6th Gen", ALC285_LENOVO_DAC_RENAME), + SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2288, "Thinkpad X390", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), @@ -7927,7 +8047,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), - SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), + SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_YOGA_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ HDA_CODEC_QUIRK(0x17aa, 0x38a7, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), @@ -7958,6 +8078,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x17aa, 0x3926, "Legion Pro 5 16ADR10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + /* Legion R9000P ADR10 shares PCI SSID 17aa:38bb with Yoga S780-14.5 Air AMD quad AAC; + * use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x3927, "Legion R9000P ADR10", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3928, "Legion R9000P ADR10", ALC287_FIXUP_LENOVO_LEGION_AW88399), SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C), @@ -7975,7 +8101,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; * use codec SSID to distinguish them */ @@ -7985,7 +8110,9 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x3906, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3907, "Legion Pro 7i 16IAX10H / Y9000P IAX10", ALC287_FIXUP_LENOVO_LEGION_AW88399), + SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET), SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC), @@ -7994,14 +8121,19 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + HDA_CODEC_QUIRK(0x17aa, 0x3936, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3937, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3938, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + HDA_CODEC_QUIRK(0x17aa, 0x3939, "Legion Pro 7 16AFR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399), + SND_PCI_QUIRK(0x17aa, 0x393e, "Lenovo ThinkBook 14 G8+ IPH", ALC287_FIXUP_LENOVO_XPAD_HEADSET_JACK), HDA_CODEC_QUIRK(0x17aa, 0x394c, "Lenovo Yoga Slim 7 14AGP11", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), - SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), - SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK), @@ -8014,7 +8146,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS), - SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_THINKPAD_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), @@ -8028,9 +8160,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS), SND_PCI_QUIRK(0x1854, 0x0490, "LG Gram Style 14 (14Z90RS)", ALC298_FIXUP_LG_GRAM_STYLE_14), + SND_PCI_QUIRK(0x1854, 0x0554, "LG gram 16 (16Z90TR)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS), SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS), SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1a58, 0x2023, "Razer Blade 16 (2025)", + ALC298_FIXUP_RAZER_BLADE16_2025_PINS), + SND_PCI_QUIRK(0x1a58, 0x300e, "Razer Blade 16 (2025)", + ALC298_FIXUP_RAZER_BLADE16_2025_PINS), SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20), SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI), SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101), @@ -8064,13 +8201,17 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1e39, 0xca14, "MEDION NM14LNL", ALC233_FIXUP_MEDION_MTL_SPK), SND_PCI_QUIRK(0x1e50, 0x7007, "Positivo DN50E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1e50, 0x7036, "Acer Gadget E10 ETBook", ALC233_FIXUP_WUJIE_SPEAKERS), SND_PCI_QUIRK(0x1e50, 0x7038, "Positivo DN140", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1ee7, 0x2081, "HONOR MRB-XXX M1020", ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO), + SND_PCI_QUIRK(0x1f4c, 0xb020, "Minisforum AI X1 Pro", + ALC245_FIXUP_MINISFORUM_JACK_DETECT), SND_PCI_QUIRK(0x1f4c, 0xe001, "Minisforum V3 (SE)", ALC245_FIXUP_BASS_HP_DAC), SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK), + SND_PCI_QUIRK(0x2145, 0x0001, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), @@ -8081,6 +8222,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX), SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK), + SND_PCI_QUIRK(0x2782, 0xa128, "Positivo N15RPE-S", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0xa212, "Lunnen Ground 14", ALC269VC_FIXUP_LUNNEN_GROUND_14), SND_PCI_QUIRK(0x7017, 0x2014, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER), SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), @@ -8154,6 +8296,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI), SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED), + SND_PCI_QUIRK_VENDOR(0x2145, "Star Labs", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), {} }; @@ -8293,6 +8436,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"}, {.id = ALC245_FIXUP_BASS_HP_DAC, .name = "alc245-fixup-bass-hp-dac"}, {.id = ALC256_FIXUP_HONOR_MRB_XXX_M1020_AUDIO, .name = "alc256-honor-mrb-xxx-m1020-audio"}, + {.id = ALC287_FIXUP_LENOVO_LEGION_AW88399, .name = "alc287-lenovo-legion-aw88399"}, {} }; #define ALC225_STANDARD_PINS \ @@ -8898,6 +9042,7 @@ static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id) spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ if (codec->core.vendor_id == 0x10ec0236 && + codec->bus->pci && codec->bus->pci->vendor != PCI_VENDOR_ID_AMD) spec->en_3kpull_low = false; break; diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c index 2cf3664a35ff..eff7c83b9c59 100644 --- a/sound/hda/codecs/realtek/alc662.c +++ b/sound/hda/codecs/realtek/alc662.c @@ -325,6 +325,7 @@ enum { ALC897_FIXUP_UNIS_H3C_X500S, ALC897_FIXUP_HEADSET_MIC_PIN3, ALC662_FIXUP_CSL_GPIO, + ALC897_FIXUP_BEELINK_SER6_AMP, }; static const struct hda_fixup alc662_fixups[] = { @@ -782,6 +783,10 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc662_fixup_csl_amp, }, + [ALC897_FIXUP_BEELINK_SER6_AMP] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_gpio4, + }, }; static const struct hda_quirk alc662_fixup_tbl[] = { @@ -852,6 +857,8 @@ static const struct hda_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1e63, 0x6d9a, "F+ FLAPTOP r", ALC897_FIXUP_HP_HSMIC_VERB), + SND_PCI_QUIRK(0x1f66, 0x0202, "Beelink SER6 Max 6900", ALC897_FIXUP_BEELINK_SER6_AMP), #if 0 /* Below is a quirk table taken from the old code. diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig index 1cfd83e251e4..f90f1dfcec68 100644 --- a/sound/hda/codecs/side-codecs/Kconfig +++ b/sound/hda/codecs/side-codecs/Kconfig @@ -13,6 +13,24 @@ config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST Documentation/dev-tools/kunit/. If in doubt, say "N". +config SND_HDA_SCODEC_AW88399 + tristate + select SND_HDA_GENERIC + +config SND_HDA_SCODEC_AW88399_I2C + tristate "Build AW88399 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select SND_HDA_SCODEC_AW88399 + select SND_SOC_AW88399_LIB + help + Say Y or M here to include AW88399 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_AW88399_I2C=m + config SND_HDA_SCODEC_CS35L41 tristate select SND_HDA_GENERIC diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile index 245e84f6a121..7dd010c68f78 100644 --- a/sound/hda/codecs/side-codecs/Makefile +++ b/sound/hda/codecs/side-codecs/Makefile @@ -3,6 +3,8 @@ subdir-ccflags-y += -I$(src)/../../common snd-hda-cirrus-scodec-y := cirrus_scodec.o snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o +snd-hda-scodec-aw88399-y := aw88399_hda.o +snd-hda-scodec-aw88399-i2c-y := aw88399_hda_i2c.o snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o @@ -16,6 +18,8 @@ snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o +obj-$(CONFIG_SND_HDA_SCODEC_AW88399) += snd-hda-scodec-aw88399.o +obj-$(CONFIG_SND_HDA_SCODEC_AW88399_I2C) += snd-hda-scodec-aw88399-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.c b/sound/hda/codecs/side-codecs/aw88399_hda.c new file mode 100644 index 000000000000..11cef4923024 --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// AW88399 HDA side codec driver +// +// Based on cs35l41_hda.c and aw88399.c +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../generic.h" +#include "hda_component.h" +#include "aw88399_hda.h" + +#define AW88399_HDA_I2C_BASE_ADDR 0x34 + +static void aw88399_hda_playback_hook(struct device *dev, int action) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct aw88399 *core = aw88399->core; + int ret = 0; + + dev_dbg(aw88399->dev, "Playback action: %d\n", action); + + switch (action) { + case HDA_GEN_PCM_ACT_OPEN: + pm_runtime_get_sync(dev); + aw88399->playing = true; + break; + case HDA_GEN_PCM_ACT_PREPARE: + if (core) + aw88399_start(core, AW88399_SYNC_START); + break; + case HDA_GEN_PCM_ACT_CLEANUP: + if (aw88399->aw_dev) + ret = aw88399_stop(aw88399->aw_dev); + if (ret) + dev_err(aw88399->dev, "Failed to stop amplifier: %d\n", ret); + break; + case HDA_GEN_PCM_ACT_CLOSE: + if (aw88399->aw_dev) + aw88399_stop(aw88399->aw_dev); + aw88399->playing = false; + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + break; + default: + dev_warn(aw88399->dev, "Unsupported action: %d\n", action); + break; + } +} + +static int aw88399_hda_bind(struct device *dev, struct device *master, void *master_data) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, aw88399->index); + if (!comp) + return -EINVAL; + + if (comp->dev) + return -EBUSY; + + comp->dev = dev; + + strscpy(comp->name, dev_name(dev), sizeof(comp->name)); + + comp->playback_hook = aw88399_hda_playback_hook; + + dev_info(aw88399->dev, + "AW88399 Bound - SSID: %s, channel: %d\n", + aw88399->acpi_subsystem_id, aw88399->channel); + + return 0; +} + +static void aw88399_hda_unbind(struct device *dev, struct device *master, void *master_data) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + struct hda_component_parent *parent = master_data; + struct hda_component *comp; + + comp = hda_component_from_index(parent, aw88399->index); + if (comp && (comp->dev == dev)) + memset(comp, 0, sizeof(*comp)); + + dev_dbg(aw88399->dev, "Unbound from HDA codec\n"); +} + +static const struct component_ops aw88399_hda_comp_ops = { + .bind = aw88399_hda_bind, + .unbind = aw88399_hda_unbind, +}; + +static int aw88399_hda_index_from_i2c(struct aw88399_hda *aw88399) +{ + return to_i2c_client(aw88399->dev)->addr - AW88399_HDA_I2C_BASE_ADDR; +} + +static int aw88399_hda_init(struct aw88399_hda *aw88399) +{ + struct device *dev = aw88399->dev; + struct i2c_client *i2c = to_i2c_client(dev); + struct aw88399 *core; + int ret; + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + mutex_init(&core->lock); + core->reset_gpio = aw88399->reset_gpio; + core->regmap = aw88399->regmap; + core->bsts_unreliable = aw88399->bsts_unreliable; + + aw88399_hw_reset(core); + + ret = aw88399_init(core, i2c, aw88399->regmap); + if (ret) + return ret; + + /* Set channel BEFORE loading firmware so ACF parser sees correct value */ + if (core->aw_pa) + aw88399_dev_set_channel(core, aw88399->channel); + + ret = aw88399_request_firmware_file(core); + if (ret) + return ret; + + aw88399->core = core; + aw88399->aw_dev = core->aw_pa; + + return 0; +} + +static int aw88399_swap_channels(struct aw88399_hda *aw88399) +{ + /* + * Certain Lenovo Legion laptops have their + * I2C wiring reversed: 0x34 is physically the right speaker, + * 0x35 is the left. Swap channels to correct L/R assignment. + * This is a model-specific hardware wiring issue, not a driver bug. + */ + aw88399->channel = 1 - aw88399->channel; + dev_dbg(aw88399->dev, + "Channel swap applied: index %d -> channel %d\n", + aw88399->index, aw88399->channel); + return 0; +} + +static int aw88399_skip_bsts_check(struct aw88399_hda *aw88399) +{ + /* + * BSTS (boost-finished) status bit does not reliably report on + * some hardware. On certain Lenovo Legion laptops, both amps + * report BSTS=0 (boost not finished) during normal playback + * despite clean audio output. Skip BSTS in the startup status + * check to avoid false init failures. + */ + aw88399->bsts_unreliable = true; + dev_dbg(aw88399->dev, "BSTS status check disabled\n"); + return 0; +} + +static int aw88399_apply_legion_quirks(struct aw88399_hda *aw88399) +{ + aw88399_swap_channels(aw88399); + aw88399_skip_bsts_check(aw88399); + return 0; +} + +struct aw88399_prop_model { + const char *ssid; + int (*apply_prop)(struct aw88399_hda *aw88399); +}; + +static const struct aw88399_prop_model aw88399_prop_model_table[] = { + { "17AA3906", aw88399_apply_legion_quirks }, + { "17AA3907", aw88399_apply_legion_quirks }, + { "17AA3927", aw88399_apply_legion_quirks }, + { "17AA3928", aw88399_apply_legion_quirks }, + { "17AA3936", aw88399_apply_legion_quirks }, + { "17AA3937", aw88399_apply_legion_quirks }, + { "17AA3938", aw88399_apply_legion_quirks }, + { "17AA3939", aw88399_apply_legion_quirks }, + { } +}; + +static int aw88399_hda_acpi_probe(struct aw88399_hda *aw88399) +{ + struct acpi_device *adev; + const char *sub; + const struct aw88399_prop_model *model; + + aw88399->index = aw88399_hda_index_from_i2c(aw88399); + aw88399->channel = aw88399->index; + aw88399->acpi_subsystem_id = NULL; + + adev = acpi_dev_get_first_match_dev("AWDZ8399", NULL, -1); + if (!adev) { + dev_err(aw88399->dev, "Failed to find an ACPI device for AWDZ8399\n"); + return -ENODEV; + } + + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); + acpi_dev_put(adev); + if (!physdev) + return -ENODEV; + + sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); + if (IS_ERR_OR_NULL(sub)) + return 0; + + aw88399->acpi_subsystem_id = devm_kstrdup(aw88399->dev, sub, GFP_KERNEL); + kfree(sub); + if (!aw88399->acpi_subsystem_id) + return -ENOMEM; + + for (model = aw88399_prop_model_table; model->ssid; model++) { + if (!strcasecmp(model->ssid, aw88399->acpi_subsystem_id)) { + dev_info(aw88399->dev, + "Applying properties for SSID %s\n", + aw88399->acpi_subsystem_id); + return model->apply_prop(aw88399); + } + } + + return 0; +} + +int aw88399_hda_probe(struct device *dev, struct regmap *regmap) +{ + struct aw88399_hda *aw88399; + int ret; + + aw88399 = devm_kzalloc(dev, sizeof(*aw88399), GFP_KERNEL); + if (!aw88399) + return -ENOMEM; + + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to obtain regmap\n"); + + aw88399->dev = dev; + aw88399->regmap = regmap; + dev_set_drvdata(dev, aw88399); + + aw88399->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88399->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(aw88399->reset_gpio), + "Failed to get reset GPIO\n"); + + ret = aw88399_hda_acpi_probe(aw88399); + if (ret) + return dev_err_probe(dev, ret, "ACPI probe failed\n"); + + ret = aw88399_hda_init(aw88399); + if (ret) + return dev_err_probe(dev, ret, "Chip initialization failed\n"); + + /* Enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = component_add(dev, &aw88399_hda_comp_ops); + if (ret) { + pm_runtime_disable(dev); + return dev_err_probe(dev, ret, "Failed to register component\n"); + } + + dev_info(dev, "AW88399 HDA side codec registered successfully\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(aw88399_hda_probe, "SND_HDA_SCODEC_AW88399"); + +void aw88399_hda_remove(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + + if (aw88399->aw_dev) + aw88399_stop(aw88399->aw_dev); + + component_del(dev, &aw88399_hda_comp_ops); + + dev_dbg(aw88399->dev, "AW88399 HDA side codec removed\n"); +} +EXPORT_SYMBOL_NS_GPL(aw88399_hda_remove, "SND_HDA_SCODEC_AW88399"); + +static int aw88399_hda_runtime_suspend(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + dev_dbg(aw88399->dev, "Runtime suspend\n"); + + if (aw88399->aw_dev && aw88399->playing) + aw88399_stop(aw88399->aw_dev); + + return 0; +} + +static int aw88399_hda_runtime_resume(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + + dev_dbg(aw88399->dev, "Runtime resume\n"); + + if (aw88399->core && aw88399->aw_dev && aw88399->playing) + aw88399_start(aw88399->core, AW88399_SYNC_START); + + return 0; +} + +static int aw88399_hda_system_suspend(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + int ret; + + dev_dbg(aw88399->dev, "System suspend\n"); + + if (aw88399->aw_dev && aw88399->playing) + aw88399_stop(aw88399->aw_dev); + + if (aw88399->core) + aw88399->core->fw_needs_reload = true; + + ret = pm_runtime_force_suspend(dev); + if (ret) + dev_err(aw88399->dev, "Runtime force suspend failed: %d\n", ret); + + return ret; +} + +static int aw88399_hda_system_resume(struct device *dev) +{ + struct aw88399_hda *aw88399 = dev_get_drvdata(dev); + int ret; + + dev_dbg(aw88399->dev, "System resume\n"); + + if (aw88399->aw_dev) + aw88399_hw_reset(aw88399->core); + + ret = pm_runtime_force_resume(dev); + if (ret) + dev_err(aw88399->dev, "Runtime force resume failed: %d\n", ret); + + return ret; +} + +const struct dev_pm_ops aw88399_hda_pm_ops = { + RUNTIME_PM_OPS(aw88399_hda_runtime_suspend, aw88399_hda_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(aw88399_hda_system_suspend, aw88399_hda_system_resume) +}; +EXPORT_SYMBOL_NS_GPL(aw88399_hda_pm_ops, "SND_HDA_SCODEC_AW88399"); + +MODULE_DESCRIPTION("AW88399 HDA driver"); +MODULE_AUTHOR("Yakov Till "); +MODULE_AUTHOR("Marco Giunta "); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("aw88399_acf.bin"); diff --git a/sound/hda/codecs/side-codecs/aw88399_hda.h b/sound/hda/codecs/side-codecs/aw88399_hda.h new file mode 100644 index 000000000000..ca0aa5279298 --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * AW88399 HDA side codec driver + */ + +#ifndef __AW88399_HDA_H__ +#define __AW88399_HDA_H__ + +#include +#include +#include + +struct aw88399; +struct aw_device; + +struct aw88399_hda { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct aw_device *aw_dev; + struct aw88399 *core; + bool bsts_unreliable; + + const char *acpi_subsystem_id; + int index; + int channel; + + bool playing; +}; + +int aw88399_hda_probe(struct device *dev, struct regmap *regmap); +void aw88399_hda_remove(struct device *dev); + +extern const struct dev_pm_ops aw88399_hda_pm_ops; + +#endif /* __AW88399_HDA_H__ */ diff --git a/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c b/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c new file mode 100644 index 000000000000..186e13adbc6c --- /dev/null +++ b/sound/hda/codecs/side-codecs/aw88399_hda_i2c.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// AW88399 HDA I2C driver +// +// Based on cs35l41_hda_i2c.c +// + +#include +#include +#include + +#include "aw88399_hda.h" + +static int aw88399_hda_i2c_probe(struct i2c_client *clt) +{ + if (!strstr(dev_name(&clt->dev), "AWDZ8399")) + return -ENODEV; + + return aw88399_hda_probe(&clt->dev, + devm_regmap_init_i2c(clt, &aw88399_remap_config)); +} + +static void aw88399_hda_i2c_remove(struct i2c_client *clt) +{ + aw88399_hda_remove(&clt->dev); +} + +static const struct i2c_device_id aw88399_hda_i2c_id[] = { + { .name = "aw88399-hda" }, + { } +}; + +static const struct acpi_device_id aw88399_acpi_hda_match[] = { + { "AWDZ8399", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, aw88399_acpi_hda_match); + +static struct i2c_driver aw88399_hda_i2c_driver = { + .driver = { + .name = "aw88399-hda", + .acpi_match_table = aw88399_acpi_hda_match, + .pm = &aw88399_hda_pm_ops, + }, + .probe = aw88399_hda_i2c_probe, + .remove = aw88399_hda_i2c_remove, + .id_table = aw88399_hda_i2c_id, +}; +module_i2c_driver(aw88399_hda_i2c_driver); + +MODULE_DESCRIPTION("HDA AW88399 I2C driver"); +MODULE_IMPORT_NS("SND_HDA_SCODEC_AW88399"); +MODULE_AUTHOR("Yakov Till "); +MODULE_LICENSE("GPL"); diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 237059ef22f5..62296be45c55 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -170,6 +170,7 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, char *s, c; int ret = 0; + *firmware = NULL; if (spkid > -1 && ssid && amp_name) *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type], @@ -528,8 +529,8 @@ static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) { - const struct firmware *tuning_param_file = NULL; - char *tuning_param_filename = NULL; + const struct firmware *tuning_param_file __free(firmware) = NULL; + char *tuning_param_filename __free(kfree) = NULL; int ret; ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, @@ -548,19 +549,16 @@ static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_ cs35l41_set_default_tuning_params(cs35l41); } - release_firmware(tuning_param_file); - kfree(tuning_param_filename); - return ret; } static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) { - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; + const struct firmware *coeff_firmware __free(firmware) = NULL; + const struct firmware *wmfw_firmware __free(firmware) = NULL; struct cs_dsp *dsp = &cs35l41->cs_dsp; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; + char *coeff_filename __free(kfree) = NULL; + char *wmfw_filename __free(kfree) = NULL; int ret; if (!cs35l41->halo_initialized) { @@ -592,20 +590,13 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, cs35l41_hda_fw_ids[cs35l41->firmware_type]); - if (ret) - goto err; + if (ret) { + cs35l41_set_default_tuning_params(cs35l41); + return ret; + } cs35l41_hda_apply_calibration(cs35l41); - -err: - if (ret) - cs35l41_set_default_tuning_params(cs35l41); - release_firmware(wmfw_firmware); - release_firmware(coeff_firmware); - kfree(wmfw_filename); - kfree(coeff_filename); - - return ret; + return 0; } static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) @@ -1924,7 +1915,6 @@ int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id) { struct acpi_device *adev; - struct device *physdev; struct spi_device *spi; const char *sub; int ret; @@ -1936,7 +1926,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i } cs35l41->dacpi = adev; - physdev = get_device(acpi_get_first_physical_node(adev)); + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); if (!physdev) { acpi_dev_put(adev); return -ENODEV; @@ -1954,13 +1945,9 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i } ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id); - if (ret) { - put_device(physdev); + if (ret) return ret; - } out: - put_device(physdev); - cs35l41->bypass_fw = false; if (cs35l41->control_bus == SPI) { spi = to_spi_device(cs35l41->dev); diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index 78c2cf387a00..bc207ab5b020 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -527,18 +527,6 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56, base_name, NULL, NULL, "bin"); } -static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware, - char *wmfw_filename, - const struct firmware *coeff_firmware, - char *coeff_filename) -{ - release_firmware(wmfw_firmware); - kfree(wmfw_filename); - - release_firmware(coeff_firmware); - kfree(coeff_filename); -} - static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) { int ret; @@ -561,10 +549,10 @@ static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56) static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) { - const struct firmware *coeff_firmware = NULL; - const struct firmware *wmfw_firmware = NULL; - char *coeff_filename = NULL; - char *wmfw_filename = NULL; + const struct firmware *coeff_firmware __free(firmware) = NULL; + const struct firmware *wmfw_firmware __free(firmware) = NULL; + char *coeff_filename __free(kfree) = NULL; + char *wmfw_filename __free(kfree) = NULL; unsigned int preloaded_fw_ver; bool firmware_missing; int ret; @@ -606,14 +594,14 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (firmware_missing) { if (!wmfw_firmware) { dev_err(cs35l56->base.dev, ".%s file required but not found\n", "wmfw"); - goto err_fw_release; + return; } else if (!coeff_firmware) { dev_err(cs35l56->base.dev, ".%s file required but not found\n", "bin"); - goto err_fw_release; + return; } } - mutex_lock(&cs35l56->base.irq_lock); + guard(mutex)(&cs35l56->base.irq_lock); /* * If the firmware hasn't been patched it must be shutdown before @@ -624,14 +612,14 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) if (firmware_missing && (wmfw_firmware || coeff_firmware)) { ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) - goto err; + return; } ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, "misc"); if (ret) { dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret); - goto err; + return; } if (wmfw_filename) @@ -679,11 +667,6 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56) err_powered_up: if (!cs35l56->base.fw_patched) cs_dsp_power_down(&cs35l56->cs_dsp); -err: - mutex_unlock(&cs35l56->base.irq_lock); -err_fw_release: - cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename, - coeff_firmware, coeff_filename); } static void cs35l56_hda_dsp_work(struct work_struct *work) diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index 624db967f17b..6c502c34e015 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -88,7 +88,6 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) { struct gpio_desc *speaker_id; struct acpi_device *adev; - struct device *physdev; LIST_HEAD(resources); const char *sub; uint32_t subid; @@ -101,7 +100,8 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) return -ENODEV; } - physdev = get_device(acpi_get_first_physical_node(adev)); + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p); if (ret < 0) { dev_err(p->dev, "Failed to get ACPI resource.\n"); @@ -151,14 +151,12 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid) end_2563: acpi_dev_free_resource_list(&resources); strscpy(p->dev_name, hid, sizeof(p->dev_name)); - put_device(physdev); acpi_dev_put(adev); return 0; err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); acpi_dev_put(adev); return ret; diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c index 271c56a79c32..d3b2a746e1a7 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c @@ -328,7 +328,6 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, { struct tasdevice_priv *p = tas_hda->priv; struct acpi_device *adev; - struct device *physdev; u32 values[HDA_MAX_COMPONENTS]; const char *property; size_t nval; @@ -341,7 +340,9 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, } strscpy(p->dev_name, hid, sizeof(p->dev_name)); - physdev = get_device(acpi_get_first_physical_node(adev)); + + struct device *physdev __free(put_device) = + get_device(acpi_get_first_physical_node(adev)); acpi_dev_put(adev); if (!physdev) return -ENODEV; @@ -381,13 +382,11 @@ static int tas2781_read_acpi(struct tas2781_hda *tas_hda, goto err; } } - put_device(physdev); return 0; + err: dev_err(p->dev, "read acpi error, ret: %d\n", ret); - put_device(physdev); - return ret; } diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c index 5bc95d3116ff..b3f16f616396 100644 --- a/sound/hda/common/auto_parser.c +++ b/sound/hda/common/auto_parser.c @@ -601,9 +601,9 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return -1; } -/* get a unique suffix or an index number */ +/* get a unique suffix */ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, - int num_pins, int *indexp) + int num_pins) { static const char * const channel_sfx[] = { " Front", " Surround", " CLFE", " Side" @@ -615,11 +615,8 @@ static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins, return NULL; if (num_pins == 1) return ""; - if (num_pins > ARRAY_SIZE(channel_sfx)) { - if (indexp) - *indexp = i; + if (num_pins > ARRAY_SIZE(channel_sfx)) return ""; - } return channel_sfx[i]; } @@ -638,27 +635,9 @@ static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid) return ""; } -static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid, - const hda_nid_t *pins, int num_pins) -{ - int i, j, idx = 0; - - const char *pfx = check_output_pfx(codec, nid); - - i = find_idx_in_nid_list(nid, pins, num_pins); - if (i < 0) - return -1; - for (j = 0; j < i; j++) - if (pfx == check_output_pfx(codec, pins[j])) - idx++; - - return idx; -} - static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - const char *name, char *label, int maxlen, - int *indexp) + const char *name, char *label, int maxlen) { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); int attr = snd_hda_get_input_pin_attr(def_conf); @@ -671,19 +650,11 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, if (cfg) { /* try to give a unique suffix if needed */ - sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs, - indexp); + sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs); + if (!sfx) + sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs); if (!sfx) - sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs, - indexp); - if (!sfx) { - /* don't add channel suffix for Headphone controls */ - int idx = get_hp_label_index(codec, nid, cfg->hp_pins, - cfg->hp_outs); - if (idx >= 0 && indexp) - *indexp = idx; sfx = ""; - } } snprintf(label, maxlen, "%s%s%s", pfx, name, sfx); return 1; @@ -699,7 +670,6 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, * @cfg: the parsed pin configuration * @label: the string buffer to store * @maxlen: the max length of string buffer (including termination) - * @indexp: the pointer to return the index number (for multiple ctls) * * Get a label for the given pin. This function works for both input and * output pins. When @cfg is given as non-NULL, the function tries to get @@ -708,47 +678,33 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, * This function tries to give a unique label string for the pin as much as * possible. For example, when the multiple line-outs are present, it adds * the channel suffix like "Front", "Surround", etc (only when @cfg is given). - * If no unique name with a suffix is available and @indexp is non-NULL, the - * index number is stored in the pointer. */ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp) + char *label, int maxlen) { unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); const char *name = NULL; int i; bool hdmi; - if (indexp) - *indexp = 0; if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) return 0; switch (get_defcfg_device(def_conf)) { case AC_JACK_LINE_OUT: return fill_audio_out_name(codec, nid, cfg, "Line Out", - label, maxlen, indexp); + label, maxlen); case AC_JACK_SPEAKER: return fill_audio_out_name(codec, nid, cfg, "Speaker", - label, maxlen, indexp); + label, maxlen); case AC_JACK_HP_OUT: return fill_audio_out_name(codec, nid, cfg, "Headphone", - label, maxlen, indexp); + label, maxlen); case AC_JACK_SPDIF_OUT: case AC_JACK_DIG_OTHER_OUT: hdmi = is_hdmi_cfg(def_conf); name = hdmi ? "HDMI" : "SPDIF"; - if (cfg && indexp) - for (i = 0; i < cfg->dig_outs; i++) { - hda_nid_t pin = cfg->dig_out_pins[i]; - unsigned int c; - if (pin == nid) - break; - c = snd_hda_codec_get_pincfg(codec, pin); - if (hdmi == is_hdmi_cfg(c)) - (*indexp)++; - } break; default: if (cfg) { diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c index ef533770179b..7d17d773cfbf 100644 --- a/sound/hda/common/codec.c +++ b/sound/hda/common/codec.c @@ -39,6 +39,12 @@ static int call_exec_verb(struct hda_bus *bus, struct hda_codec *codec, int err; CLASS(snd_hda_power_pm, pm)(codec); + if (pm.err < 0 && !bus->core.chip_init) { + codec_warn(codec, + "Failed to send cmd 0x%x ret=[%d], hda control stopped\n", + cmd, pm.err); + return pm.err; + } guard(mutex)(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; @@ -2967,8 +2973,11 @@ static void hda_codec_pm_complete(struct device *dev) dev->power.power_state = PMSG_RESUME; if (pm_runtime_suspended(dev) && (codec->jackpoll_interval || - hda_codec_need_resume(codec) || codec->forced_resume)) + hda_codec_need_resume(codec) || codec->forced_resume || + codec->acomp_requested_resume)) { + codec->acomp_requested_resume = 0; pm_request_resume(dev); + } } static int hda_codec_pm_suspend(struct device *dev) @@ -3370,7 +3379,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, for (; knew->name; knew++) { struct snd_kcontrol *kctl; int addr = 0, idx = 0; - if (knew->iface == (__force snd_ctl_elem_iface_t)-1) + if (knew->iface == -1) continue; /* skip this codec private value */ for (;;) { kctl = snd_ctl_new1(knew, codec); diff --git a/sound/hda/common/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h index 87af3d8c02f7..3c5f3ad40074 100644 --- a/sound/hda/common/hda_auto_parser.h +++ b/sound/hda/common/hda_auto_parser.h @@ -46,7 +46,7 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec, int input); int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, const struct auto_pin_cfg *cfg, - char *label, int maxlen, int *indexp); + char *label, int maxlen); enum { INPUT_PIN_ATTR_UNUSED, /* pin not connected */ diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h index 98b2c4acebc2..947a3152fdb5 100644 --- a/sound/hda/common/hda_local.h +++ b/sound/hda/common/hda_local.h @@ -723,4 +723,11 @@ void snd_hda_codec_display_power(struct hda_codec *codec, bool enable); #define codec_dbg(codec, fmt, args...) \ dev_dbg(hda_codec_dev(codec), fmt, ##args) +/* append a suffix string safely; equivalent with strlcat() */ +static inline void hda_append_suffix(char *str, const char *suffix, size_t size) +{ + size_t len = strnlen(str, size); + strscpy(str + len, suffix, size - len); +} + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c index e0a5cc38540b..1d6b0f0e6f27 100644 --- a/sound/hda/common/jack.c +++ b/sound/hda/common/jack.c @@ -612,10 +612,10 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, if (base_name) strscpy(name, base_name, sizeof(name)); else - snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL); + snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name)); if (phantom_jack) /* Example final name: "Internal Mic Phantom Jack" */ - strncat(name, " Phantom", sizeof(name) - strlen(name) - 1); + hda_append_suffix(name, " Phantom", sizeof(name)); err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL); if (err < 0) return err; diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig index 5d6a77e68588..26ca69b09a9c 100644 --- a/sound/hda/controllers/Kconfig +++ b/sound/hda/controllers/Kconfig @@ -33,6 +33,7 @@ config SND_HDA_TEGRA config SND_HDA_CIX_IPBLOQ tristate "CIX IPBLOQ HD Audio" depends on ARCH_CIX || COMPILE_TEST + depends on ARCH_DMA_ADDR_T_64BIT select SND_HDA select SND_HDA_ALIGNED_MMIO help diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 28c55c5a2d2e..1e6d97e08fee 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -100,6 +100,8 @@ enum { #define ATIHDMI_NUM_CAPTURE 0 #define ATIHDMI_NUM_PLAYBACK 8 +/* Hygon HD Audio controller */ +#define PCI_DEVICE_ID_HYGON_18H_M05H_HDA 0x14a9 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -241,6 +243,7 @@ enum { AZX_DRIVER_ZHAOXIN, AZX_DRIVER_ZHAOXINHDMI, AZX_DRIVER_LOONGSON, + AZX_DRIVER_HYGON, AZX_DRIVER_GENERIC, AZX_NUM_DRIVERS, /* keep this as last entry */ }; @@ -360,6 +363,7 @@ static const char * const driver_short_names[] = { [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin", [AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI", [AZX_DRIVER_LOONGSON] = "HDA Loongson", + [AZX_DRIVER_HYGON] = "HDA Hygon", [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; @@ -1926,6 +1930,10 @@ static int azx_first_init(struct azx *chip) if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI) bus->polling_mode = 1; + if (chip->driver_type == AZX_DRIVER_HYGON && + chip->pci->device == PCI_DEVICE_ID_HYGON_18H_M05H_HDA) + bus->access_sdnctl_in_dword = 1; + bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio"); if (IS_ERR(bus->remap_addr)) return PTR_ERR(bus->remap_addr); @@ -2189,6 +2197,15 @@ static int azx_probe(struct pci_dev *pci, dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n"); } + /* A sanity check against wild device binding; + * here the range 0x200 is enough for the registers used at probe, + * but it doesn't mean covering all HD-audio registers + */ + if (pci_resource_len(pci, 0) < 0x200) { + dev_err(&pci->dev, "Too small PCI BAR0\n"); + return -EINVAL; + } + err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { @@ -2381,7 +2398,7 @@ static int azx_probe_continue(struct azx *chip) #ifdef CONFIG_SND_HDA_PATCH_LOADER if (patch[dev] && *patch[dev]) { - const struct firmware *fw = NULL; + const struct firmware *fw __free(firmware) = NULL; dev_info(&pci->dev, "Applying patch firmware '%s'\n", patch[dev]); @@ -2390,7 +2407,6 @@ static int azx_probe_continue(struct azx *chip) "Cannot load firmware, continue without patching\n"); } else { err = snd_hda_load_patch(&chip->bus, fw->size, fw->data); - release_firmware(fw); if (err < 0) goto out_free; } @@ -2838,6 +2854,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL }, + /* Hygon HDAudio */ + { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_HDA), + .driver_data = AZX_DRIVER_HYGON | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_NO_MSI }, { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c index 69e11d62bbfa..78855ac357c6 100644 --- a/sound/hda/core/controller.c +++ b/sound/hda/core/controller.c @@ -511,7 +511,10 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) { unsigned long timeout; - snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); + if (bus->access_sdnctl_in_dword) + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); + else + snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); timeout = jiffies + msecs_to_jiffies(100); while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) @@ -576,7 +579,10 @@ static void azx_int_disable(struct hdac_bus *bus) /* disable interrupts in stream descriptor */ list_for_each_entry(azx_dev, &bus->stream_list, list) - snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_INT_MASK, 0); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); /* disable SIE for all streams & disable controller CIE and GIE */ snd_hdac_chip_writel(bus, INTCTL, 0); @@ -682,6 +688,11 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; + if (sd_status & (SD_INT_FIFO_ERR | SD_INT_DESC_ERR)) { + dev_warn_ratelimited(bus->dev, + "stream %u dma error: 0x%02x\n", + azx_dev->index, sd_status); + } if ((!azx_dev->substream && !azx_dev->cstream) || !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) continue; @@ -713,7 +724,7 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) BDL_SIZE, &s->bdl); num_streams++; if (err < 0) - return -ENOMEM; + goto error_bdl; } if (WARN_ON(!num_streams)) @@ -722,12 +733,24 @@ int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) err = snd_dma_alloc_pages(dma_type, bus->dev, num_streams * 8, &bus->posbuf); if (err < 0) - return -ENOMEM; + goto error_bdl; list_for_each_entry(s, &bus->stream_list, list) s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb); + err = snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb); + if (err < 0) + goto error_posbuf; + return 0; + +error_posbuf: + snd_dma_free_pages(&bus->posbuf); +error_bdl: + list_for_each_entry(s, &bus->stream_list, list) { + if (s->bdl.area) + snd_dma_free_pages(&s->bdl); + } + return -ENOMEM; } EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c index 160c8d0453b0..832494035f0a 100644 --- a/sound/hda/core/device.c +++ b/sound/hda/core/device.c @@ -765,7 +765,7 @@ unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subfor params_set_format(¶ms, snd_hdac_format_normalize(format)); snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT), - (__force unsigned int)subformat); + subformat); bits = snd_pcm_hw_params_bits(¶ms); if (maxbits) diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c index 4c7506d49f55..517bd151fcc3 100644 --- a/sound/hda/core/ext/stream.c +++ b/sound/hda/core/ext/stream.c @@ -210,8 +210,8 @@ void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream) break; udelay(3); } while (--timeout); - val &= ~AZX_PPLCCTL_STRST; - writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, + AZX_PPLCCTL_STRST, 0); udelay(3); timeout = 50; diff --git a/sound/hda/core/hda_bus_type.c b/sound/hda/core/hda_bus_type.c index a4afd41b6f84..e1e986a8b5b5 100644 --- a/sound/hda/core/hda_bus_type.c +++ b/sound/hda/core/hda_bus_type.c @@ -39,10 +39,7 @@ EXPORT_SYMBOL_GPL(hdac_get_device_id); static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv) { - if (hdac_get_device_id(dev, drv)) - return 1; - else - return 0; + return !!hdac_get_device_id(dev, drv); } static int hda_bus_match(struct device *dev, const struct device_driver *drv) @@ -59,9 +56,7 @@ static int hda_bus_match(struct device *dev, const struct device_driver *drv) */ if (hdrv->match) return hdrv->match(hdev, hdrv); - else - return hdac_codec_match(hdev, hdrv); - return 1; + return hdac_codec_match(hdev, hdrv); } static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env) diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c index b471a038b314..0091c4ef6486 100644 --- a/sound/hda/core/stream.c +++ b/sound/hda/core/stream.c @@ -146,8 +146,12 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev) stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream); else stripe_ctl = 0; - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, - stripe_ctl); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); } /* set DMA start and interrupt mask */ if (bus->access_sdnctl_in_dword) @@ -166,11 +170,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start); */ static void snd_hdac_stream_clear(struct hdac_stream *azx_dev) { - snd_hdac_stream_updateb(azx_dev, SD_CTL, - SD_CTL_DMA_START | SD_INT_MASK, 0); - snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ - if (azx_dev->stripe) - snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + struct hdac_bus *bus = azx_dev->bus; + + if (bus->access_sdnctl_in_dword) { + snd_hdac_stream_updatel(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + if (azx_dev->stripe) + snd_hdac_stream_updatel(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + } else { + snd_hdac_stream_updateb(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + if (azx_dev->stripe) + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); + } + azx_dev->running = false; } @@ -225,12 +240,16 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) { unsigned char val; int dma_run_state; + struct hdac_bus *bus = azx_dev->bus; snd_hdac_stream_clear(azx_dev); dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; - snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); /* wait for hardware to report that the stream entered reset */ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300); @@ -238,7 +257,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) if (azx_dev->bus->dma_stop_delay && dma_run_state) udelay(azx_dev->bus->dma_stop_delay); - snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); + if (bus->access_sdnctl_in_dword) + snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0); /* wait for hardware to report that the stream is out of reset */ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300); diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index 0d5f4461a7bc..eac6e22362cc 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -367,7 +367,8 @@ static int snd_msnd_init_sma(struct snd_msnd *chip) static int upload_dsp_code(struct snd_card *card) { struct snd_msnd *chip = card->private_data; - const struct firmware *init_fw = NULL, *perm_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; + const struct firmware *perm_fw __free(firmware) = NULL; int err; outb(HPBLKSEL_0, chip->io + HP_BLKS); @@ -375,28 +376,21 @@ static int upload_dsp_code(struct snd_card *card) err = request_firmware(&init_fw, INITCODEFILE, card->dev); if (err < 0) { dev_err(card->dev, LOGNAME ": Error loading " INITCODEFILE); - goto cleanup1; + return err; } err = request_firmware(&perm_fw, PERMCODEFILE, card->dev); if (err < 0) { dev_err(card->dev, LOGNAME ": Error loading " PERMCODEFILE); - goto cleanup; + return err; } memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size); if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) { dev_warn(card->dev, LOGNAME ": Error uploading to DSP\n"); - err = -ENODEV; - goto cleanup; + return -ENODEV; } dev_info(card->dev, LOGNAME ": DSP firmware uploaded\n"); - err = 0; - -cleanup: - release_firmware(perm_fw); -cleanup1: - release_firmware(init_fw); - return err; + return 0; } #ifdef MSND_CLASSIC diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index ce8a59650e38..9b615b588cdf 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -526,7 +526,7 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data, static int sscape_upload_bootblock(struct snd_card *card) { struct soundscape *sscape = get_card_soundscape(card); - const struct firmware *init_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; int data = 0; int ret; @@ -537,8 +537,6 @@ static int sscape_upload_bootblock(struct snd_card *card) } ret = upload_dma_data(sscape, init_fw->data, init_fw->size); - release_firmware(init_fw); - guard(spinlock_irqsave)(&sscape->lock); if (ret == 0) data = host_read_ctrl_unsafe(sscape->io_base, 100); @@ -562,7 +560,7 @@ static int sscape_upload_bootblock(struct snd_card *card) static int sscape_upload_microcode(struct snd_card *card, int version) { struct soundscape *sscape = get_card_soundscape(card); - const struct firmware *init_fw = NULL; + const struct firmware *init_fw __free(firmware) = NULL; char name[14]; int err; @@ -579,8 +577,6 @@ static int sscape_upload_microcode(struct snd_card *card, int version) dev_info(card->dev, "sscape: MIDI firmware loaded %zu KBs\n", init_fw->size >> 10); - release_firmware(init_fw); - return err; } diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index beca35ce04f3..6d95ea338ea9 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -232,41 +232,32 @@ snd_wavefront_fx_start (snd_wavefront_t *dev) { unsigned int i; int err; - const struct firmware *firmware = NULL; + const struct firmware *firmware __free(firmware) = NULL; if (dev->fx_initialized) return 0; err = request_firmware(&firmware, "yamaha/yss225_registers.bin", dev->card->dev); - if (err < 0) { - err = -1; - goto out; - } + if (err < 0) + return -1; for (i = 0; i + 1 < firmware->size; i += 2) { if (firmware->data[i] >= 8 && firmware->data[i] < 16) { outb(firmware->data[i + 1], dev->base + firmware->data[i]); } else if (firmware->data[i] == WAIT_IDLE) { - if (!wavefront_fx_idle(dev)) { - err = -1; - goto out; - } + if (!wavefront_fx_idle(dev)) + return -1; } else { dev_err(dev->card->dev, "invalid address in register data\n"); - err = -1; - goto out; + return -1; } } dev->fx_initialized = 1; - err = 0; - -out: - release_firmware(firmware); - return err; + return 0; } MODULE_FIRMWARE("yamaha/yss225_registers.bin"); diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 2f57a6795d22..0c8f5cf26455 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -2053,7 +2053,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path) const unsigned char *buf; int len, err; int section_cnt_downloaded = 0; - const struct firmware *firmware; + const struct firmware *firmware __free(firmware) = NULL; err = request_firmware(&firmware, path, dev->card->dev); if (err < 0) { @@ -2108,11 +2108,9 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path) section_cnt_downloaded++; } - release_firmware(firmware); return 0; failure: - release_firmware(firmware); dev_err(dev->card->dev, "firmware download failed!!!\n"); return 1; } diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 4dbc79899c09..b78e96caef9d 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -280,7 +280,7 @@ static void print_hwparams(struct snd_pcm_substream *substream, snd_pcm_format_width(params_format(p)) / 8); } -#define INVALID_FORMAT (__force snd_pcm_format_t)(-1) +#define INVALID_FORMAT -1 static const snd_pcm_format_t hpi_to_alsa_formats[] = { INVALID_FORMAT, /* INVALID */ diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index c8d1518ee3e7..fd7fe9dba0b8 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -537,6 +537,11 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, hr1.size = sizeof(hr1); error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0); + if (error) { + HPI_DEBUG_LOG(ERROR, "message transport error %d\n", + error); + return error; + } if (hr0.error) { HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error); return hr0.error; diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c index b1b5a131f626..6d2c27e47d07 100644 --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -23,7 +23,7 @@ struct dsp_code_private { short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, u32 *os_error_code) { - const struct firmware *firmware; + const struct firmware *firmware __free(firmware) = NULL; struct pci_dev *dev = os_data; struct code_header header; char fw_name[20]; @@ -37,11 +37,11 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, if (err || !firmware) { dev_err(&dev->dev, "%d, request_firmware failed for %s\n", err, fw_name); - goto error1; + goto error; } if (firmware->size < sizeof(header)) { dev_err(&dev->dev, "Header size too small %s\n", fw_name); - goto error2; + goto error; } memcpy(&header, firmware->data, sizeof(header)); @@ -51,7 +51,7 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dev_err(&dev->dev, "Invalid firmware header size %d != file %zd\n", header.size, firmware->size); - goto error2; + goto error; } if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) { @@ -59,7 +59,7 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dev_err(&dev->dev, "Incompatible firmware version DSP image %X != Driver %X\n", header.version, HPI_VER); - goto error2; + goto error; } if (header.version != HPI_VER) { @@ -72,19 +72,17 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, dsp_code->pvt = kmalloc_obj(*dsp_code->pvt); if (!dsp_code->pvt) { err_ret = HPI_ERROR_MEMORY_ALLOC; - goto error2; + goto error; } dsp_code->pvt->dev = dev; - dsp_code->pvt->firmware = firmware; + dsp_code->pvt->firmware = no_free_ptr(firmware); dsp_code->header = header; dsp_code->block_length = header.size / sizeof(u32); dsp_code->word_count = sizeof(header) / sizeof(u32); return 0; -error2: - release_firmware(firmware); -error1: +error: dsp_code->block_length = 0; return err_ret; } diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 9de9ae7032b8..ec2da792e1c8 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -385,8 +385,7 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { memlen = pci_resource_len(pci_dev, idx); pci.ap_mem_base[idx] = - ioremap(pci_resource_start(pci_dev, idx), - memlen); + pcim_iomap(pci_dev, idx, memlen); if (!pci.ap_mem_base[idx]) { HPI_DEBUG_LOG(ERROR, "ioremap failed, aborting\n"); @@ -509,13 +508,6 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, return 0; err: - while (--idx >= 0) { - if (pci.ap_mem_base[idx]) { - iounmap(pci.ap_mem_base[idx]); - pci.ap_mem_base[idx] = NULL; - } - } - if (adapter.p_buffer) { adapter.buffer_size = 0; vfree(adapter.p_buffer); @@ -527,14 +519,11 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, void asihpi_adapter_remove(struct pci_dev *pci_dev) { - int idx; struct hpi_message hm; struct hpi_response hr; struct hpi_adapter *pa; - struct hpi_pci pci; pa = pci_get_drvdata(pci_dev); - pci = pa->adapter->pci; /* Disable IRQ generation on DSP side */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, @@ -550,10 +539,6 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) hm.adapter_index = pa->adapter->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - /* unmap PCI memory space, mapped during device init. */ - for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx) - iounmap(pci.ap_mem_base[idx]); - if (pa->irq) free_irq(pa->irq, pa); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 1c11023184ac..19a6927c079d 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -387,7 +387,7 @@ static int load_firmware(struct snd_cs46xx *chip, unsigned int nums, fwlen, fwsize; const __le32 *fwdat; struct dsp_module_desc *module = NULL; - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; char fw_path[32]; sprintf(fw_path, "cs46xx/%s", fw_name); @@ -395,10 +395,8 @@ static int load_firmware(struct snd_cs46xx *chip, if (err < 0) return err; fwsize = fw->size / 4; - if (fwsize < 2) { - err = -EINVAL; - goto error; - } + if (fwsize < 2) + return -EINVAL; err = -ENOMEM; module = kzalloc_obj(*module); @@ -454,14 +452,12 @@ static int load_firmware(struct snd_cs46xx *chip, } *module_ret = module; - release_firmware(fw); return 0; error_inval: err = -EINVAL; error: free_module_desc(module); - release_firmware(fw); return err; } @@ -500,22 +496,18 @@ MODULE_FIRMWARE("cs46xx/ba1"); static int load_firmware(struct snd_cs46xx *chip) { - const struct firmware *fw; + const struct firmware *fw __free(firmware) = NULL; int i, size, err; err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev); if (err < 0) return err; - if (fw->size != sizeof(*chip->ba1)) { - err = -EINVAL; - goto error; - } + if (fw->size != sizeof(*chip->ba1)) + return -EINVAL; chip->ba1 = vmalloc(sizeof(*chip->ba1)); - if (!chip->ba1) { - err = -ENOMEM; - goto error; - } + if (!chip->ba1) + return -ENOMEM; memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1)); @@ -524,11 +516,9 @@ static int load_firmware(struct snd_cs46xx *chip) for (i = 0; i < BA1_MEMORY_COUNT; i++) size += chip->ba1->memory[i].size; if (size > BA1_DWORD_SIZE * 4) - err = -EINVAL; + return -EINVAL; - error: - release_firmware(fw); - return err; + return 0; } static __maybe_unused int snd_cs46xx_download_image(struct snd_cs46xx *chip) diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 08e0556bf161..49cabb2eb2b7 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -990,7 +990,7 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu, i < icode->gpr_list_control_count) { memset(gctl, 0, sizeof(*gctl)); id = &ctl->kcontrol->id; - gctl->id.iface = (__force int)id->iface; + gctl->id.iface = id->iface; strscpy(gctl->id.name, id->name, sizeof(gctl->id.name)); gctl->id.index = id->index; gctl->id.device = id->device; @@ -1156,7 +1156,7 @@ static void snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; if (high_res_gpr_volume) { @@ -1180,7 +1180,7 @@ static void snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval, int defval_hr) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; if (high_res_gpr_volume) { @@ -1205,7 +1205,7 @@ static void snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 1; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; @@ -1218,7 +1218,7 @@ static void snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl, const char *name, int gpr, int defval) { - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, name); ctl->vcount = ctl->count = 2; ctl->gpr[0] = gpr + 0; ctl->value[0] = defval; @@ -1542,7 +1542,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) * Process tone control */ ctl = &controls[nctl + 0]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; @@ -1551,7 +1551,7 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) ctl->value[0] = ctl->value[1] = 20; ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[nctl + 1]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; @@ -2137,7 +2137,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) * Process tone control */ ctl = &controls[i + 0]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Bass"); ctl->vcount = 2; ctl->count = 10; @@ -2147,7 +2147,7 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) ctl->tlv = snd_emu10k1_bass_treble_db_scale; ctl->translation = EMU10K1_GPR_TRANSLATION_BASS; ctl = &controls[i + 1]; - ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER; + ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strscpy(ctl->id.name, "Tone Control - Treble"); ctl->vcount = 2; ctl->count = 10; diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index d16acf83668a..f4d8402f835f 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1980,7 +1980,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) __maybe_unused unsigned ioport_size; __maybe_unused unsigned iomem2_size; struct snd_korg1212 *korg1212 = card->private_data; - const struct firmware *dsp_code; + const struct firmware *dsp_code __free(firmware) = NULL; err = pcim_enable_device(pci); if (err < 0) @@ -2147,10 +2147,8 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) korg1212->dma_dsp = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, dsp_code->size); - if (!korg1212->dma_dsp) { - release_firmware(dsp_code); + if (!korg1212->dma_dsp) return -ENOMEM; - } K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", korg1212->dma_dsp->area, korg1212->dma_dsp->addr, dsp_code->size, @@ -2158,8 +2156,6 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci) memcpy(korg1212->dma_dsp->area, dsp_code->data, dsp_code->size); - release_firmware(dsp_code); - rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_RebootCard, 0, 0, 0, 0); if (rc) diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 439e3ff05fe1..aa8d5bae30d0 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -560,12 +560,11 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr) "miXart8.xlx", "miXart8.elf", "miXart8AES.xlx" }; char path[32]; - - const struct firmware *fw_entry; int i, err; for (i = 0; i < 3; i++) { sprintf(path, "mixart/%s", fw_files[i]); + const struct firmware *fw_entry __free(firmware) = NULL; if (request_firmware(&fw_entry, path, &mgr->pci->dev)) { dev_err(&mgr->pci->dev, "miXart: can't load firmware %s\n", path); @@ -573,7 +572,6 @@ int snd_mixart_setup_firmware(struct mixart_mgr *mgr) } /* fake hwdep dsp record */ err = mixart_dsp_load(mgr, i, fw_entry); - release_firmware(fw_entry); if (err < 0) return err; mgr->dsp_loaded |= 1 << i; diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c index 249805065f61..54f3950579aa 100644 --- a/sound/pci/pcxhr/pcxhr_hwdep.c +++ b/sound/pci/pcxhr/pcxhr_hwdep.c @@ -367,7 +367,6 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) }; char path[32]; - const struct firmware *fw_entry; int i, err; int fw_set = mgr->fw_file_set; @@ -375,6 +374,7 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) if (!fw_files[fw_set][i]) continue; sprintf(path, "pcxhr/%s", fw_files[fw_set][i]); + const struct firmware *fw_entry __free(firmware) = NULL; if (request_firmware(&fw_entry, path, &mgr->pci->dev)) { dev_err(&mgr->pci->dev, "pcxhr: can't load firmware %s\n", @@ -383,7 +383,6 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr) } /* fake hwdep dsp record */ err = pcxhr_dsp_load(mgr, i, fw_entry); - release_firmware(fw_entry); if (err < 0) return err; mgr->dsp_loaded |= 1 << i; diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index 4be085d27712..55c1b11a2900 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1260,16 +1260,16 @@ static int sis_chip_create(struct snd_card *card, sis->irq = -1; sis->ioport = pci_resource_start(pci, 0); - rc = pcim_request_all_regions(pci, "SiS7019"); + rc = pcim_request_region(pci, 0, "SiS7019"); if (rc) { - dev_err(&pci->dev, "unable request regions\n"); + dev_err(&pci->dev, "unable request I/O region\n"); return rc; } - sis->ioaddr = devm_ioremap(&pci->dev, pci_resource_start(pci, 1), 0x4000); - if (!sis->ioaddr) { + sis->ioaddr = pcim_iomap_region(pci, 1, "SiS7019"); + if (IS_ERR(sis->ioaddr)) { dev_err(&pci->dev, "unable to remap MMIO, aborting\n"); - return -EIO; + return PTR_ERR(sis->ioaddr); } rc = sis_alloc_suspend(sis); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 9b84d3fb9eaf..b32f84ac17cc 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -573,24 +573,18 @@ static inline unsigned int calc_linear_pos(struct via82xx_modem *chip, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count); #endif - if (count && size < count) { + if (! count) + /* bogus count 0 on the DMA boundary? */ + res = viadev->idx_table[idx].offset; + else + /* count register returns full size + * when end of buffer is reached + */ + res = viadev->idx_table[idx].offset + size; + if (check_invalid_pos(viadev, res)) { dev_dbg(chip->card->dev, - "invalid via82xx_cur_ptr, using last valid pointer\n"); + "invalid via82xx_cur_ptr (2), using last valid pointer\n"); res = viadev->lastpos; - } else { - if (! count) - /* bogus count 0 on the DMA boundary? */ - res = viadev->idx_table[idx].offset; - else - /* count register returns full size - * when end of buffer is reached - */ - res = viadev->idx_table[idx].offset + size; - if (check_invalid_pos(viadev, res)) { - dev_dbg(chip->card->dev, - "invalid via82xx_cur_ptr (2), using last valid pointer\n"); - res = viadev->lastpos; - } } } viadev->lastpos = res; /* remember the last position */ diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 8196e1bf0416..078fc7fc08f9 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -518,8 +518,9 @@ static const struct snd_kcontrol_new snd_aica_pcmvolume_control = { static int load_aica_firmware(void) { int err; - const struct firmware *fw_entry; spu_reset(); + + const struct firmware *fw_entry __free(firmware) = NULL; err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); if (unlikely(err)) return err; @@ -527,7 +528,6 @@ static int load_aica_firmware(void) spu_disable(); spu_memload(0, fw_entry->data, fw_entry->size); spu_enable(); - release_firmware(fw_entry); return err; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e4d139108d72..d3298dcbac08 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -726,6 +726,10 @@ config SND_SOC_AW87390 sound quality, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. +config SND_SOC_AW88399_LIB + tristate + select SND_SOC_AW88395_LIB + config SND_SOC_AW88399 tristate "Soc Audio for awinic aw88399" depends on I2C @@ -733,6 +737,7 @@ config SND_SOC_AW88399 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB + select SND_SOC_AW88399_LIB help This option enables support for aw88399 Smart PA. The awinic AW88399 is an I2S/TDM input, high efficiency diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aa0396e5b575..d2a689006d69 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,6 +55,7 @@ snd-soc-aw88395-y := aw88395/aw88395.o snd-soc-aw88166-y := aw88166.o snd-soc-aw88261-y := aw88261.o snd-soc-aw88399-y := aw88399.o +snd-soc-aw88399-lib-y := aw88399-lib.o snd-soc-bd28623-y := bd28623.o snd-soc-bt-sco-y := bt-sco.o snd-soc-chv3-codec-y := chv3-codec.o @@ -496,6 +497,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o +obj-$(CONFIG_SND_SOC_AW88399_LIB) += snd-soc-aw88399-lib.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c new file mode 100644 index 000000000000..5c7982891def --- /dev/null +++ b/sound/soc/codecs/aw88399-lib.c @@ -0,0 +1,1411 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88399-lib.c -- AW88399 Common functions for ASoC and HDA audio drivers +// +// Copyright (c) 2023 AWINIC Technology CO., LTD +// +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include +#include +#include "aw88395/aw88395_device.h" + +const struct regmap_config aw88399_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88399_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; +EXPORT_SYMBOL_GPL(aw88399_remap_config); + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88399_CCO_MUX_MASK); + if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_check_syspll); + +static int aw_dev_check_sysst(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); + if (ret) + return ret; + + if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) + check_val = AW88399_BIT_SYSST_NOSWS_CHECK; + else + check_val = AW88399_BIT_SYSST_SWS_CHECK; + + /* + * On some hardware the BSTS (boost-finished) status bit does not + * reliably assert even when audio output is working normally. + * Allow per-instance bypass when flagged by the side-codec driver. + */ + if (aw88399->bsts_unreliable) + check_val &= ~AW88399_BSTS_FINISHED_VALUE; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + + if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed\n", __func__); +} +EXPORT_SYMBOL_GPL(aw_dev_dsp_enable); + +static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) +{ + uint16_t icalkh_val, icalkl_val, icalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); + if (ret) + return ret; + icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); + if (ret) + return ret; + icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + icalk_val = icalkh_val & icalkl_val; + else + icalk_val = icalkh_val | icalkl_val; + + if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) + icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; + *icalk = (int16_t)icalk_val; + + return 0; +} + +static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); + if (ret) + return ret; + + vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); + if (ret) + return ret; + + vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = vcalkh_val & vcalkl_val; + else + vcalk_val = vcalkh_val | vcalkl_val; + + if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) + vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); + if (ret) + return ret; + vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); + if (ret) + return ret; + vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + else + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + + if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) + vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; + + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw_dev_set_vcalb(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int vsense_select, vsense_value; + int32_t ical_k, vcal_k, vcalb; + int16_t icalk, vcalk; + uint16_t reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); + if (ret) + return ret; + + vsense_select = vsense_value & (~AW88399_VDSEL_MASK); + + ret = aw88399_dev_get_icalk(aw88399, &icalk); + if (ret) { + dev_err(aw_dev->dev, "get icalk failed\n"); + return ret; + } + + ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; + + switch (vsense_select) { + case AW88399_DEV_VDSEL_VSENSE: + ret = aw88399_dev_get_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * + ical_k / vcal_k * aw88399->vcalb_init_val; + break; + case AW88399_DEV_VDSEL_DAC: + ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / + AW88399_ISCAL_DAC_FACTOR * ical_k / + vcal_k * aw88399->vcalb_init_val; + break; + default: + dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); + ret = -EINVAL; + break; + } + if (ret) + return ret; + + vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; + reg_val = (uint32_t)vcalb; + + regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); + + return 0; +} + +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + uint16_t re_lbits, re_hbits; + u32 cali_re; + int ret; + + if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || + (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) + return -EINVAL; + + cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); + + re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; + re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; + + ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); + if (ret) + dev_err(aw_dev->dev, "set cali re error"); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_update_cali_re); + +static int aw_dev_fw_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, fw_len_val; + unsigned int reg_val; + int ret; + + /* calculate fw_end_addr */ + fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; + + /* write fw_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, fw_len_val); + if (ret) + return ret; + /* enable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); + + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + + /* read crc check result */ + regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", + __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, cfg_len_val; + unsigned int reg_val; + int ret; + + /* calculate cfg end addr */ + cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; + + /* write cfg_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); + if (ret) + return ret; + + /* enable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); + if (ret) + return ret; + + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + /* read crc check result */ + ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", + check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_hw_crc_check(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); + if (ret) + return ret; + + ret = aw_dev_fw_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "fw_crc_check failed\n"); + goto crc_check_failed; + } + + ret = aw_dev_cfg_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "cfg_crc_check failed\n"); + goto crc_check_failed; + } + + ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); + if (ret) + return ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + + return ret; + +crc_check_failed: + regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88399_WDT_CNT_MASK))) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_get_dsp_status); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88399_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88399_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value; + int ret; + + real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); + + ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88399_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } +} +EXPORT_SYMBOL_GPL(aw88399_dev_mute); + +static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + + if (dither) + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); +} + +static int aw88399_dev_start(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + if (aw_dev->status == AW88399_DEV_PW_ON) { + dev_dbg(aw_dev->dev, "already power on"); + return 0; + } + + aw88399_dev_set_dither(aw88399, false); + + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw88399); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { + ret = aw_dev_hw_crc_check(aw88399); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + aw_dev_dsp_enable(aw_dev, false); + aw_dev_set_vcalb(aw88399); + aw_dev_update_cali_re(&aw_dev->cali_desc); + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) + aw88399_dev_set_dither(aw88399, true); + + /* close mute */ + aw88399_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88399_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88399_DEV_PW_OFF; + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + u32 tmp_len; + int i, ret; + + ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); + if (ret) + return ret; + + for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { + tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); + ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88399_DSP_RE_SHIFT); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); + if (ret) + return ret; + + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_get_ra(&aw_dev->cali_desc); + + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + + aw_dev->dsp_fw_len = len; + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); + + return ret; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + /* read dsp_rom_check_reg */ + aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { + dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", + reg_val, AW88399_DSP_ROM_CHECK_DATA); + return -EPERM; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, + AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); + aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); + return -EPERM; + } + + return 0; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88399_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_DAPHCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88399_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_OSCCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88399_RCV_MODE; + else + profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; +} + +static int aw_dev_update_reg_container(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + u16 read_vol, reg_val; + int data_len, i, ret; + int16_t *reg_data; + u8 reg_addr; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88399_DSPVCALB_REG) { + aw88399->vcalb_init_val = reg_val; + continue; + } + + if (reg_addr == AW88399_SYSCTRL_REG) { + if (reg_val & (~AW88399_DSPBY_MASK)) + aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; + else + aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; + + reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | + AW88399_DSPBY_MASK); + reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | + AW88399_DSPBY_BYPASS_VALUE); + } + + if (reg_addr == AW88399_I2SCTRL3_REG) { + reg_val &= AW88399_I2STXEN_MASK; + reg_val |= AW88399_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88399_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88399_VOL_MASK)) >> + AW88399_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + if (reg_addr == AW88399_DBGCTRL_REG) { + if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) + aw88399->check_val = AW_EF_OR_CHECK; + else + aw88399->check_val = AW_EF_AND_CHECK; + + aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); + } + + if (reg_addr == AW88399_CRCCTRL_REG) + aw88399->crc_init_val = reg_val; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + return ret; + } + + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + else + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + + return 0; +} + +static int aw_dev_reg_update(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw88399, data, len); + if (ret) + dev_err(aw88399->aw_pa->dev, "reg update failed"); + + return ret; +} + +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_dev_get_prof_name); + +static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88399_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw88399, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw88399_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + if (up_dsp_fw_en) { + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + return ret; +} + +static void aw88399_start_pa(struct aw88399 *aw88399) +{ + int ret, i; + + for (i = 0; i < AW88399_START_RETRIES; i++) { + ret = aw88399_dev_start(aw88399); + if (ret) { + dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); + ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_dbg(aw88399->aw_pa->dev, "start success\n"); + break; + } + } +} + +void aw88399_startup_work(struct work_struct *work) +{ + struct aw88399 *aw88399 = + container_of(work, struct aw88399, start_work.work); + + mutex_lock(&aw88399->lock); + aw88399_start_pa(aw88399); + mutex_unlock(&aw88399->lock); +} +EXPORT_SYMBOL_GPL(aw88399_startup_work); + +void aw88399_start(struct aw88399 *aw88399, bool sync_start) +{ + int ret; + + if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) + return; + + if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) + return; + + ret = aw88399_dev_fw_update(aw88399, aw88399->fw_needs_reload ? + AW88399_DSP_FW_UPDATE_ON : AW88399_DSP_FW_UPDATE_OFF, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed."); + return; + } + + aw88399->fw_needs_reload = false; + + if (sync_start == AW88399_SYNC_START) + aw88399_start_pa(aw88399); + else + queue_delayed_work(system_dfl_wq, + &aw88399->start_work, + AW88399_START_WORK_DELAY_MS); +} +EXPORT_SYMBOL_GPL(aw88399_start); + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + if (reg_val & AW88399_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +int aw88399_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st; + + if (aw_dev->status == AW88399_DEV_PW_OFF) { + dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88399_DEV_PW_OFF; + + aw88399_dev_mute(aw_dev, true); + usleep_range(AW88399_4000_US, AW88399_4000_US + 100); + + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + int_st = aw_dev_check_sysint(aw_dev); + + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_amppd(aw_dev, true); + + if (int_st) { + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + } + + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_stop); + +static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88399_1000_US / 10; + aw_dev->fade_out_time = AW88399_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + aw88399_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +int aw88399_request_firmware_file(struct aw88399 *aw88399) +{ + const struct firmware *cont = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); + if (ret) { + dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", + AW88399_ACF_FILE, cont ? cont->size : 0); + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw88399->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { + dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + mutex_lock(&aw88399->lock); + /* aw device init */ + ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); + if (ret) + dev_err(aw88399->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88399->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aw88399_request_firmware_file); + +void aw88399_hw_reset(struct aw88399 *aw88399) +{ + if (aw88399->reset_gpio) { + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 0); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + } +} +EXPORT_SYMBOL_GPL(aw88399_hw_reset); + +static void aw88399_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + int ret; + + ret = of_property_read_u32(np, "awinic,audio-channel", &channel_value); + if (ret) { + /* + * On ACPI systems, DT properties don't exist. Derive channel + * from I2C address: 0x34 -> channel 0 (left), 0x35 -> channel 1 (right) + */ + aw_dev->channel = aw_dev->i2c->addr - 0x34; + dev_dbg(aw_dev->dev, + "DT channel property not found, using I2C address-based channel %d (addr 0x%02x)\n", + aw_dev->channel, aw_dev->i2c->addr); + return; + } + aw_dev->channel = channel_value; +} + +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88399_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + aw88399->aw_pa = aw_dev; + + aw_dev->i2c = i2c; + aw_dev->dev = &i2c->dev; + aw_dev->regmap = regmap; + mutex_init(&aw_dev->dsp_lock); + + aw_dev->chip_id = chip_id; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = AW88399_DEV_DEFAULT_CH; + aw_dev->fw_status = AW88399_DEV_FW_FAILED; + + aw_dev->fade_step = AW88399_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; + + aw88399_parse_channel_dt(aw_dev); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_init); + +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel) +{ + aw88399->aw_pa->channel = channel; +} +EXPORT_SYMBOL_GPL(aw88399_dev_set_channel); + +MODULE_DESCRIPTION("AW88399 common device library"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index b2ec3503f7e2..13495e71ad0e 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -10,1217 +10,12 @@ #include #include #include -#include -#include #include #include #include #include "aw88399.h" #include "aw88395/aw88395_device.h" -static const struct regmap_config aw88399_remap_config = { - .val_bits = 16, - .reg_bits = 8, - .max_register = AW88399_REG_MAX, - .reg_format_endian = REGMAP_ENDIAN_LITTLE, - .val_format_endian = REGMAP_ENDIAN_BIG, -}; - -static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) -{ - int ret; - - if (pwd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); - if (ret) - dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); - else - *int_status = reg_val; - - dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); -} - -static void aw_dev_clear_int_status(struct aw_device *aw_dev) -{ - u16 int_status; - - /* read int status and clear */ - aw_dev_get_int_status(aw_dev, &int_status); - /* make sure int status is clear */ - aw_dev_get_int_status(aw_dev, &int_status); - if (int_status) - dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); -} - -static int aw_dev_get_iis_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { - dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) -{ - int ret, i; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode1 iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); - if (ret) - return ret; - - reg_val &= (~AW88399_CCO_MUX_MASK); - if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { - dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); - return -EPERM; - } - - /* change mode2 */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); - if (ret) - return ret; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - - /* change mode1 */ - regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); - if (ret == 0) { - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - } - - return ret; -} - -static int aw_dev_check_syspll(struct aw_device *aw_dev) -{ - int ret; - - ret = aw_dev_check_mode1_pll(aw_dev); - if (ret) { - dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); - ret = aw_dev_check_mode2_pll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 check iis failed"); - return ret; - } - } - - return 0; -} - -static int aw_dev_check_sysst(struct aw_device *aw_dev) -{ - unsigned int check_val; - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); - if (ret) - return ret; - - if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) - check_val = AW88399_BIT_SYSST_NOSWS_CHECK; - else - check_val = AW88399_BIT_SYSST_SWS_CHECK; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - - if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { - dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", - i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) -{ - int ret; - - if (amppd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) -{ - int ret; - - if (is_enable) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed\n", __func__); -} - -static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) -{ - uint16_t icalkh_val, icalkl_val, icalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); - if (ret) - return ret; - icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); - if (ret) - return ret; - icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - icalk_val = icalkh_val & icalkl_val; - else - icalk_val = icalkh_val | icalkl_val; - - if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) - icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; - *icalk = (int16_t)icalk_val; - - return 0; -} - -static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); - if (ret) - return ret; - - vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); - if (ret) - return ret; - - vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = vcalkh_val & vcalkl_val; - else - vcalk_val = vcalkh_val | vcalkl_val; - - if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) - vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); - if (ret) - return ret; - vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); - if (ret) - return ret; - vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - else - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - - if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) - vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; - - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw_dev_set_vcalb(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int vsense_select, vsense_value; - int32_t ical_k, vcal_k, vcalb; - int16_t icalk, vcalk; - uint16_t reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); - if (ret) - return ret; - - vsense_select = vsense_value & (~AW88399_VDSEL_MASK); - - ret = aw88399_dev_get_icalk(aw88399, &icalk); - if (ret) { - dev_err(aw_dev->dev, "get icalk failed\n"); - return ret; - } - - ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; - - switch (vsense_select) { - case AW88399_DEV_VDSEL_VSENSE: - ret = aw88399_dev_get_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * - ical_k / vcal_k * aw88399->vcalb_init_val; - break; - case AW88399_DEV_VDSEL_DAC: - ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / - AW88399_ISCAL_DAC_FACTOR * ical_k / - vcal_k * aw88399->vcalb_init_val; - break; - default: - dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); - ret = -EINVAL; - break; - } - if (ret) - return ret; - - vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; - reg_val = (uint32_t)vcalb; - - regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); - - return 0; -} - -static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - uint16_t re_lbits, re_hbits; - u32 cali_re; - int ret; - - if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || - (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) - return -EINVAL; - - cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + - aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); - - re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; - re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; - - ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); - if (ret) { - dev_err(aw_dev->dev, "set cali re error"); - return ret; - } - - ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); - if (ret) - dev_err(aw_dev->dev, "set cali re error"); - - return ret; -} - -static int aw_dev_fw_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, fw_len_val; - unsigned int reg_val; - int ret; - - /* calculate fw_end_addr */ - fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; - - /* write fw_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, fw_len_val); - if (ret) - return ret; - /* enable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); - - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - - /* read crc check result */ - regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", - __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, cfg_len_val; - unsigned int reg_val; - int ret; - - /* calculate cfg end addr */ - cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; - - /* write cfg_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); - if (ret) - return ret; - - /* enable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); - if (ret) - return ret; - - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - /* read crc check result */ - ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", - check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_hw_crc_check(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); - if (ret) - return ret; - - ret = aw_dev_fw_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "fw_crc_check failed\n"); - goto crc_check_failed; - } - - ret = aw_dev_cfg_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "cfg_crc_check failed\n"); - goto crc_check_failed; - } - - ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); - if (ret) - return ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - - return ret; - -crc_check_failed: - regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - return ret; -} - -static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) -{ - int ret; - - if (flag) - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static int aw_dev_get_dsp_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); - if (ret) - return ret; - if (!(reg_val & (~AW88399_WDT_CNT_MASK))) - return -EPERM; - - return 0; -} - -static int aw_dev_dsp_check(struct aw_device *aw_dev) -{ - int ret, i; - - switch (aw_dev->dsp_cfg) { - case AW88399_DEV_DSP_BYPASS: - dev_dbg(aw_dev->dev, "dsp bypass"); - ret = 0; - break; - case AW88399_DEV_DSP_WORK: - aw_dev_dsp_enable(aw_dev, false); - aw_dev_dsp_enable(aw_dev, true); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { - ret = aw_dev_get_dsp_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } - } - break; - default: - dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); - ret = -EINVAL; - break; - } - - return ret; -} - -static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) -{ - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - unsigned int reg_value; - u16 real_value; - int ret; - - real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); - - real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); - - ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); - - return ret; -} - -static void aw_dev_fade_in(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - u16 fade_in_vol = desc->ctl_volume; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_in_time == 0) { - aw_dev_set_volume(aw_dev, fade_in_vol); - return; - } - - for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); - } - - if (i != fade_in_vol) - aw_dev_set_volume(aw_dev, fade_in_vol); -} - -static void aw_dev_fade_out(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_out_time == 0) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - return; - } - - for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } - - if (i != AW88399_MUTE_VOL) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } -} - -static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) -{ - if (is_mute) { - aw_dev_fade_out(aw_dev); - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); - } else { - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); - aw_dev_fade_in(aw_dev); - } -} - -static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - - if (dither) - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); - else - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); -} - -static int aw88399_dev_start(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - if (aw_dev->status == AW88399_DEV_PW_ON) { - dev_dbg(aw_dev->dev, "already power on"); - return 0; - } - - aw88399_dev_set_dither(aw88399, false); - - /* power on */ - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - - ret = aw_dev_check_syspll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "pll check failed cannot start"); - goto pll_check_fail; - } - - /* amppd on */ - aw_dev_amppd(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 50); - - /* check i2s status */ - ret = aw_dev_check_sysst(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "sysst check failed"); - goto sysst_check_fail; - } - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { - ret = aw_dev_hw_crc_check(aw88399); - if (ret) { - dev_err(aw_dev->dev, "dsp crc check failed"); - goto crc_check_fail; - } - aw_dev_dsp_enable(aw_dev, false); - aw_dev_set_vcalb(aw88399); - aw_dev_update_cali_re(&aw_dev->cali_desc); - - ret = aw_dev_dsp_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp status check failed"); - goto dsp_check_fail; - } - } else { - dev_dbg(aw_dev->dev, "start pa with dsp bypass"); - } - - /* enable tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, true); - - if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) - aw88399_dev_set_dither(aw88399, true); - - /* close mute */ - aw88399_dev_mute(aw_dev, false); - /* clear inturrupt */ - aw_dev_clear_int_status(aw_dev); - aw_dev->status = AW88399_DEV_PW_ON; - - return 0; - -dsp_check_fail: -crc_check_fail: - aw_dev_dsp_enable(aw_dev, false); -sysst_check_fail: - aw_dev_clear_int_status(aw_dev); - aw_dev_amppd(aw_dev, true); -pll_check_fail: - aw_dev_pwd(aw_dev, true); - aw_dev->status = AW88399_DEV_PW_OFF; - - return ret; -} - -static int aw_dev_dsp_update_container(struct aw_device *aw_dev, - unsigned char *data, unsigned int len, unsigned short base) -{ - u32 tmp_len; - int i, ret; - - ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); - if (ret) - return ret; - - for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { - tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); - ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, - &data[i], tmp_len); - if (ret) - return ret; - } - - return 0; -} - -static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - u32 dsp_ra; - int ret; - - ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, - &dsp_ra, AW_DSP_32_DATA); - if (ret) { - dev_err(aw_dev->dev, "read ra error"); - return ret; - } - - cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, - AW88399_DSP_RE_SHIFT); - - return 0; -} - -static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp config len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp config data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); - if (ret) - return ret; - - aw_dev->dsp_cfg_len = len; - - ret = aw_dev_get_ra(&aw_dev->cali_desc); - - return ret; -} - -static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); - return -EINVAL; - } - - aw_dev->dsp_fw_len = len; - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); - - return ret; -} - -static int aw_dev_check_sram(struct aw_device *aw_dev) -{ - unsigned int reg_val; - - /* read dsp_rom_check_reg */ - aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { - dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", - reg_val, AW88399_DSP_ROM_CHECK_DATA); - return -EPERM; - } - - /* check dsp_cfg_base_addr */ - aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, - AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); - aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", - reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); - return -EPERM; - } - - return 0; -} - -static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) -{ - int ret; - - switch (flag) { - case AW88399_DEV_MEMCLK_PLL: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_DAPHCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select pll failed"); - break; - case AW88399_DEV_MEMCLK_OSC: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_OSCCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select OSC failed"); - break; - default: - dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); - break; - } -} - -static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) -{ - struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); - if (ret) { - dev_dbg(aw_dev->dev, "%s failed", __func__); - return; - } - if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) - profctrl_desc->cur_mode = AW88399_RCV_MODE; - else - profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; -} - -static int aw_dev_update_reg_container(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - u16 read_vol, reg_val; - int data_len, i, ret; - int16_t *reg_data; - u8 reg_addr; - - reg_data = (int16_t *)data; - data_len = len >> 1; - - if (data_len & 0x1) { - dev_err(aw_dev->dev, "data len:%d unsupported", data_len); - return -EINVAL; - } - - for (i = 0; i < data_len; i += 2) { - reg_addr = reg_data[i]; - reg_val = reg_data[i + 1]; - - if (reg_addr == AW88399_DSPVCALB_REG) { - aw88399->vcalb_init_val = reg_val; - continue; - } - - if (reg_addr == AW88399_SYSCTRL_REG) { - if (reg_val & (~AW88399_DSPBY_MASK)) - aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; - else - aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; - - reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | - AW88399_DSPBY_MASK); - reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | - AW88399_DSPBY_BYPASS_VALUE); - } - - if (reg_addr == AW88399_I2SCTRL3_REG) { - reg_val &= AW88399_I2STXEN_MASK; - reg_val |= AW88399_I2STXEN_DISABLE_VALUE; - } - - if (reg_addr == AW88399_SYSCTRL2_REG) { - read_vol = (reg_val & (~AW88399_VOL_MASK)) >> - AW88399_VOL_START_BIT; - aw_dev->volume_desc.init_volume = read_vol; - } - - if (reg_addr == AW88399_DBGCTRL_REG) { - if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) - aw88399->check_val = AW_EF_OR_CHECK; - else - aw88399->check_val = AW_EF_AND_CHECK; - - aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); - } - - if (reg_addr == AW88399_CRCCTRL_REG) - aw88399->crc_init_val = reg_val; - - ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); - if (ret) - return ret; - } - - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - aw_dev_get_cur_mode_st(aw_dev); - - if (aw_dev->prof_cur != aw_dev->prof_index) - vol_desc->ctl_volume = 0; - else - aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); - - return 0; -} - -static int aw_dev_reg_update(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - int ret; - - if (!len || !data) { - dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_update_reg_container(aw88399, data, len); - if (ret) - dev_err(aw88399->aw_pa->dev, "reg update failed"); - - return ret; -} - -static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) -{ - struct aw_prof_info *prof_info = &aw_dev->prof_info; - struct aw_prof_desc *prof_desc; - - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "index[%d] overflow count[%d]", - index, aw_dev->prof_info.count); - return -EINVAL; - } - - prof_desc = &aw_dev->prof_info.prof_desc[index]; - - *prof_name = prof_info->prof_name_list[prof_desc->id]; - - return 0; -} - -static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, - struct aw_prof_desc **prof_desc) -{ - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", - __func__, index, aw_dev->prof_info.count); - return -EINVAL; - } - - *prof_desc = &aw_dev->prof_info.prof_desc[index]; - - return 0; -} - -static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_prof_desc *prof_index_desc; - struct aw_sec_data_desc *sec_desc; - char *prof_name; - int ret; - - if ((aw_dev->prof_cur == aw_dev->prof_index) && - (force_up_en == AW88399_FORCE_UPDATE_OFF)) { - dev_dbg(aw_dev->dev, "scene no change, not update"); - return 0; - } - - if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { - dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); - return -EPERM; - } - - ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "start update %s", prof_name); - - ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); - if (ret) - return ret; - - /* update reg */ - sec_desc = prof_index_desc->sec_desc; - ret = aw_dev_reg_update(aw88399, sec_desc[AW88395_DATA_TYPE_REG].data, - sec_desc[AW88395_DATA_TYPE_REG].len); - if (ret) { - dev_err(aw_dev->dev, "update reg failed"); - return ret; - } - - aw88399_dev_mute(aw_dev, true); - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - - ret = aw_dev_check_sram(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "check sram failed"); - goto error; - } - - if (up_dsp_fw_en) { - dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); - ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, - sec_desc[AW88395_DATA_TYPE_DSP_FW].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp fw failed"); - goto error; - } - } - - /* update dsp config */ - ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, - sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp cfg failed"); - goto error; - } - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - - aw_dev->prof_cur = aw_dev->prof_index; - - return 0; - -error: - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - return ret; -} - -static void aw88399_start_pa(struct aw88399 *aw88399) -{ - int ret, i; - - for (i = 0; i < AW88399_START_RETRIES; i++) { - ret = aw88399_dev_start(aw88399); - if (ret) { - dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed"); - continue; - } - } else { - dev_dbg(aw88399->aw_pa->dev, "start success\n"); - break; - } - } -} - -static void aw88399_startup_work(struct work_struct *work) -{ - struct aw88399 *aw88399 = - container_of(work, struct aw88399, start_work.work); - - mutex_lock(&aw88399->lock); - aw88399_start_pa(aw88399); - mutex_unlock(&aw88399->lock); -} - -static void aw88399_start(struct aw88399 *aw88399, bool sync_start) -{ - int ret; - - if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) - return; - - if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) - return; - - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed."); - return; - } - - if (sync_start == AW88399_SYNC_START) - aw88399_start_pa(aw88399); - else - queue_delayed_work(system_dfl_wq, - &aw88399->start_work, - AW88399_START_WORK_DELAY_MS); -} - -static int aw_dev_check_sysint(struct aw_device *aw_dev) -{ - u16 reg_val; - - aw_dev_get_int_status(aw_dev, ®_val); - if (reg_val & AW88399_BIT_SYSINT_CHECK) { - dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw88399_stop(struct aw_device *aw_dev) -{ - struct aw_sec_data_desc *dsp_cfg = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; - struct aw_sec_data_desc *dsp_fw = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; - int int_st; - - if (aw_dev->status == AW88399_DEV_PW_OFF) { - dev_dbg(aw_dev->dev, "already power off"); - return 0; - } - - aw_dev->status = AW88399_DEV_PW_OFF; - - aw88399_dev_mute(aw_dev, true); - usleep_range(AW88399_4000_US, AW88399_4000_US + 100); - - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - int_st = aw_dev_check_sysint(aw_dev); - - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_amppd(aw_dev, true); - - if (int_st) { - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); - aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - } - - aw_dev_pwd(aw_dev, true); - - return 0; -} - static struct snd_soc_dai_driver aw88399_dai[] = { { .name = "aw88399-aif", @@ -1869,86 +664,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, return 0; } -static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); - if (ret) { - dev_err(aw_dev->dev, "aw_dev acf parse failed"); - return -EINVAL; - } - aw_dev->fade_in_time = AW88399_1000_US / 10; - aw_dev->fade_out_time = AW88399_1000_US >> 1; - aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; - aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; - - ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); - if (ret) { - dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); - return ret; - } - - aw88399_dev_mute(aw_dev, true); - - /* close tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - /* enable amppd */ - aw_dev_amppd(aw_dev, true); - - /* close dsp */ - aw_dev_dsp_enable(aw_dev, false); - /* set power down */ - aw_dev_pwd(aw_dev, true); - - return 0; -} - -static int aw88399_request_firmware_file(struct aw88399 *aw88399) -{ - const struct firmware *cont = NULL; - int ret; - - aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; - - ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); - if (ret) { - dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", - AW88399_ACF_FILE, cont ? cont->size : 0); - - aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, - struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88399->aw_cfg) { - release_firmware(cont); - return -ENOMEM; - } - aw88399->aw_cfg->len = (int)cont->size; - memcpy(aw88399->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); - if (ret) { - dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - mutex_lock(&aw88399->lock); - /* aw device init */ - ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); - if (ret) - dev_err(aw88399->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88399->lock); - - return ret; -} - static const struct snd_kcontrol_new aw88399_controls[] = { SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG, 6, AW88399_MUTE_VOL, 0, aw88399_volume_get, @@ -2040,70 +755,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { .num_controls = ARRAY_SIZE(aw88399_controls), }; -static void aw88399_hw_reset(struct aw88399 *aw88399) -{ - if (aw88399->reset_gpio) { - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 0); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - } -} - -static void aw88399_parse_channel_dt(struct aw_device *aw_dev) -{ - struct device_node *np = aw_dev->dev->of_node; - u32 channel_value; - - of_property_read_u32(np, "awinic,audio-channel", &channel_value); - aw_dev->channel = channel_value; -} - -static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) -{ - struct aw_device *aw_dev; - unsigned int chip_id; - int ret; - - ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); - if (ret) { - dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); - return ret; - } - if (chip_id != AW88399_CHIP_ID) { - dev_err(&i2c->dev, "unsupported device"); - return -ENXIO; - } - dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); - - aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); - if (!aw_dev) - return -ENOMEM; - aw88399->aw_pa = aw_dev; - - aw_dev->i2c = i2c; - aw_dev->dev = &i2c->dev; - aw_dev->regmap = regmap; - mutex_init(&aw_dev->dsp_lock); - - aw_dev->chip_id = chip_id; - aw_dev->acf = NULL; - aw_dev->prof_info.prof_desc = NULL; - aw_dev->prof_info.count = 0; - aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; - aw_dev->channel = AW88399_DEV_DEFAULT_CH; - aw_dev->fw_status = AW88399_DEV_FW_FAILED; - - aw_dev->fade_step = AW88399_VOLUME_STEP_DB; - aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; - - aw88399_parse_channel_dt(aw_dev); - - return 0; -} - static int aw88399_i2c_probe(struct i2c_client *i2c) { struct aw88399 *aw88399; diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index b386f4836748..04123bf0ad84 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -10,512 +10,10 @@ #ifndef __AW88399_H__ #define __AW88399_H__ -/* registers list */ -#define AW88399_ID_REG (0x00) -#define AW88399_SYSST_REG (0x01) -#define AW88399_SYSINT_REG (0x02) -#define AW88399_SYSINTM_REG (0x03) -#define AW88399_SYSCTRL_REG (0x04) -#define AW88399_SYSCTRL2_REG (0x05) -#define AW88399_I2SCTRL1_REG (0x06) -#define AW88399_I2SCTRL2_REG (0x07) -#define AW88399_I2SCTRL3_REG (0x08) -#define AW88399_DACCFG1_REG (0x09) -#define AW88399_DACCFG2_REG (0x0A) -#define AW88399_DACCFG3_REG (0x0B) -#define AW88399_DACCFG4_REG (0x0C) -#define AW88399_DACCFG5_REG (0x0D) -#define AW88399_DACCFG6_REG (0x0E) -#define AW88399_DACCFG7_REG (0x0F) -#define AW88399_MPDCFG1_REG (0x10) -#define AW88399_MPDCFG2_REG (0x11) -#define AW88399_MPDCFG3_REG (0x12) -#define AW88399_MPDCFG4_REG (0x13) -#define AW88399_PWMCTRL1_REG (0x14) -#define AW88399_PWMCTRL2_REG (0x15) -#define AW88399_PWMCTRL3_REG (0x16) -#define AW88399_I2SCFG1_REG (0x17) -#define AW88399_DBGCTRL_REG (0x18) -#define AW88399_HAGCST_REG (0x20) -#define AW88399_VBAT_REG (0x21) -#define AW88399_TEMP_REG (0x22) -#define AW88399_PVDD_REG (0x23) -#define AW88399_ISNDAT_REG (0x24) -#define AW88399_VSNDAT_REG (0x25) -#define AW88399_I2SINT_REG (0x26) -#define AW88399_I2SCAPCNT_REG (0x27) -#define AW88399_ANASTA1_REG (0x28) -#define AW88399_ANASTA2_REG (0x29) -#define AW88399_ANASTA3_REG (0x2A) -#define AW88399_TESTDET_REG (0x2B) -#define AW88399_DSMCFG1_REG (0x30) -#define AW88399_DSMCFG2_REG (0x31) -#define AW88399_DSMCFG3_REG (0x32) -#define AW88399_DSMCFG4_REG (0x33) -#define AW88399_DSMCFG5_REG (0x34) -#define AW88399_DSMCFG6_REG (0x35) -#define AW88399_DSMCFG7_REG (0x36) -#define AW88399_DSMCFG8_REG (0x37) -#define AW88399_TESTIN_REG (0x38) -#define AW88399_TESTOUT_REG (0x39) -#define AW88399_MEMTEST_REG (0x3A) -#define AW88399_VSNCTRL1_REG (0x3B) -#define AW88399_ISNCTRL1_REG (0x3C) -#define AW88399_ISNCTRL2_REG (0x3D) -#define AW88399_DSPMADD_REG (0x40) -#define AW88399_DSPMDAT_REG (0x41) -#define AW88399_WDT_REG (0x42) -#define AW88399_ACR1_REG (0x43) -#define AW88399_ACR2_REG (0x44) -#define AW88399_ASR1_REG (0x45) -#define AW88399_ASR2_REG (0x46) -#define AW88399_DSPCFG_REG (0x47) -#define AW88399_ASR3_REG (0x48) -#define AW88399_ASR4_REG (0x49) -#define AW88399_DSPVCALB_REG (0x4A) -#define AW88399_CRCCTRL_REG (0x4B) -#define AW88399_DSPDBG1_REG (0x4C) -#define AW88399_DSPDBG2_REG (0x4D) -#define AW88399_DSPDBG3_REG (0x4E) -#define AW88399_PLLCTRL1_REG (0x50) -#define AW88399_PLLCTRL2_REG (0x51) -#define AW88399_PLLCTRL3_REG (0x52) -#define AW88399_CDACTRL1_REG (0x53) -#define AW88399_CDACTRL2_REG (0x54) -#define AW88399_CDACTRL3_REG (0x55) -#define AW88399_SADCCTRL1_REG (0x56) -#define AW88399_SADCCTRL2_REG (0x57) -#define AW88399_BOPCTRL1_REG (0x58) -#define AW88399_BOPCTRL2_REG (0x5A) -#define AW88399_BOPCTRL3_REG (0x5B) -#define AW88399_BOPCTRL4_REG (0x5C) -#define AW88399_BOPCTRL5_REG (0x5D) -#define AW88399_BOPCTRL6_REG (0x5E) -#define AW88399_BOPCTRL7_REG (0x5F) -#define AW88399_BSTCTRL1_REG (0x60) -#define AW88399_BSTCTRL2_REG (0x61) -#define AW88399_BSTCTRL3_REG (0x62) -#define AW88399_BSTCTRL4_REG (0x63) -#define AW88399_BSTCTRL5_REG (0x64) -#define AW88399_BSTCTRL6_REG (0x65) -#define AW88399_BSTCTRL7_REG (0x66) -#define AW88399_BSTCTRL8_REG (0x67) -#define AW88399_BSTCTRL9_REG (0x68) -#define AW88399_BSTCTRL10_REG (0x69) -#define AW88399_CPCTRL_REG (0x6A) -#define AW88399_EFWH_REG (0x6C) -#define AW88399_EFWM2_REG (0x6D) -#define AW88399_EFWM1_REG (0x6E) -#define AW88399_EFWL_REG (0x6F) -#define AW88399_TESTCTRL1_REG (0x70) -#define AW88399_TESTCTRL2_REG (0x71) -#define AW88399_EFCTRL1_REG (0x72) -#define AW88399_EFCTRL2_REG (0x73) -#define AW88399_EFRH4_REG (0x74) -#define AW88399_EFRH3_REG (0x75) -#define AW88399_EFRH2_REG (0x76) -#define AW88399_EFRH1_REG (0x77) -#define AW88399_EFRL4_REG (0x78) -#define AW88399_EFRL3_REG (0x79) -#define AW88399_EFRL2_REG (0x7A) -#define AW88399_EFRL1_REG (0x7B) -#define AW88399_TM_REG (0x7C) -#define AW88399_TM2_REG (0x7D) - -#define AW88399_REG_MAX (0x7E) -#define AW88399_MUTE_VOL (1023) - -#define AW88399_DSP_CFG_ADDR (0x9B00) -#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) -#define AW88399_DSP_FW_ADDR (0x8980) -#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) -#define AW88399_DSP_ROM_CHECK_DATA (0x4638) - -#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) -#define AW88399_CALI_RE_HBITS_SHIFT (16) - -#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) -#define AW88399_CALI_RE_LBITS_SHIFT (0) - -#define AW88399_I2STXEN_START_BIT (9) -#define AW88399_I2STXEN_BITS_LEN (1) -#define AW88399_I2STXEN_MASK \ - (~(((1<> (shift)) -#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) -#define AW88399_CRC_CHECK_PASS_VAL (0x4) - -#define AW88399_CRC_CFG_BASE_ADDR (0xD80) -#define AW88399_CRC_FW_BASE_ADDR (0x4C0) -#define AW88399_ACF_FILE "aw88399_acf.bin" -#define AW88399_DEV_SYSST_CHECK_MAX (10) -#define AW88399_CHIP_ID 0x2183 +#include #define AW88399_I2C_NAME "aw88399" -#define AW88399_START_RETRIES (5) -#define AW88399_START_WORK_DELAY_MS (0) - #define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000) #define AW88399_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -550,80 +48,4 @@ .put = profile_set, \ } -enum { - AW_EF_AND_CHECK = 0, - AW_EF_OR_CHECK, -}; - -enum { - AW88399_DEV_VDSEL_DAC = 0, - AW88399_DEV_VDSEL_VSENSE = 32, -}; - -enum { - AW88399_DSP_CRC_NA = 0, - AW88399_DSP_CRC_OK = 1, -}; - -enum { - AW88399_DSP_FW_UPDATE_OFF = 0, - AW88399_DSP_FW_UPDATE_ON = 1, -}; - -enum { - AW88399_FORCE_UPDATE_OFF = 0, - AW88399_FORCE_UPDATE_ON = 1, -}; - -enum { - AW88399_1000_US = 1000, - AW88399_2000_US = 2000, - AW88399_3000_US = 3000, - AW88399_4000_US = 4000, -}; - -enum AW88399_DEV_STATUS { - AW88399_DEV_PW_OFF = 0, - AW88399_DEV_PW_ON, -}; - -enum AW88399_DEV_FW_STATUS { - AW88399_DEV_FW_FAILED = 0, - AW88399_DEV_FW_OK, -}; - -enum AW88399_DEV_MEMCLK { - AW88399_DEV_MEMCLK_OSC = 0, - AW88399_DEV_MEMCLK_PLL = 1, -}; - -enum AW88399_DEV_DSP_CFG { - AW88399_DEV_DSP_WORK = 0, - AW88399_DEV_DSP_BYPASS = 1, -}; - -enum { - AW88399_NOT_RCV_MODE = 0, - AW88399_RCV_MODE = 1, -}; - -enum { - AW88399_SYNC_START = 0, - AW88399_ASYNC_START, -}; - -struct aw88399 { - struct aw_device *aw_pa; - struct mutex lock; - struct gpio_desc *reset_gpio; - struct delayed_work start_work; - struct regmap *regmap; - struct aw_container *aw_cfg; - - unsigned int check_val; - unsigned int crc_init_val; - unsigned int vcalb_init_val; - unsigned int dither_st; -}; - -#endif +#endif /* __AW88399_H__ */ diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 709543308fe9..9dc46ab1d3e1 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -1118,7 +1118,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } ret = of_property_read_u32(asrc_np, "fsl,asrc-format", &asrc_fmt); - priv->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + priv->asrc_format = asrc_fmt; if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index f23c21032287..11748c65d5cc 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1358,7 +1358,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); - asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + asrc->asrc_format = asrc_fmt; if (ret) { ret = of_property_read_u32(np, "fsl,asrc-width", &width); if (ret) { diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c index 7d39378c0622..4bc40f328f58 100644 --- a/sound/soc/fsl/fsl_asrc_m2m.c +++ b/sound/soc/fsl/fsl_asrc_m2m.c @@ -367,13 +367,13 @@ static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream, if (ret) return -EINVAL; - if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in) - pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format; + if (pcm_format_to_bits(params->codec.format) & cap.fmt_in) + pair->sample_format[IN] = params->codec.format; else return -EINVAL; - if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out) - pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format; + if (pcm_format_to_bits(params->codec.pcm_format) & cap.fmt_out) + pair->sample_format[OUT] = params->codec.pcm_format; else return -EINVAL; @@ -600,7 +600,7 @@ static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc, cap.rate_in, cap.rate_in_count * sizeof(__u32)); codec->descriptor[j].num_sample_rates = cap.rate_in_count; - codec->descriptor[j].formats = (__force __u32)k; + codec->descriptor[j].formats = k; codec->descriptor[j].pcm_formats = cap.fmt_out; codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0]; codec->descriptor[j].src.out_sample_rate_max = diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 8535ef844ce0..79eb2391058d 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2227,7 +2227,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) } ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt); - easrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + easrc->asrc_format = asrc_fmt; if (ret) { dev_err(dev, "failed to asrc format\n"); return ret; diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index d0f644573f49..f27934cf49da 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -503,8 +503,8 @@ static int qmc_dai_constraints_interleaved(struct snd_pcm_substream *substream, return ret; } - access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | - 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED; + access = 1ULL << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED | + 1ULL << SNDRV_PCM_ACCESS_RW_INTERLEAVED; ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { @@ -532,8 +532,8 @@ static int qmc_dai_constraints_noninterleaved(struct snd_pcm_substream *substrea return ret; } - access = 1ULL << (__force int)SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | - 1ULL << (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + access = 1ULL << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED | + 1ULL << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; ret = snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_ACCESS, access); if (ret) { diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index 43438af1e1c6..e3cb1438e837 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -531,7 +531,7 @@ static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_none(mask); - snd_mask_set(mask, (__force unsigned int)data->asrc_format); + snd_mask_set(mask, data->asrc_format); return 0; } @@ -684,7 +684,7 @@ static int imx_card_parse_of(struct imx_card_data *data) } ret = of_property_read_u32(args.np, "fsl,asrc-format", &asrc_fmt); - data->asrc_format = (__force snd_pcm_format_t)asrc_fmt; + data->asrc_format = asrc_fmt; if (ret) { /* Fallback to old binding; translate to asrc_format */ ret = of_property_read_u32(args.np, "fsl,asrc-width", &width); diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 099119ad28b3..74096236984a 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -144,7 +144,7 @@ static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); if (ret < 0) return ret; - bps = snd_pcm_format_physical_width((__force snd_pcm_format_t)params->codec.format); + bps = snd_pcm_format_physical_width(params->codec.format); if (bps < 0) return bps; format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 983f3b91119a..aa3d1a588479 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -172,7 +172,7 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); @@ -184,7 +184,7 @@ static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 0bc1f11e17aa..dea7ad167a49 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -99,7 +99,7 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; @@ -112,7 +112,7 @@ static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366.c b/sound/soc/mediatek/mt8186/mt8186-mt6366.c index 22123b087c3c..b68f474b63f4 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366.c @@ -387,7 +387,7 @@ static int mt8186_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, fmt); diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c index 55ebac0c3cef..75c90d1d165f 100644 --- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c +++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c @@ -623,7 +623,7 @@ static int mt8188_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); diff --git a/sound/soc/mediatek/mt8189/mt8189-nau8825.c b/sound/soc/mediatek/mt8189/mt8189-nau8825.c index e849e7a649bc..5d652d0cf01e 100644 --- a/sound/soc/mediatek/mt8189/mt8189-nau8825.c +++ b/sound/soc/mediatek/mt8189/mt8189-nau8825.c @@ -174,7 +174,7 @@ static int mt8189_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 91c57765ab57..8af8b0a366d5 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -382,7 +382,7 @@ static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 4d62bc654a58..fc293ca71502 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -387,7 +387,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -650,7 +650,7 @@ static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, { /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); diff --git a/sound/soc/mediatek/mt8196/mt8196-nau8825.c b/sound/soc/mediatek/mt8196/mt8196-nau8825.c index c9424786c53d..1d1dad86365b 100644 --- a/sound/soc/mediatek/mt8196/mt8196-nau8825.c +++ b/sound/soc/mediatek/mt8196/mt8196-nau8825.c @@ -180,7 +180,7 @@ static int mt8196_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, /* fix BE i2s format to 32bit, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); + 0, SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c index 2ff6066e1b6c..8773bf06e154 100644 --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -74,7 +74,7 @@ int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); data->params.rate_min = params_rate(params); data->params.rate_max = params_rate(params); - data->params.formats = 1ULL << (__force int) params_format(params); + data->params.formats = 1ULL << params_format(params); data->params.channels_min = params_channels(params); data->params.channels_max = params_channels(params); data->params.sig_bits = dai->driver->playback.sig_bits; diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 2f5f62079fa4..ccaa36525f8d 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -1840,6 +1840,7 @@ static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x) int rval = D_INTR_GETRVAL(x); #endif +#ifdef DBRI_DEBUG if (channel == D_INTR_CMD) { dprintk(D_CMD, "INTR: Command: %-5s Value:%d\n", cmds[command], val); @@ -1847,6 +1848,7 @@ static void dbri_process_one_interrupt(struct snd_dbri *dbri, int x) dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n", channel, code, rval); } +#endif switch (code) { case D_INTR_CMDI: diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index 9a0b85653c61..d3b7cab85699 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handler(struct urb *urb) struct midi_runtime *midi_rt = rt->chip->midi; if (!urb->status) { - if (rt->receiver_buffer[0] == 0x10) /* midi in event */ + u8 len = rt->receiver_buffer[1]; + + if (rt->receiver_buffer[0] == 0x10 && /* midi in event */ + len <= COMM_RECEIVER_BUFSIZE - 2 && + urb->actual_length >= len + 2) if (midi_rt) midi_rt->in_received(midi_rt, - rt->receiver_buffer + 2, - rt->receiver_buffer[1]); + rt->receiver_buffer + 2, len); } if (!rt->chip->shutdown) { diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 123c1c6539b8..d9dd8f44b047 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -194,23 +194,20 @@ static int usb6fire_fw_ezusb_upload( int ret; u8 data; struct usb_device *device = interface_to_usbdev(intf); - const struct firmware *fw = NULL; - struct ihex_record *rec = kmalloc_obj(struct ihex_record); + struct ihex_record *rec __free(kfree) = kmalloc_obj(struct ihex_record); if (!rec) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { - kfree(rec); dev_err(&intf->dev, "error requesting ezusb firmware %s.\n", fwname); return ret; } ret = usb6fire_fw_ihex_init(fw, rec); if (ret < 0) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "error validating ezusb firmware %s.\n", fwname); return ret; @@ -219,8 +216,6 @@ static int usb6fire_fw_ezusb_upload( data = 0x01; /* stop ezusb cpu */ ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); if (ret) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "unable to upload ezusb firmware %s: begin message.\n", fwname); @@ -231,8 +226,6 @@ static int usb6fire_fw_ezusb_upload( ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address, rec->data, rec->len); if (ret) { - kfree(rec); - release_firmware(fw); dev_err(&intf->dev, "unable to upload ezusb firmware %s: data urb.\n", fwname); @@ -240,8 +233,6 @@ static int usb6fire_fw_ezusb_upload( } } - release_firmware(fw); - kfree(rec); if (postdata) { /* write data after firmware has been uploaded */ ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr, postdata, postlen); @@ -270,19 +261,18 @@ static int usb6fire_fw_fpga_upload( int ret; int i; struct usb_device *device = interface_to_usbdev(intf); - u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); + u8 *buffer __free(kfree) = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); const char *c; const char *end; - const struct firmware *fw; if (!buffer) return -ENOMEM; + const struct firmware *fw __free(firmware) = NULL; ret = request_firmware(&fw, fwname, &device->dev); if (ret < 0) { dev_err(&intf->dev, "unable to get fpga firmware %s.\n", fwname); - kfree(buffer); return -EIO; } @@ -291,8 +281,6 @@ static int usb6fire_fw_fpga_upload( ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0); if (ret) { - kfree(buffer); - release_firmware(fw); dev_err(&intf->dev, "unable to upload fpga firmware: begin urb.\n"); return ret; @@ -304,15 +292,11 @@ static int usb6fire_fw_fpga_upload( ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { - release_firmware(fw); - kfree(buffer); dev_err(&intf->dev, "unable to upload fpga firmware: fw urb.\n"); return ret; } } - release_firmware(fw); - kfree(buffer); ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0); if (ret) { diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index bebb48cb9abc..c5c542d17ccc 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcd2000 *bcd2k) if (!midi_out_substream) return; + if (!bcd2k->midi_out_urb) + return; + /* copy command prefix bytes */ memcpy(bcd2k->midi_out_buf, device_cmd_prefix, sizeof(device_cmd_prefix)); @@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(struct snd_rawmidi_substream *substream) { struct bcd2000 *bcd2k = substream->rmidi->private_data; - if (bcd2k->midi_out_active) { + if (bcd2k->midi_out_active && bcd2k->midi_out_urb) { usb_kill_urb(bcd2k->midi_out_urb); bcd2k->midi_out_active = 0; } @@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) static void bcd2000_free_usb_related_resources(struct bcd2000 *bcd2k, struct usb_interface *interface) { - usb_kill_urb(bcd2k->midi_out_urb); - usb_kill_urb(bcd2k->midi_in_urb); + usb_poison_urb(bcd2k->midi_out_urb); + usb_poison_urb(bcd2k->midi_in_urb); usb_free_urb(bcd2k->midi_out_urb); usb_free_urb(bcd2k->midi_in_urb); + bcd2k->midi_out_urb = NULL; + bcd2k->midi_in_urb = NULL; if (bcd2k->intf) { usb_set_intfdata(bcd2k->intf, NULL); diff --git a/sound/usb/card.c b/sound/usb/card.c index 6a3b576fb067..24112e491779 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -715,7 +715,7 @@ static void snd_usb_init_quirk_flags(int idx, struct snd_usb_audio *chip) /* old style option found: the position-based integer value */ if (quirk_flags[idx] && - !kstrtou32(quirk_flags[idx], 0, &chip->quirk_flags)) { + !kstrtou64(quirk_flags[idx], 0, &chip->quirk_flags)) { snd_usb_apply_flag_dbg("module param", chip, chip->quirk_flags); return; } @@ -905,6 +905,40 @@ static int try_to_register_card(struct snd_usb_audio *chip, int ifnum) return 0; } +static void usb_audio_disconnect_components(struct snd_usb_audio *chip) +{ + struct snd_usb_stream *as; + struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; + struct list_head *p; + + /* release the pcm resources */ + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); + } + /* release the endpoint resources */ + list_for_each_entry(ep, &chip->ep_list, list) { + snd_usb_endpoint_release(ep); + } + /* release the midi resources */ + list_for_each(p, &chip->midi_list) { + snd_usbmidi_disconnect(p); + } + snd_usb_midi_v2_disconnect_all(chip); + /* + * Nice to check quirk && quirk->shares_media_device and + * then call the snd_media_device_delete(). Don't have + * access to the quirk here. snd_media_device_delete() + * accesses mixer_list + */ + snd_media_device_delete(chip); + + /* release mixer resources */ + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); + } +} + /* * probe the active usb device * @@ -1077,8 +1111,10 @@ static int usb_audio_probe(struct usb_interface *intf, * decrement before memory is possibly returned. */ atomic_dec(&chip->active); - if (!chip->num_interfaces) + if (!chip->num_interfaces) { + usb_audio_disconnect_components(chip); snd_card_free(chip->card); + } } return err; } @@ -1091,48 +1127,18 @@ static bool __usb_audio_disconnect(struct usb_interface *intf, struct snd_usb_audio *chip, struct snd_card *card) { - struct list_head *p; - guard(mutex)(®ister_mutex); if (platform_ops && platform_ops->disconnect_cb) platform_ops->disconnect_cb(chip); if (atomic_inc_return(&chip->shutdown) == 1) { - struct snd_usb_stream *as; - struct snd_usb_endpoint *ep; - struct usb_mixer_interface *mixer; - /* wait until all pending tasks done; * they are protected by snd_usb_lock_shutdown() */ snd_refcount_sync(&chip->usage_count); snd_card_disconnect(card); - /* release the pcm resources */ - list_for_each_entry(as, &chip->pcm_list, list) { - snd_usb_stream_disconnect(as); - } - /* release the endpoint resources */ - list_for_each_entry(ep, &chip->ep_list, list) { - snd_usb_endpoint_release(ep); - } - /* release the midi resources */ - list_for_each(p, &chip->midi_list) { - snd_usbmidi_disconnect(p); - } - snd_usb_midi_v2_disconnect_all(chip); - /* - * Nice to check quirk && quirk->shares_media_device and - * then call the snd_media_device_delete(). Don't have - * access to the quirk here. snd_media_device_delete() - * accesses mixer_list - */ - snd_media_device_delete(chip); - - /* release mixer resources */ - list_for_each_entry(mixer, &chip->mixer_list, list) { - snd_usb_mixer_disconnect(mixer); - } + usb_audio_disconnect_components(chip); } if (chip->quirk_flags & QUIRK_FLAG_DISABLE_AUTOSUSPEND) diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2e0c18e35281..34832183a1f0 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -600,7 +600,7 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, static int set_sample_rate_v2v3(struct snd_usb_audio *chip, const struct audioformat *fmt, int rate) { - int cur_rate, prev_rate; + int cur_rate, prev_rate = 0; int clock; /* First, try to find a valid clock. This may trigger @@ -625,9 +625,12 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, return clock; } - prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); - if (prev_rate == rate) - goto validation; + if (!(chip->quirk_flags & QUIRK_FLAG_ALWAYS_SET_RATE)) { + prev_rate = get_sample_rate_v2v3(chip, fmt->iface, + fmt->altsetting, clock); + if (prev_rate == rate) + goto validation; + } cur_rate = snd_usb_set_sample_rate_v2v3(chip, fmt, clock, rate); if (cur_rate < 0) { diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index a1d449f2a342..0835943d7b0e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1236,7 +1236,10 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) /* try to use enough URBs to contain an entire ALSA buffer */ max_urbs = min((unsigned) MAX_URBS, MAX_QUEUE * packs_per_ms / urb_packs); - ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); + if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP) + ep->nurbs = MAX_URBS; + else + ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } /* allocate and initialize data urbs */ @@ -1260,6 +1263,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) goto out_of_memory; u->urb->pipe = ep->pipe; u->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + if (chip->quirk_flags & QUIRK_FLAG_PLAYBACK_URB_FIXUP) + u->urb->transfer_flags |= URB_ISO_ASAP; u->urb->interval = 1 << ep->datainterval; u->urb->context = u; u->urb->complete = snd_complete_urb; diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index 2bf572c6fdc4..5fc2131b4561 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -82,6 +82,7 @@ struct fcp_data { struct mutex mutex; /* serialise access to the device */ struct completion cmd_done; /* wait for command completion */ struct file *file; /* hwdep file */ + struct urb *urb; /* FCP notification endpoint */ struct fcp_notify notify; @@ -190,7 +191,7 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode, const int max_retries = 5; int err; - if (!mixer->urb) + if (!private->urb) return -ENODEV; struct fcp_usb_packet *req __free(kfree) = NULL; @@ -305,7 +306,7 @@ static int fcp_reinit(struct usb_mixer_interface *mixer) { struct fcp_data *private = mixer->private_data; - if (mixer->urb) + if (private->urb) return 0; void *step0_resp __free(kfree) = @@ -493,7 +494,7 @@ static int fcp_ioctl_init(struct usb_mixer_interface *mixer, buf_size = init.step0_resp_size + init.step2_resp_size; void *resp __free(kfree) = - kmalloc(buf_size, GFP_KERNEL); + kzalloc(buf_size, GFP_KERNEL); if (!resp) return -ENOMEM; @@ -901,13 +902,15 @@ static int fcp_hwdep_init(struct usb_mixer_interface *mixer) static void fcp_cleanup_urb(struct usb_mixer_interface *mixer) { - if (!mixer->urb) + struct fcp_data *private = mixer->private_data; + + if (!private->urb) return; - usb_kill_urb(mixer->urb); - kfree(mixer->urb->transfer_buffer); - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_kill_urb(private->urb); + kfree(private->urb->transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; } static void fcp_private_free(struct usb_mixer_interface *mixer) @@ -978,37 +981,37 @@ static int fcp_init_notify(struct usb_mixer_interface *mixer) int err; /* Already set up */ - if (mixer->urb) + if (private->urb) return 0; if (usb_pipe_type_check(dev, pipe)) return -EINVAL; - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) + private->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!private->urb) return -ENOMEM; transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); if (!transfer_buffer) { - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_free_urb(private->urb); + private->urb = NULL; return -ENOMEM; } - usb_fill_int_urb(mixer->urb, dev, pipe, + usb_fill_int_urb(private->urb, dev, pipe, transfer_buffer, private->wMaxPacketSize, fcp_notify, mixer, private->bInterval); - init_completion(&private->cmd_done); + reinit_completion(&private->cmd_done); - err = usb_submit_urb(mixer->urb, GFP_KERNEL); + err = usb_submit_urb(private->urb, GFP_KERNEL); if (err) { usb_audio_err(mixer->chip, "%s: usb_submit_urb failed: %d\n", __func__, err); kfree(transfer_buffer); - usb_free_urb(mixer->urb); - mixer->urb = NULL; + usb_free_urb(private->urb); + private->urb = NULL; } return err; @@ -1030,6 +1033,8 @@ static int fcp_init(struct usb_mixer_interface *mixer, step0_resp, private->step0_resp_size); if (err < 0) return err; + if (err != private->step0_resp_size) + return -EIO; err = fcp_init_notify(mixer); if (err < 0) @@ -1059,6 +1064,7 @@ static int fcp_init_private(struct usb_mixer_interface *mixer) return -ENOMEM; mutex_init(&private->mutex); + init_completion(&private->cmd_done); init_waitqueue_head(&private->notify.queue); spin_lock_init(&private->notify.lock); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 703c118f9d4e..5de182181ede 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3935,6 +3935,12 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) struct usb_mixer_elem_list *list; int id, err; + if (mixer->private_resume) { + err = mixer->private_resume(mixer); + if (err < 0) + return err; + } + /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { for_each_mixer_elem(list, mixer, id) { diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 3fa1bd96f858..037b446d8b6f 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -18,6 +18,7 @@ struct usb_mixer_interface { struct usb_host_interface *hostif; struct list_head list; unsigned int ignore_ctl_error; + /* UAC2 status interrupt endpoint; owned by mixer.c */ struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ struct usb_mixer_elem_list **id_elems; @@ -42,6 +43,7 @@ struct usb_mixer_interface { void *private_data; void (*private_free)(struct usb_mixer_interface *mixer); void (*private_suspend)(struct usb_mixer_interface *mixer); + int (*private_resume)(struct usb_mixer_interface *mixer); }; #define MAX_CHANNELS 64 /* max logical channels */ diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index ce27fc871f51..8046d5987d5b 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -505,6 +505,19 @@ static const struct usbmix_connector_map gigabyte_b450_connector_map[] = { {} }; +/* Audient iD14: FU 12 advertises Volume on only 4 of its 6 logical channels + * and sits on the monitor mixer branch, but it is traced through to the + * Speaker output terminal and gets named "Speaker Playback Volume". Userspace + * then adopts it as the stream's hardware volume, and any setting below 0 dB + * attenuates some channels but not others (20 dB imbalance at 80%). Give it a + * non-standard name so that it is no longer taken for the stream's master + * volume, while remaining reachable for anyone who wants the monitor gain. + */ +static const struct usbmix_name_map audient_id14_map[] = { + { 12, "Monitor Mix Playback" }, /* FU, partial coverage */ + {} +}; + /* * Control map entries */ @@ -588,6 +601,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x2573, 0x0008), .map = maya44_map, }, + { + /* Audient iD14 */ + .id = USB_ID(0x2708, 0x0008), + .map = audient_id14_map, + }, { /* KEF X300A */ .id = USB_ID(0x27ac, 0x1000), diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 74d26fd86efb..a1f5592cc5d5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3920,6 +3920,7 @@ static int snd_rme_digiface_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_450_IDX 0x5 #define SND_DJM_A9_IDX 0x6 #define SND_DJM_V10_IDX 0x7 +#define SND_DJM_S11_IDX 0x8 #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ .name = _name, \ @@ -4260,6 +4261,21 @@ static const struct snd_djm_ctl snd_djm_ctls_v10[] = { // playback channels are fixed and controlled by hardware knobs on the mixer }; +// DJM-S11 +static const u16 snd_djm_opts_s11_cap1[] = { + 0x0100, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010d }; +static const u16 snd_djm_opts_s11_cap2[] = { + 0x0200, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020d }; +static const u16 snd_djm_opts_s11_cap3[] = { + 0x0307, 0x0308, 0x0309, 0x030a, 0x030d, 0x0311, 0x0312 }; + +static const struct snd_djm_ctl snd_djm_ctls_s11[] = { + SND_DJM_CTL("Master Input Level Capture Switch", cap_level, 0, SND_DJM_WINDEX_CAPLVL), + SND_DJM_CTL("Input 1 Capture Switch", s11_cap1, 1, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Input 2 Capture Switch", s11_cap2, 1, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Input 3 Capture Switch", s11_cap3, 3, SND_DJM_WINDEX_CAP) +}; + static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_250MK2_IDX] = SND_DJM_DEVICE(250mk2), [SND_DJM_750_IDX] = SND_DJM_DEVICE(750), @@ -4269,6 +4285,7 @@ static const struct snd_djm_device snd_djm_devices[] = { [SND_DJM_450_IDX] = SND_DJM_DEVICE(450), [SND_DJM_A9_IDX] = SND_DJM_DEVICE(a9), [SND_DJM_V10_IDX] = SND_DJM_DEVICE(v10), + [SND_DJM_S11_IDX] = SND_DJM_DEVICE(s11), }; static int snd_djm_controls_info(struct snd_kcontrol *kctl, @@ -4573,6 +4590,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2b73, 0x0034): /* Pioneer DJ DJM-V10 */ err = snd_djm_controls_create(mixer, SND_DJM_V10_IDX); break; + case USB_ID(0x2b73, 0x0037): /* Pioneer DJ DJM-S11 */ + err = snd_djm_controls_create(mixer, SND_DJM_S11_IDX); + break; case USB_ID(0x03f0, 0x0269): /* HP TB Dock G2 */ err = hp_dock_mixer_create(mixer); break; diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index 78fb72e626ca..502854cc9f9f 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -1403,6 +1403,7 @@ struct scarlett2_data { struct usb_mixer_interface *mixer; struct mutex usb_mutex; /* prevent sending concurrent USB requests */ struct completion cmd_done; + struct urb *urb; /* notification endpoint */ struct mutex data_mutex; /* lock access to this data */ u8 running; u8 hwdep_in_use; @@ -8565,13 +8566,70 @@ static void scarlett2_notify(struct urb *urb) } } -/*** Cleanup/Suspend Callbacks ***/ +/*** Notification URB and Cleanup/Suspend Callbacks ***/ + +/* Submit a URB to receive notifications from the device */ +static int scarlett2_init_notify(struct usb_mixer_interface *mixer) +{ + struct usb_device *dev = mixer->chip->dev; + struct scarlett2_data *private = mixer->private_data; + unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); + void *transfer_buffer; + int err; + + /* Already set up */ + if (private->urb) + return 0; + + if (usb_pipe_type_check(dev, pipe)) + return -EINVAL; + + private->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!private->urb) + return -ENOMEM; + + transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); + if (!transfer_buffer) { + usb_free_urb(private->urb); + private->urb = NULL; + return -ENOMEM; + } + + usb_fill_int_urb(private->urb, dev, pipe, + transfer_buffer, private->wMaxPacketSize, + scarlett2_notify, mixer, private->bInterval); + + reinit_completion(&private->cmd_done); + + err = usb_submit_urb(private->urb, GFP_KERNEL); + if (err) { + kfree(transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; + } + + return err; +} + +static void scarlett2_cleanup_urb(struct usb_mixer_interface *mixer) +{ + struct scarlett2_data *private = mixer->private_data; + + if (!private->urb) + return; + + usb_kill_urb(private->urb); + kfree(private->urb->transfer_buffer); + usb_free_urb(private->urb); + private->urb = NULL; +} static void scarlett2_private_free(struct usb_mixer_interface *mixer) { struct scarlett2_data *private = mixer->private_data; cancel_delayed_work_sync(&private->work); + scarlett2_cleanup_urb(mixer); kfree(private); mixer->private_data = NULL; } @@ -8582,6 +8640,8 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) if (cancel_delayed_work_sync(&private->work)) scarlett2_config_save(private->mixer); + + scarlett2_cleanup_urb(mixer); } /*** Initialisation ***/ @@ -8701,11 +8761,13 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, mutex_init(&private->usb_mutex); mutex_init(&private->data_mutex); + init_completion(&private->cmd_done); INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); mixer->private_data = private; mixer->private_free = scarlett2_private_free; mixer->private_suspend = scarlett2_private_suspend; + mixer->private_resume = scarlett2_init_notify; private->info = entry->info; @@ -8722,40 +8784,6 @@ static int scarlett2_init_private(struct usb_mixer_interface *mixer, return scarlett2_find_fc_interface(mixer->chip->dev, private); } -/* Submit a URB to receive notifications from the device */ -static int scarlett2_init_notify(struct usb_mixer_interface *mixer) -{ - struct usb_device *dev = mixer->chip->dev; - struct scarlett2_data *private = mixer->private_data; - unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); - void *transfer_buffer; - - if (mixer->urb) { - usb_audio_err(mixer->chip, - "%s: mixer urb already in use!\n", __func__); - return 0; - } - - if (usb_pipe_type_check(dev, pipe)) - return -EINVAL; - - mixer->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!mixer->urb) - return -ENOMEM; - - transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); - if (!transfer_buffer) - return -ENOMEM; - - usb_fill_int_urb(mixer->urb, dev, pipe, - transfer_buffer, private->wMaxPacketSize, - scarlett2_notify, mixer, private->bInterval); - - init_completion(&private->cmd_done); - - return usb_submit_urb(mixer->urb, GFP_KERNEL); -} - /* Cargo cult proprietary initialisation sequence */ static int scarlett2_usb_init(struct usb_mixer_interface *mixer) { diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 71444c2898b4..0a3d39b8385b 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2692,6 +2692,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), { QUIRK_DATA_AUDIOFORMAT(2) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 2, .altsetting = 1, @@ -2703,11 +2704,16 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, { QUIRK_DATA_AUDIOFORMAT(3) { .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .fmt_bits = 24, .channels = 2, .iface = 3, .altsetting = 1, @@ -2719,6 +2725,10 @@ YAMAHA_DEVICE(0x7010, "UB99"), SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + }, } }, QUIRK_COMPOSITE_END @@ -3310,6 +3320,73 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + /* + * Pioneer DJ / AlphaTheta DJM-S11 + */ + USB_DEVICE(0x2b73, 0x0037), + QUIRK_DRIVER_INFO { + QUIRK_DATA_COMPOSITE { + { + QUIRK_DATA_STANDARD_MIXER(0) + }, + { + QUIRK_DATA_AUDIOFORMAT(1) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 14, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 48000 }, + .clock = 1, + .fmt_type = UAC_FORMAT_TYPE_I + } + }, + { + QUIRK_DATA_AUDIOFORMAT(2) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 10, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC | + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 48000 }, + .clock = 1, + .fmt_type = UAC_FORMAT_TYPE_I + } + }, + { + /* Audio Control (unknown purpose) */ + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + /* HID */ + .ifnum = 5, + .type = QUIRK_IGNORE_INTERFACE + }, + QUIRK_COMPOSITE_END + } + } +}, /* * MacroSilicon MS2100/MS2106 based AV capture cards diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 4d2957e62fbd..00b686a6c25b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2145,7 +2145,7 @@ struct usb_string_match { struct usb_audio_quirk_flags_table { u32 id; - u32 flags; + u64 flags; const struct usb_string_match *usb_string_match; }; @@ -2337,6 +2337,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_IGNORE_CTL_ERROR), DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */ QUIRK_FLAG_CTL_MSG_DELAY_1M), + DEVICE_FLG(0x0a73, 0x003a, /* Mackie DLZ Creator XS */ + QUIRK_FLAG_ALWAYS_SET_RATE), DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */ QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE), DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */ @@ -2432,6 +2434,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x1e0b, 0xd01e, /* Generic USB Audio Device */ + QUIRK_FLAG_PLAYBACK_URB_FIXUP), DEVICE_FLG(0x1ff7, 0x0f81, /* SC13A Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */ @@ -2528,6 +2532,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */ QUIRK_FLAG_ALIGN_TRANSFER), + DEVICE_FLG(0x84ef, 0x002a, /* Valeton GP-200 */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x84ef, 0x0082, /* Hotone Audio Pulze Mini */ QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL), @@ -2641,18 +2647,12 @@ static const char *const snd_usb_audio_quirk_flag_names[] = { QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL), QUIRK_STRING_ENTRY(IFB_SILENCE_ON_EMPTY), QUIRK_STRING_ENTRY(MIXER_GET_CUR_BROKEN), + QUIRK_STRING_ENTRY(PLAYBACK_URB_FIXUP), + QUIRK_STRING_ENTRY(ALWAYS_SET_RATE), NULL }; -const char *snd_usb_quirk_flag_find_name(unsigned long index) -{ - if (index >= ARRAY_SIZE(snd_usb_audio_quirk_flag_names)) - return NULL; - - return snd_usb_audio_quirk_flag_names[index]; -} - -u32 snd_usb_quirk_flags_from_name(const char *name) +static u64 snd_usb_quirk_flags_from_name(const char *name) { int i; @@ -2661,7 +2661,7 @@ u32 snd_usb_quirk_flags_from_name(const char *name) for (i = 0; snd_usb_audio_quirk_flag_names[i]; i++) { if (strcasecmp(name, snd_usb_audio_quirk_flag_names[i]) == 0) - return BIT_U32(i); + return BIT_U64(i); } return 0; @@ -2719,7 +2719,7 @@ void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, { u16 chip_vid = USB_ID_VENDOR(chip->usb_id); u16 chip_pid = USB_ID_PRODUCT(chip->usb_id); - u32 mask_flags, unmask_flags, bit; + u64 mask_flags, unmask_flags, bit; char *p, *field, *flag; bool is_unmask; u16 vid, pid; @@ -2773,7 +2773,7 @@ void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, is_unmask = false; } - if (!kstrtou32(flag, 16, &bit)) { + if (!kstrtou64(flag, 16, &bit)) { if (is_unmask) unmask_flags |= bit; else diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index f24d6a5a197a..0a723c266bb4 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -56,7 +56,4 @@ void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip); void snd_usb_init_quirk_flags_parse_string(struct snd_usb_audio *chip, const char *str); -const char *snd_usb_quirk_flag_find_name(unsigned long flag); -u32 snd_usb_quirk_flags_from_name(const char *name); - #endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index e26f9092417e..c49709d7ad25 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -44,7 +44,7 @@ struct snd_usb_audio { atomic_t active; atomic_t shutdown; struct snd_refcount usage_count; - unsigned int quirk_flags; + u64 quirk_flags; unsigned int need_delayed_register:1; /* warn for delayed registration */ int num_interfaces; int last_iface; @@ -254,6 +254,16 @@ extern bool snd_usb_skip_validation; * check being non-fatal and only disabling GET_CUR instead of the whole mixer. * The current volume will then be provided by the internal cache that stores * the last set volume + * QUIRK_FLAG_PLAYBACK_URB_FIXUP + * Set URB_ISO_ASAP flag for isochronous URBs and force nurbs to MAX_URBS. + * This is needed for devices that exhibit boot-time audio stuttering due + * to insufficient buffer depth combined with xHCI scheduling variability. + * The larger buffer (MAX_URBS = 12, ~64ms) absorbs system scheduling + * jitter during boot, while URB_ISO_ASAP ensures consistent xHCI scheduling. + * QUIRK_FLAG_ALWAYS_SET_RATE: + * Issue SET_CUR for the sample rate even when the clock already reports the + * requested rate. A device advertising a single rate is otherwise never sent + * the request at all, and some require it before streaming will start. */ enum { @@ -288,10 +298,12 @@ enum { QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL = 28, QUIRK_TYPE_IFB_SILENCE_ON_EMPTY = 29, QUIRK_TYPE_MIXER_GET_CUR_BROKEN = 30, + QUIRK_TYPE_PLAYBACK_URB_FIXUP = 31, + QUIRK_TYPE_ALWAYS_SET_RATE = 32, /* Please also edit snd_usb_audio_quirk_flag_names */ }; -#define QUIRK_FLAG(x) BIT_U32(QUIRK_TYPE_ ## x) +#define QUIRK_FLAG(x) BIT_U64(QUIRK_TYPE_ ## x) #define QUIRK_FLAG_GET_SAMPLE_RATE QUIRK_FLAG(GET_SAMPLE_RATE) #define QUIRK_FLAG_SHARE_MEDIA_DEVICE QUIRK_FLAG(SHARE_MEDIA_DEVICE) @@ -324,5 +336,7 @@ enum { #define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL) #define QUIRK_FLAG_IFB_SILENCE_ON_EMPTY QUIRK_FLAG(IFB_SILENCE_ON_EMPTY) #define QUIRK_FLAG_MIXER_GET_CUR_BROKEN QUIRK_FLAG(MIXER_GET_CUR_BROKEN) +#define QUIRK_FLAG_PLAYBACK_URB_FIXUP QUIRK_FLAG(PLAYBACK_URB_FIXUP) +#define QUIRK_FLAG_ALWAYS_SET_RATE QUIRK_FLAG(ALWAYS_SET_RATE) #endif /* __USBAUDIO_H */ diff --git a/sound/usb/usx2y/us144mkii_pcm.h b/sound/usb/usx2y/us144mkii_pcm.h index 74da8564431b..a130678405e3 100644 --- a/sound/usb/usx2y/us144mkii_pcm.h +++ b/sound/usb/usx2y/us144mkii_pcm.h @@ -7,7 +7,7 @@ #include "us144mkii.h" /** - * tascam_pcm_hw - Hardware capabilities for TASCAM US-144MKII PCM. + * var tascam_pcm_hw - Hardware capabilities for TASCAM US-144MKII PCM. * * Defines the supported PCM formats, rates, channels, and buffer/period sizes * for the TASCAM US-144MKII audio interface. @@ -15,14 +15,14 @@ extern const struct snd_pcm_hardware tascam_pcm_hw; /** - * tascam_playback_ops - ALSA PCM operations for playback. + * var tascam_playback_ops - ALSA PCM operations for playback. * * This structure defines the callback functions for playback stream operations. */ extern const struct snd_pcm_ops tascam_playback_ops; /** - * tascam_capture_ops - ALSA PCM operations for capture. + * var tascam_capture_ops - ALSA PCM operations for capture. * * This structure defines the callback functions for capture stream operations. */ diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 3808df54727d..a3a0bc13632b 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -167,7 +167,6 @@ static int usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, i if (!urb) return -ENODEV; urb->start_frame = frame + NRURBS * nr_of_packs(); // let hcd do rollover sanity checks - urb->hcpriv = NULL; urb->dev = subs->usx2y->dev; /* we need to set this at each time */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) {