sound fixes for 7.4-rc1

A collection of fixes across the sound subsystem since the initial
 7.4 PR.  Nothing particularly stands out, mostly driver- and
 device-specific fixes along with a few race / UAF fixes and cleanups.
 Here are some highlights:
 
 * ALSA core:
 - Fix potential race in PCM proc handling at disconnection
 - Fix potential race in UMP sequencer callback at releasing
 
 * HD-audio:
 - Reject codec reset/reconfig on ASoC buses to prevent deadlock
 - Force MSI for Hygon H34M51A board
 - Usual Realtek quirks for ASUS and HP laptops/AiO
 
 * USB-audio:
 - Bound packet copies by PCM buffer size in generic and ua101 drivers
 - Rate-limit URB submission errors at stream start
 - Skip redundant probe-time interface setup on MSI ALC4080
 - Device quirks for Novation UltraNova, MOTU MicroBook II, and RYDZ
   Headphone
 
 * ASoC:
 - Fixes for runtime PM and IRQ handling in hdac_hda and SDCA
 - SOF: Fix potential UAF at release, fix for Kconfig dependency
 - Qualcomm: Fixes for sm8250 and q6apm/q6afe
 - NXP / i.MX: Fix for pending work in hw_free
 - Xilinx: Fix read-modify-write of AUD_CTRL in formatter_pcm
 - Intel: Arrow Lake SoundWire match table additions (RT712, RT713 +
   dual RT1320)
 - AMD: DMI quirk for HP Laptop 14-em0xxx and memory leak fix in Raven
   DMA close
 - Various codec fixes for midas_wm1811, wm8994, cs35l56, cs42l56,
   WSA881x/883x/884x, es8326/es8375/es9356,  rt721/rt722, and
   tlv320aic32x4
 
 * Misc:
 - OPL3: Fix races in timer initialization, shutdown, and voice cleanup
 - Bounds checks and string helpers for AC97 and emu10k1
 - Fix spu_memset() stride in legacy SH AICA driver
 - Parameter handling fixes in pcmtest virtual driver
 -----BEGIN PGP SIGNATURE-----
 
 iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmrmFsAOHHRpd2FpQHN1
 c2UuZGUACgkQLtJE4w1nLE8DzA//WKBXzuHaYoQCel0Ky0qbTjOsLJaI63D1+w6r
 2SeeEjKIRhRVSGSuGvtMLKQ2IEFq9njhVCjnv9FK+bY2x4jrc6auSixOmXZNgplj
 f39DlJjy4ZZ6N1M9LclbiHFPR7UuRlbgXqS6UR83ijapHGqTf5eWcL1RIHmiUZ3k
 Xr6Yd2Q58u7sMRdwLIoXYum7VD/46iSTS/SQT5Iq8eQij7YVlVTglxUPTouCVWI7
 rNm9TZe5VggJ77ADI2Fo3VRI+QhQU/BohZ7R155fpqroCvGKLmWppypkCqpxtc/u
 DnVGI7Cq/TDoBh2u4GrtBhx+voyt84X2N1epJ6vZzp5VZc6cjBnKFynvsxuGH4ox
 0hb9Syu70rUAtulJwefRuftXv/gl0coZJCS1NoPsoKy3pEBGHExjgBoCZv7oCNH9
 chk4n7XFEb7yktN5bd6MG7yhdF1U6jwkpFyDphBTb992eaB0KTylx/JcmOzwxSun
 MKcAq8jsigvoxfvjGhE1OugtOg0qgdNgW9vCD1xXwSaNRXFEGNoBHWE33Y4nq3GW
 e5PqUkYI6k2cAVyosxS3N9h9H/5FfCQynC1VZECl7gdFKvhNgHVAFuVLzbQ/Hn8K
 vYitKNrVdC07EubPuOyOiDqEMCqvtAv7acH11ABMkKdnqwbbi0MzZB15s1lt1vzh
 er1a04E=
 =zbju
 -----END PGP SIGNATURE-----

Merge tag 'sound-fix-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "A collection of fixes across the sound subsystem. Nothing particularly
  stands out, mostly driver- and device-specific fixes along with a few
  race / UAF fixes and cleanups. Here are some highlights:

  ALSA core:
   - Fix potential race in PCM proc handling at disconnection
   - Fix potential race in UMP sequencer callback at releasing

  HD-audio:
   - Reject codec reset/reconfig on ASoC buses to prevent deadlock
   - Force MSI for Hygon H34M51A board
   - Usual Realtek quirks for ASUS and HP laptops/AiO

  USB-audio:
   - Bound packet copies by PCM buffer size in generic and ua101 drivers
   - Rate-limit URB submission errors at stream start
   - Skip redundant probe-time interface setup on MSI ALC4080
   - Device quirks for Novation UltraNova, MOTU MicroBook II, and RYDZ
     Headphone

  ASoC:
   - Fixes for runtime PM and IRQ handling in hdac_hda and SDCA
   - SOF: Fix potential UAF at release, fix for Kconfig dependency
   - Qualcomm: Fixes for sm8250 and q6apm/q6afe
   - NXP / i.MX: Fix for pending work in hw_free
   - Xilinx: Fix read-modify-write of AUD_CTRL in formatter_pcm
   - Intel: Arrow Lake SoundWire match table additions (RT712, RT713 +
     dual RT1320)
   - AMD: DMI quirk for HP Laptop 14-em0xxx and memory leak fix in Raven
     DMA close
   - Various codec fixes for midas_wm1811, wm8994, cs35l56, cs42l56,
     WSA881x/883x/884x, es8326/es8375/es9356, rt721/rt722, and
     tlv320aic32x4

  Misc:
   - OPL3: Fix races in timer initialization, shutdown, and voice
     cleanup
   - Bounds checks and string helpers for AC97 and emu10k1
   - Fix spu_memset() stride in legacy SH AICA driver
   - Parameter handling fixes in pcmtest virtual driver"

* tag 'sound-fix-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (71 commits)
  ASoC: SOF: Intel: hda: Stop the controller before freeing its DMA buffers
  ASoC: imx-pcm-rpmsg: Flush pending work in hw_free
  ASoC: xilinx: formatter_pcm: Fix read-modify-write of AUD_CTRL in hw_params
  ASoC: hdac_hda: Keep runtime PM state in sync across codec remove/probe
  ASoC: rt721: Remove invalid register defaults
  ASoC: hdac_hda: Balance the runtime PM disable in codec remove
  ASoC: hdac_hda: Restore the codec device type on unbind
  ASoC: hdac_hda: Allocate the private data at codec driver bind
  ASoC: qcom: sm8250: Limit RX digital gain to 0 dB
  ALSA: hda: Reject codec reset on ASoC (ext_ops) buses
  ALSA: usb-audio: add quirk for Novation UltraNova (1235:0011)
  ALSA: usb-audio: Add correct datainterval in the quirks-table for Microbook II
  ALSA: usb-audio: Skip probe-time interface setup for MSI MAG X570S ALC4080
  ALSA: hda/realtek: Extend quirk for HP Pavilion All-in-One 27-ca1xxx
  ALSA: pcmtest: Honor the enable parameter
  ALSA: ua101: Bound packet copies by the PCM buffer size
  ALSA: usb-audio: Bound packet copies by the PCM buffer size
  ASoC: SDCA: re-enable IRQ on pm_runtime_force_suspend failure
  ASoC: SDCA: drop pm_runtime reference on probe failure path
  ASoC: SDCA: propagate sdw_slave_read_prop() failure in class_read_prop
  ...
This commit is contained in:
Linus Torvalds 2026-10-31 12:30:31 -07:00
commit e9954f3fbd
61 changed files with 1017 additions and 321 deletions

View File

@ -209,6 +209,9 @@ struct snd_ctl_elem_value;
#define CS35L56_GPIO_FN_GPIO 0x00000001
/* DSP1_FW_VER */
#define CS35L56_FW_MAIN_VERSION_MASK GENMASK(19, 0)
/* Mixer input sources */
#define CS35L56_INPUT_SRC_NONE 0x00
#define CS35L56_INPUT_SRC_ASP1RX1 0x08
@ -446,6 +449,7 @@ int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base,
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
bool *fw_missing, unsigned int *fw_version);
void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base);
bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base);
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);

View File

@ -117,6 +117,7 @@ struct snd_info_entry *snd_info_create_module_entry(struct module *module,
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
struct snd_info_entry *parent);
void snd_info_disconnect_entry(struct snd_info_entry *entry);
void snd_info_free_entry(struct snd_info_entry *entry);
int snd_info_card_create(struct snd_card *card);
@ -164,6 +165,7 @@ static inline int snd_info_get_line(struct snd_info_buffer *buffer, char *line,
static inline char *snd_info_get_str(char *dest, char *src, int len) { return NULL; }
static inline struct snd_info_entry *snd_info_create_module_entry(struct module *module, const char *name, struct snd_info_entry *parent) { return NULL; }
static inline struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, const char *name, struct snd_info_entry *parent) { return NULL; }
static inline void snd_info_disconnect_entry(struct snd_info_entry *entry) { }
static inline void snd_info_free_entry(struct snd_info_entry *entry) { ; }
static inline int snd_info_card_create(struct snd_card *card) { return 0; }

View File

@ -66,6 +66,7 @@ struct snd_ump_endpoint {
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
struct snd_seq_device *seq_dev;
/* Published after seq_client; calls are covered by UMP core SRCU. */
const struct snd_seq_ump_ops *seq_ops;
void *seq_client;
#endif
@ -126,6 +127,10 @@ int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val);
int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol);
void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump);
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
void snd_ump_detach_seq(struct snd_ump_endpoint *ump);
#endif
/*
* Some definitions for UMP
*/

View File

@ -737,6 +737,23 @@ static void snd_info_clear_entries(struct snd_info_entry *entry)
entry->p = NULL;
}
/**
* snd_info_disconnect_entry - remove proc nodes and drain their callbacks
* @entry: root of the info entry subtree
*
* Retains the info entries for snd_info_free_entry(). Callers must serialize
* registration and destruction, and must not hold locks needed by callbacks.
*/
void snd_info_disconnect_entry(struct snd_info_entry *entry)
{
if (entry && entry->p) {
proc_remove(entry->p);
guard(mutex)(&info_mutex);
snd_info_clear_entries(entry);
}
}
EXPORT_SYMBOL(snd_info_disconnect_entry);
/**
* snd_info_free_entry - release the info entry
* @entry: the info entry
@ -749,11 +766,7 @@ void snd_info_free_entry(struct snd_info_entry * entry)
if (!entry)
return;
if (entry->p) {
proc_remove(entry->p);
guard(mutex)(&info_mutex);
snd_info_clear_entries(entry);
}
snd_info_disconnect_entry(entry);
/* free all children at first */
list_for_each_entry_safe(p, n, &entry->children, list)

View File

@ -520,6 +520,11 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
return 0;
}
static void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr)
{
snd_info_disconnect_entry(pstr->proc_root);
}
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{
snd_info_free_entry(pstr->proc_root);
@ -580,6 +585,7 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
#else /* !CONFIG_SND_VERBOSE_PROCFS */
static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
static inline void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) { }
static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
#endif /* CONFIG_SND_VERBOSE_PROCFS */
@ -1094,33 +1100,37 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
int cidx;
guard(mutex)(&register_mutex);
guard(mutex)(&pcm->open_mutex);
wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
scoped_guard(mutex, &pcm->open_mutex) {
wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
for_each_pcm_substream(pcm, cidx, substream) {
snd_pcm_stream_lock_irq(substream);
if (substream->runtime) {
if (snd_pcm_running(substream))
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
/* to be sure, set the state unconditionally */
__snd_pcm_set_state(substream->runtime,
SNDRV_PCM_STATE_DISCONNECTED);
wake_up(&substream->runtime->sleep);
wake_up(&substream->runtime->tsleep);
for_each_pcm_substream(pcm, cidx, substream) {
snd_pcm_stream_lock_irq(substream);
if (substream->runtime) {
if (snd_pcm_running(substream))
snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
/* to be sure, set the state unconditionally */
__snd_pcm_set_state(substream->runtime,
SNDRV_PCM_STATE_DISCONNECTED);
wake_up(&substream->runtime->sleep);
wake_up(&substream->runtime->tsleep);
}
snd_pcm_stream_unlock_irq(substream);
}
snd_pcm_stream_unlock_irq(substream);
}
for_each_pcm_substream(pcm, cidx, substream)
snd_pcm_sync_stop(substream, false);
for_each_pcm_substream(pcm, cidx, substream)
snd_pcm_sync_stop(substream, false);
pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
if (pcm->streams[cidx].dev)
snd_unregister_device(pcm->streams[cidx].dev);
free_chmap(&pcm->streams[cidx]);
pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
if (pcm->streams[cidx].dev)
snd_unregister_device(pcm->streams[cidx].dev);
free_chmap(&pcm->streams[cidx]);
}
}
/* Proc rundown can run release callbacks which take open_mutex. */
for (cidx = 0; cidx < 2; cidx++)
snd_pcm_stream_proc_disconnect(&pcm->streams[cidx]);
return 0;
}

View File

@ -353,12 +353,12 @@ static int create_ump_endpoint_port(struct seq_ump_client *client)
/* release the client resources */
static void seq_ump_client_free(struct seq_ump_client *client)
{
snd_ump_detach_seq(client->ump);
cancel_work_sync(&client->group_notify_work);
if (client->seq_client >= 0)
snd_seq_delete_kernel_client(client->seq_client);
client->ump->seq_ops = NULL;
client->ump->seq_client = NULL;
kfree(client);
@ -523,7 +523,8 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev)
snd_seq_kernel_client_put(cptr);
ump->seq_client = client;
ump->seq_ops = &seq_ump_ops;
/* Publish the initialized client before callbacks can observe seq_ops. */
smp_store_release(&ump->seq_ops, &seq_ump_ops);
return 0;
error:

View File

@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/srcu.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include <sound/ump.h>
@ -18,6 +19,31 @@
#define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args)
#define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args)
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
DEFINE_STATIC_SRCU(ump_seq_srcu);
static const struct snd_seq_ump_ops *seq_ops_load(struct snd_ump_endpoint *ump)
{
/* Pair with publication after seq_client has been initialized. */
return smp_load_acquire(&ump->seq_ops);
}
/**
* snd_ump_detach_seq - stop callbacks to a sequencer binding
* @ump: UMP endpoint
*
* Withdraw the binding and wait for all callbacks already in progress.
* The caller can then drain binding work and free the sequencer client.
* Must not be called from one of the binding callbacks.
*/
void snd_ump_detach_seq(struct snd_ump_endpoint *ump)
{
WRITE_ONCE(ump->seq_ops, NULL);
synchronize_srcu(&ump_seq_srcu);
}
EXPORT_SYMBOL_GPL(snd_ump_detach_seq);
#endif
static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
@ -348,8 +374,13 @@ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
continue;
ump_handle_stream_msg(ump, ump->input_buf, n);
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (ump->seq_ops)
ump->seq_ops->input_receive(ump, ump->input_buf, n);
scoped_guard(srcu, &ump_seq_srcu) {
const struct snd_seq_ump_ops *ops;
ops = seq_ops_load(ump);
if (ops)
ops->input_receive(ump, ump->input_buf, n);
}
#endif
process_legacy_input(ump, ump->input_buf, n);
}
@ -559,6 +590,7 @@ static void snd_ump_proc_read(struct snd_info_entry *entry,
/* update dir_bits and active flag for all groups in the client */
void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
{
static const char separator[] = ", ";
struct snd_ump_block *fb;
struct snd_ump_group *group;
int i;
@ -598,7 +630,8 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
if (!*fb->info.name)
continue;
if (*group->name)
strlcat(group->name, ", ", sizeof(group->name));
safe_append_string(group->name, sizeof(group->name),
separator, sizeof(separator));
safe_append_string(group->name, sizeof(group->name),
fb->info.name, sizeof(fb->info.name));
}
@ -699,8 +732,12 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump)
static void seq_notify_ep_change(struct snd_ump_endpoint *ump)
{
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change)
ump->seq_ops->notify_ep_change(ump);
const struct snd_seq_ump_ops *ops;
guard(srcu)(&ump_seq_srcu);
ops = seq_ops_load(ump);
if (ump->parsed && ops && ops->notify_ep_change)
ops->notify_ep_change(ump);
#endif
}
@ -797,8 +834,12 @@ static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
static void seq_notify_protocol(struct snd_ump_endpoint *ump)
{
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (ump->seq_ops && ump->seq_ops->switch_protocol)
ump->seq_ops->switch_protocol(ump);
const struct snd_seq_ump_ops *ops;
guard(srcu)(&ump_seq_srcu);
ops = seq_ops_load(ump);
if (ops && ops->switch_protocol)
ops->switch_protocol(ump);
#endif /* CONFIG_SND_SEQUENCER */
}
@ -893,8 +934,12 @@ static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
struct snd_ump_block *fb)
{
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (ump->seq_ops && ump->seq_ops->notify_fb_change)
ump->seq_ops->notify_fb_change(ump, fb);
const struct snd_seq_ump_ops *ops;
guard(srcu)(&ump_seq_srcu);
ops = seq_ops_load(ump);
if (ops && ops->notify_fb_change)
ops->notify_fb_change(ump, fb);
#endif
}

View File

@ -632,6 +632,21 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
}
void snd_opl3_clear_voices(struct snd_opl3 *opl3)
{
int i;
guard(spinlock_irqsave)(&opl3->voice_lock);
/* A 4-op primary clears its higher-numbered partner's state. */
for (i = MAX_OPL3_VOICES - 1; i >= 0; i--) {
if (opl3->voices[i].state > 0)
snd_opl3_kill_voice(opl3, i);
opl3->voices[i].chan = NULL;
opl3->voices[i].note_off_check = 0;
opl3->voices[i].state = SNDRV_OPL3_ST_OFF;
}
}
/*
* Release a note in response to a midi note off.
*/

View File

@ -41,6 +41,7 @@ static void snd_opl3_oss_free_port(void *private_data)
{
struct snd_opl3 *opl3 = private_data;
snd_opl3_clear_voices(opl3);
snd_midi_channel_free_set(opl3->oss_chset);
}

View File

@ -152,6 +152,7 @@ static void snd_opl3_synth_free_port(void *private_data)
{
struct snd_opl3 *opl3 = private_data;
snd_opl3_clear_voices(opl3);
snd_midi_channel_free_set(opl3->chset);
}
@ -214,6 +215,11 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev)
spin_lock_init(&opl3->voice_lock);
/* Initialize the timer before an attached port can receive events. */
timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
spin_lock_init(&opl3->sys_timer_lock);
opl3->sys_timer_status = 0;
opl3->seq_client = -1;
/* allocate new client */
@ -232,11 +238,6 @@ static int snd_opl3_seq_probe(struct snd_seq_device *dev)
return err;
}
/* setup system timer */
timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
spin_lock_init(&opl3->sys_timer_lock);
opl3->sys_timer_status = 0;
#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_init_seq_oss(opl3, name);
#endif
@ -258,6 +259,8 @@ static void snd_opl3_seq_remove(struct snd_seq_device *dev)
snd_seq_delete_kernel_client(opl3->seq_client);
opl3->seq_client = -1;
}
/* Stop the shared timer after all event callbacks have been drained. */
timer_shutdown_sync(&opl3->tlist);
}
static struct snd_seq_driver opl3_seq_driver = {

View File

@ -13,6 +13,7 @@ int snd_opl3_synth_use_inc(struct snd_opl3 * opl3);
void snd_opl3_synth_use_dec(struct snd_opl3 * opl3);
int snd_opl3_synth_setup(struct snd_opl3 * opl3);
void snd_opl3_synth_cleanup(struct snd_opl3 * opl3);
void snd_opl3_clear_voices(struct snd_opl3 *opl3);
/* Prototypes for opl3_midi.c */
void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan);

View File

@ -72,13 +72,32 @@ static u8 playback_capture_test;
static u8 ioctl_reset_test;
static struct dentry *driver_debug_dir;
static int fill_mode_set(const char *val, const struct kernel_param *kp)
{
short mode;
int err;
err = kstrtos16(val, 0, &mode);
if (err)
return err;
if (mode != FILL_MODE_RAND && mode != FILL_MODE_PAT)
return -EINVAL;
fill_mode = mode;
return 0;
}
static const struct kernel_param_ops fill_mode_ops = {
.set = fill_mode_set,
.get = param_get_short,
};
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pcmtest soundcard");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for pcmtest soundcard");
module_param(enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable pcmtest soundcard.");
module_param(fill_mode, short, 0600);
module_param_cb(fill_mode, &fill_mode_ops, &fill_mode, 0600);
MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)");
module_param(inject_delay, int, 0600);
MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)");
@ -693,6 +712,9 @@ static int __init mod_init(void)
{
int err = 0;
if (!enable)
return -ENODEV;
buf_allocated = setup_patt_bufs();
if (!buf_allocated)
return -ENOMEM;

View File

@ -4492,6 +4492,7 @@ enum {
ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT,
ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT,
ALC245_FIXUP_HP_X360_MUTE_LEDS,
ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS,
ALC287_FIXUP_THINKPAD_I2S_SPK,
ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
ALC287_FIXUP_CS35L41_I2C_4_THINKPAD_ACPI,
@ -7113,6 +7114,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC245_FIXUP_HP_GPIO_LED
},
[ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc245_fixup_hp_mute_led_v2_coefbit,
.chained = true,
.chain_id = ALC245_FIXUP_HP_GPIO_LED
},
[ALC287_FIXUP_THINKPAD_I2S_SPK] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc287_fixup_bind_dacs,
@ -7869,6 +7876,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS),
SND_PCI_QUIRK(0x103c, 0x89e9, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS),
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@ -7951,6 +7959,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c31, "HP Pavilion Plus Laptop 14-ew1xxx", ALC245_FIXUP_HP_PAVILION_14_EW1XXX_LEDS),
SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
@ -8060,6 +8069,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
HDA_CODEC_QUIRK(0x103c, 0x84ee, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK),
SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
@ -8214,6 +8224,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15),
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, 0x1e03, "ASUS ExpertBook B3402FVA", ALC256_FIXUP_ASUS_HPE),
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),
@ -8226,6 +8237,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
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, 0x1ec1, "ASUS X509JB", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", 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),

View File

@ -1807,6 +1807,12 @@ int snd_hda_codec_reset(struct hda_codec *codec)
{
struct hda_bus *bus = codec->bus;
/* ASoC: driver unbind frees the card, incl. the hwdep issuing the reset */
if (bus->core.ext_ops) {
codec_err(codec, "Codec reset is not supported with ASoC\n");
return -EOPNOTSUPP;
}
if (snd_hda_lock_devices(bus) < 0)
return -EBUSY;

View File

@ -117,6 +117,8 @@ static int clear_codec(struct hda_codec *codec)
int err;
err = snd_hda_codec_reset(codec);
if (err == -EOPNOTSUPP)
return err;
if (err < 0) {
codec_err(codec, "The codec is being used, can't free.\n");
return err;
@ -132,6 +134,8 @@ static int reconfig_codec(struct hda_codec *codec)
CLASS(snd_hda_power, pm)(codec);
codec_info(codec, "hda-codec: reconfiguring\n");
err = snd_hda_codec_reset(codec);
if (err == -EOPNOTSUPP)
return err;
if (err < 0) {
codec_err(codec,
"The codec is being used, can't reconfigure.\n");

View File

@ -1668,6 +1668,7 @@ static const struct snd_pci_quirk msi_deny_list[] = {
SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */
SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
SND_PCI_QUIRK(0x1d94, 0xc950, "Hygon H34M51A", 1), /* INTx broken */
{}
};
@ -2329,6 +2330,7 @@ static const struct snd_pci_quirk power_save_denylist[] = {
SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0),
/* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */
SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0),
SND_PCI_QUIRK(0x103c, 0x89e9, "HP 89E9", 0),
{}
};

View File

@ -1832,36 +1832,39 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name,
size_t maxlen, int modem)
{
const struct ac97_codec_id *pid;
size_t len;
sprintf(name, "0x%x %c%c%c", id,
printable(id >> 24),
printable(id >> 16),
printable(id >> 8));
pid = look_for_codec_id(snd_ac97_codec_id_vendors, id);
if (! pid)
if (!pid) {
snprintf(name, maxlen, "0x%x %c%c%c", id,
printable(id >> 24),
printable(id >> 16),
printable(id >> 8));
return;
}
strscpy(name, pid->name, maxlen);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
(!modem && !(pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
}
}
len = strnlen(name, maxlen);
pid = look_for_codec_id(snd_ac97_codec_ids, id);
if (pid) {
strlcat(name, " ", maxlen);
strlcat(name, pid->name, maxlen);
if (pid->mask != 0xffffffff)
sprintf(name + strlen(name), " rev %u", id & ~pid->mask);
snprintf(name + len, maxlen - len, " %s rev %u",
pid->name, id & ~pid->mask);
else
snprintf(name + len, maxlen - len, " %s", pid->name);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
(!modem && !(pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
}
} else {
int l = strlen(name);
snprintf(name + l, maxlen - l, " id %x", id & 0xff);
snprintf(name + len, maxlen - len, " id %x", id & 0xff);
}
}

View File

@ -2423,12 +2423,12 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file)
return 0;
}
static void copy_string(char *dst, const char *src, const char *null, int idx)
static void copy_string(char *dst, size_t size, const char *src, const char *null, int idx)
{
if (src == NULL)
sprintf(dst, "%s %02X", null, idx);
snprintf(dst, size, "%s %02X", null, idx);
else
strcpy(dst, src);
strscpy(dst, src, size);
}
static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
@ -2446,12 +2446,16 @@ static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res);
copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
copy_string(info->fxbus_names[res], sizeof(info->fxbus_names[res]),
*fxbus, "FXBUS", res);
copy_string(info->extin_names[res], sizeof(info->extin_names[res]),
extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
copy_string(info->extout_names[res], sizeof(info->extout_names[res]),
extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
}
for (res = 16; res < 32; res++, extout++)
copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
copy_string(info->extout_names[res], sizeof(info->extout_names[res]),
extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
info->gpr_controls = emu->fx8010.gpr_count;
}

View File

@ -94,7 +94,7 @@ static void spu_memset(u32 toi, u32 what, int length)
local_irq_save(flags);
writel(what, toi + SPU_MEMORY_BASE);
local_irq_restore(flags);
toi++;
toi += sizeof(what);
}
}

View File

@ -360,6 +360,8 @@ static int acp3x_dma_close(struct snd_soc_component *component,
}
}
kfree(ins);
return 0;
}

View File

@ -759,6 +759,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "8BD6"),
}
},
{
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
DMI_MATCH(DMI_BOARD_NAME, "8DC2"),
}
},
{
.driver_data = &acp6x_card,
.matches = {

View File

@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
@ -36,6 +37,9 @@
#define CS35L56_LATE_READ_POLL_US 10
#define CS35L56_LATE_READ_TIMEOUT_US 1000
static DEFINE_MUTEX(cs35l56_fw_idle_wait_lock);
static bool cs35l56_fw_idle_wait_pending;
static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral,
unsigned int mask,
unsigned int match)
@ -387,14 +391,34 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
return 0;
}
static int cs35l56_sdw_system_suspend_prepare(struct device *dev)
{
cs35l56_fw_idle_wait_pending = true;
return 0;
}
static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
int ret;
if (cs35l56->sdw_attached)
cs35l56_mask_soundwire_interrupts(cs35l56);
return cs35l56_system_suspend(dev);
ret = cs35l56_system_suspend(dev);
if (ret < 0)
return ret;
scoped_guard(mutex, &cs35l56_fw_idle_wait_lock) {
if (cs35l56_fw_idle_wait_pending && cs35l56->needs_wait_for_fw_idle) {
dev_dbg(cs35l56->base.dev, "Wait for FW timer expiry\n");
msleep(CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 10);
cs35l56_fw_idle_wait_pending = false;
}
}
return 0;
}
static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
@ -460,6 +484,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral)
}
static const struct dev_pm_ops cs35l56_sdw_pm = {
.prepare = cs35l56_sdw_system_suspend_prepare,
SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume)
LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)

View File

@ -1379,6 +1379,44 @@ void cs35l56_warn_if_firmware_missing(struct cs35l56_base *cs35l56_base)
}
EXPORT_SYMBOL_NS_GPL(cs35l56_warn_if_firmware_missing, "SND_SOC_CS35L56_SHARED");
bool cs35l56_needs_wait_for_firmware_timer_expiry(struct cs35l56_base *cs35l56_base)
{
unsigned int fw_ver;
bool prot_sts;
int ret;
switch (cs35l56_base->type) {
case 0x54:
case 0x56:
case 0x57:
switch (cs35l56_base->rev) {
case 0xb0:
ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver);
if (ret)
return true;
if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x30d07)
return true;
return false;
case 0xb2:
ret = cs35l56_read_prot_status(cs35l56_base, &prot_sts, &fw_ver);
if (ret)
return true;
if ((fw_ver & CS35L56_FW_MAIN_VERSION_MASK) < 0x40710)
return true;
return false;
default:
return false;
}
default:
return false;
}
}
EXPORT_SYMBOL_NS_GPL(cs35l56_needs_wait_for_firmware_timer_expiry, "SND_SOC_CS35L56_SHARED");
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
{
__be32 pid, sid, tid;

View File

@ -1703,6 +1703,10 @@ int cs35l56_system_resume(struct device *dev)
dev_dbg(dev, "system_resume\n");
/* Assume the firmware reset to default until we can check it */
if (cs35l56->base.init_done)
cs35l56->needs_wait_for_fw_idle = true;
/*
* We might have done a hard reset or the CS35L56 was power-cycled
* so wait for control port to be ready.
@ -1717,6 +1721,16 @@ int cs35l56_system_resume(struct device *dev)
if (ret)
return ret;
if (cs35l56->sdw_peripheral && cs35l56->base.init_done) {
ret = pm_runtime_resume_and_get(cs35l56->base.dev);
if (ret < 0)
return ret;
cs35l56->needs_wait_for_fw_idle =
cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base);
pm_runtime_put_autosuspend(cs35l56->base.dev);
}
/* Firmware won't have been loaded if the component hasn't probed */
if (!cs35l56->component)
return 0;
@ -2241,6 +2255,9 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
if (ret)
return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n");
cs35l56->needs_wait_for_fw_idle =
cs35l56_needs_wait_for_firmware_timer_expiry(&cs35l56->base);
cs35l56->base.init_done = true;
complete_all(&cs35l56->init_completion);

View File

@ -43,6 +43,7 @@ struct cs35l56_private {
bool soft_resetting;
bool sdw_attached;
bool component_registered;
bool needs_wait_for_fw_idle;
struct completion init_completion;
int speaker_id;

View File

@ -419,7 +419,6 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv),
SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR,
CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv),
SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1),
SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,

View File

@ -353,7 +353,7 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
SOC_SINGLE_TLV("DRE Gate Volume", ES8326_ADC_DRE_GATE,
4, 0x07, 0, dre_gate_tlv),
SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0),
SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL,
SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_RECOVERY,
0, 4, 0, drc_recovery_tlv),
SOC_ENUM("ALC Capture Winsize", alc_winsize),
SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,

View File

@ -199,68 +199,68 @@ struct _coeff_div {
static const struct _coeff_div coeff_div[] = {
{32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
{32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
{32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{32, 1536000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
{36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
{48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
{48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
{48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2},
{50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
{50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
{50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
{64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
{64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
{64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
{72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
{72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
{72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2},
{96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
{96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
{96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
{100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
{100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2},
{100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
{128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2},
{128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
{128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
{144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2},
{144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
{144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
{192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2},
{192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
{192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
{250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
{256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
{256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
{256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
{384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
{384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
{384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
{400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
{500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
{512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
{512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
{512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
{768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
{768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
{1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
{1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
{1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
{1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
{1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
{1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
{1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
{1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
{2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2},
{2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2},
{2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2},
{3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2},
{32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
{64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
{96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
{128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2},
{36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{36, 1728000, 48000, 0x05, 0xB3, 0xD5, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2},
{48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2},
{48, 2304000, 48000, 0x05, 0x91, 0x53, 0x55, 0x17, 0x20, 0x95, 0x00, 0x28, 2, 2},
{50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{50, 2400000, 48000, 0x05, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{64, 3072000, 48000, 0x05, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{72, 3456000, 48000, 0x05, 0x91, 0x53, 0x33, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
{96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{96, 4608000, 48000, 0x15, 0xB3, 0xD5, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
{100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{100, 4800000, 48000, 0x03, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x00, 0x27, 2, 2},
{128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2},
{128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2},
{128, 6144000, 48000, 0x03, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x01, 0x1F, 2, 2},
{144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2},
{144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2},
{144, 6912000, 48000, 0x03, 0x80, 0x31, 0x11, 0x23, 0x08, 0x95, 0x01, 0x1F, 2, 2},
{192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2},
{192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2},
{192, 9216000, 48000, 0x15, 0x91, 0x53, 0x33, 0x1F, 0x00, 0x95, 0x02, 0x1F, 2, 2},
{250, 12000000, 48000, 0x25, 0x91, 0x53, 0x55, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2},
{256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2},
{256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2},
{256, 12288000, 48000, 0x0B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x03, 0x1F, 2, 2},
{384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2},
{384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2},
{384, 18432000, 48000, 0x13, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x05, 0x1F, 2, 2},
{400, 19200000, 48000, 0x1B, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2},
{500, 24000000, 48000, 0x23, 0x80, 0x31, 0x33, 0x18, 0x24, 0x95, 0x04, 0x27, 2, 2},
{512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2},
{512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2},
{512, 24576000, 48000, 0x1B, 0x80, 0x31, 0x11, 0x1F, 0x00, 0x95, 0x07, 0x1F, 2, 2},
{768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2},
{768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0B, 0x1F, 2, 2},
{1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2},
{1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2},
{1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2},
{1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x0F, 0x1F, 2, 2},
{1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2},
{1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x11, 0x27, 2, 2},
{1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2},
{1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x95, 0x17, 0x1F, 2, 2},
{2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x1F, 0x1F, 2, 2},
{2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x23, 0x1F, 2, 2},
{2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x95, 0x25, 0x27, 2, 2},
{3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x95, 0x2F, 0x1F, 2, 2},
{32, 3072000, 96000, 0x05, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2},
{64, 6144000, 96000, 0x03, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2},
{96, 9216000, 96000, 0x15, 0x91, 0x53, 0x55, 0x0F, 0x00, 0x95, 0x00, 0x37, 2, 2},
{128, 12288000, 96000, 0x0B, 0x80, 0x31, 0x33, 0x0F, 0x00, 0x95, 0x01, 0x37, 2, 2},
};
static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
@ -543,18 +543,20 @@ static void es8375_init(struct snd_soc_component *component)
regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02);
regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05);
regmap_write(es8375->regmap, ES8375_CSM1, 0x82);
regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20);
regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20);
regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28);
regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC);
regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x10);
regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x10);
regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x2F);
regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xEC);
regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0);
regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE);
regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8);
regmap_write(es8375->regmap, ES8375_VSEL2, 0x05);
regmap_write(es8375->regmap, ES8375_LP_DAC, 0xCC);
regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03);
regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16);
regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
msleep(80);
regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00);
regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x08);
regmap_write(es8375->regmap, ES8375_CSM1, 0x86);
regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B);
regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00);
@ -566,7 +568,7 @@ static void es8375_init(struct snd_soc_component *component)
regmap_write(es8375->regmap, ES8375_ADC2, 0x00);
regmap_write(es8375->regmap, ES8375_DAC2, 0x00);
regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88);
regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7);
regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE3);
regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0);
regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40);
regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE);

View File

@ -49,6 +49,9 @@
#define ES8375_ANALOG_SPK2 0x29
#define ES8375_VMID_SEL 0x2D
#define ES8375_ANALOG1 0x2E
#define ES8375_VSEL1 0x2F
#define ES8375_VSEL2 0x30
#define ES8375_LP_DAC 0x31
#define ES8375_ANALOG2 0x32
#define ES8375_ANALOG3 0x37
#define ES8375_ADC2DAC_CLKTRI 0xF8
@ -91,7 +94,7 @@
// Function values
#define ES8375_ADC_OSR_GAIN_MAX 0x3F
#define ES8375_DMIC_GAIN_MAX 0x04
#define ES8375_DMIC_GAIN_MAX 0x03
#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F
#define ES8375_AUTOMUTE_NG_MAX 0x07
#define ES8375_ADC_VOLUME_MAX 0xFF

View File

@ -352,7 +352,7 @@ static int es9356_sdca_headset_detect(struct es9356_sdw_priv *es9356)
if (reg) {
regmap_write(es9356->regmap,
SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0), reg);
regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x75);
regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x74);
} else {
regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4);
}
@ -403,7 +403,7 @@ static void es9356_interrupt_handler(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4);
mod_delayed_work(system_power_efficient_wq,
&es9356->button_detect_work, msecs_to_jiffies(280));
&es9356->button_detect_work, msecs_to_jiffies(50));
}
}
@ -450,7 +450,7 @@ static void es9356_button_detect_handler(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4);
mod_delayed_work(system_power_efficient_wq,
&es9356->button_detect_work, msecs_to_jiffies(280));
&es9356->button_detect_work, msecs_to_jiffies(50));
}
return;
@ -724,68 +724,122 @@ static bool es9356_sdca_volatile_register(struct device *dev, unsigned int reg)
}
}
static bool es9356_writeable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case ES9356_VERSION:
case ES9356_BUF_ADDR_HID:
case ES9356_HID_BYTE2:
case ES9356_HID_BYTE3:
case ES9356_HID_BYTE4:
return false;
default:
return true;
}
}
static const struct regmap_config es9356_sdca_regmap = {
.reg_bits = 32,
.val_bits = 16,
.volatile_reg = es9356_sdca_volatile_register,
.writeable_reg = es9356_writeable_register,
.max_register = 0x45ffffff,
.cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
static void es9356_register_init(struct es9356_sdw_priv *es9356)
static int es9356_register_init(struct es9356_sdw_priv *es9356)
{
unsigned int version;
int ret;
ret = regmap_read(es9356->regmap, ES9356_VERSION, &version);
if (ret < 0) {
pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
return ret;
}
regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x2c);
if (!(version >> 1))
regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x11);
else
regmap_write(es9356->regmap, ES9356_HP_VOLUME, 0x00);
regmap_write(es9356->regmap, ES9356_SPKCLK_CTL, 0xc3);
regmap_write(es9356->regmap, ES9356_STATE, 0x02);
regmap_write(es9356->regmap, ES9356_ENDPOINT_MODE, 0x24);
regmap_write(es9356->regmap, ES9356_PRE_DIV_CTL, 0x00);
regmap_write(es9356->regmap, ES9356_ADC_OSR, 0x18);
regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x13);
regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x11);
regmap_write(es9356->regmap, ES9356_DAC_OSR, 0x16);
regmap_write(es9356->regmap, ES9356_CLK_CTL, 0x0f);
regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01);
regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30);
if (!(version >> 1))
regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30);
else
regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x10);
regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01);
regmap_write(es9356->regmap, ES9356_DETCLK_CTL, 0x51);
regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x10);
regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x00);
regmap_write(es9356->regmap, ES9356_MICBIAS_CTL, 0x10);
regmap_write(es9356->regmap, ES9356_HPDETECT_CTL, 0x07);
regmap_write(es9356->regmap, ES9356_ADC_ANA, 0x30);
regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xa8);
regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xaa);
regmap_write(es9356->regmap, ES9356_ADC_INT, 0xaa);
regmap_write(es9356->regmap, ES9356_ADC_LP, 0x19);
regmap_write(es9356->regmap, ES9356_VMID1SEL, 0xbc);
regmap_write(es9356->regmap, ES9356_VMID_TIME, 0x0b);
regmap_write(es9356->regmap, ES9356_STATE_TIME, 0xbb);
regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x77);
regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x7a);
regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4);
regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15);
if ((version & GENMASK(1, 0)) == 0x03)
regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15);
else
regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x25);
regmap_write(es9356->regmap, ES9356_SPARE_REGISTER3, 0x00);
regmap_write(es9356->regmap, ES9356_KEY_PRESS_TIME, 0xff);
regmap_write(es9356->regmap, ES9356_KEY_RELEASE_TIME, 0xff);
regmap_write(es9356->regmap, ES9356_KEY_HOLD_TIME, 0x0f);
regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00);
if ((version & GENMASK(1, 0)) == 0x03)
regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x04);
else
regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00);
regmap_write(es9356->regmap, ES9356_KEYD_DETECT, 0x18);
regmap_write(es9356->regmap, ES9356_MICBIAS_RES, 0x03);
regmap_write(es9356->regmap, ES9356_BUTTON_CHARGE, 0x00);
regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x13);
regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4);
regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x1a);
if (!(version >> 1)) {
regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xc3);
} else {
regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4);
regmap_write(es9356->regmap, ES9356_SPK_SCP, 0x55);
}
regmap_write(es9356->regmap, ES9356_SPK_VOLUME, 0x33);
regmap_write(es9356->regmap, ES9356_DAC_VROI, 0x01);
regmap_write(es9356->regmap, ES9356_DAC_LP, 0x00);
regmap_write(es9356->regmap, ES9356_HP_IBIAS, 0x04);
regmap_write(es9356->regmap, ES9356_HP_LP, 0x03);
regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x65);
regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x6c);
regmap_write(es9356->regmap, ES9356_SPKBIAS_COMP, 0x09);
regmap_write(es9356->regmap, ES9356_VMID1STL, 0x00);
regmap_write(es9356->regmap, ES9356_VMID1STL, 0x01);
regmap_write(es9356->regmap, ES9356_VMID2STL, 0x00);
regmap_write(es9356->regmap, ES9356_VSEL, 0xfc);
regmap_write(es9356->regmap, ES9356_IBIASGEN, 0x10);
regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x0d);
regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x05);
regmap_write(es9356->regmap, ES9356_STATE, 0x0e);
regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x00);
regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x08);
regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE, 0x31);
regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_WINSIZE, 0x3D);
regmap_write(es9356->regmap, ES9356_DAC_AUTOMUTE_RESULT1, 0x80);
regmap_write(es9356->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
@ -806,11 +860,14 @@ static void es9356_register_init(struct es9356_sdw_priv *es9356)
SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
regmap_write(es9356->regmap,
SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME);
return 0;
}
static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
int ret = 0;
if (es9356->hw_init)
return 0;
@ -825,7 +882,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* update count of parent 'active' children */
pm_runtime_set_active(&slave->dev);
es9356_register_init(es9356);
ret = es9356_register_init(es9356);
if (ret < 0) {
regcache_cache_only(es9356->regmap, true);
goto err_out;
}
}
pm_runtime_get_noresume(&slave->dev);
@ -852,10 +913,11 @@ static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave)
es9356->hw_init = true;
err_out:
pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
return 0;
return ret;
}
static int es9356_sdw_update_status(struct sdw_slave *slave,
@ -1000,10 +1062,10 @@ static int es9356_sdw_interrupt_callback(struct sdw_slave *slave,
count++;
} while (stat != 0 && count < retry);
/* The 280 ms figure was determined through testing */
/* The 50 ms figure was determined through testing */
if (status->sdca_cascade && !es9356->disable_irq)
mod_delayed_work(system_power_efficient_wq,
&es9356->interrupt_handle_work, msecs_to_jiffies(280));
&es9356->interrupt_handle_work, msecs_to_jiffies(50));
mutex_unlock(&es9356->disable_irq_lock);
return 0;

View File

@ -4,6 +4,7 @@
#define __ES9356_H__
/*ES9356 Implementation-define*/
#define ES9356_VERSION 0x2000
#define ES9356_FLAGS_HP 0x2003
#define ES9356_CSM_RESET 0x2020
#define ES9356_FUC_RESET 0x2021
@ -37,6 +38,7 @@
#define ES9356_MICBIAS_CTL 0x2047
#define ES9356_HPDETECT_CTL 0x2048
#define ES9356_MICBIAS_RES 0x2049
#define ES9356_SPARE_REGISTER3 0x204a
/*CLK*/
#define ES9356_CLK_SEL 0x2050
@ -81,6 +83,9 @@
#define ES9356_DAC_RAMP 0x2093
#define ES9356_DAC_VPPSCALE 0x2094
#define ES9356_DAC_SWAP 0x2097
#define ES9356_DAC_AUTOMUTE 0x2098
#define ES9356_DAC_AUTOMUTE_WINSIZE 0x2099
#define ES9356_DAC_AUTOMUTE_RESULT1 0x209B
#define ES9356_SPKCMP_VPPSC 0x20A0
#define ES9356_CALIBRATION_TIME 0x20A1
#define ES9356_CALIBRATION_SETTING 0x20A2

View File

@ -514,6 +514,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
snd_hdac_display_power(hdev->bus,
HDA_CODEC_IDX_CONTROLLER, false);
/* The codec is in D0, the status is RPM_SUSPENDED after a previous unbind */
pm_runtime_set_active(&hdev->dev);
/* match for forbid call in snd_hda_codec_device_new() */
pm_runtime_allow(&hdev->dev);
@ -554,6 +557,13 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
return;
}
/*
* Resume the codec to have its link reference (dropped below) and
* codec_powered bit in the same state as after the probe.
*/
pm_runtime_get_sync(&hdev->dev);
/* prevent runtime PM callbacks while the link and the codec are torn down */
pm_runtime_disable(&hdev->dev);
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
@ -561,6 +571,12 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
driver->ops->remove(codec);
snd_hda_codec_cleanup_for_unbind(codec);
/* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */
pm_runtime_enable(&hdev->dev);
pm_runtime_put_noidle(&hdev->dev);
/* The codec is cleaned up, system suspend must skip it from now on */
pm_runtime_set_suspended(&hdev->dev);
}
static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {
@ -617,12 +633,35 @@ static const struct snd_soc_component_driver hdac_hda_hdmi_codec = {
.endianness = 1,
};
static void hdac_hda_restore_dev_type(void *data)
{
struct hdac_device *hdev = data;
hdev->type = HDA_DEV_LEGACY;
}
static int hdac_hda_dev_probe(struct hdac_device *hdev)
{
struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev);
struct hdac_hda_priv *hda_pvt;
struct hdac_ext_link *hlink;
int ret;
hda_pvt = devm_kzalloc(&hdev->dev, sizeof(*hda_pvt), GFP_KERNEL);
if (!hda_pvt)
return -ENOMEM;
hda_pvt->codec = dev_to_hda_codec(&hdev->dev);
hda_pvt->dev_index = hdev->addr;
/* Intel iDisp (HDMI/DP) codec */
if (((hdev->vendor_id >> 16) & 0xFFFF) == 0x8086)
hda_pvt->need_display_power = true;
dev_set_drvdata(&hdev->dev, hda_pvt);
/* Allow the legacy codec driver to match again after unbind */
ret = devm_add_action(&hdev->dev, hdac_hda_restore_dev_type, hdev);
if (ret)
return ret;
/* hold the ref while we probe */
hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
if (!hlink) {

View File

@ -92,7 +92,6 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re
case 0x2000026:
case 0x200002c:
case 0x200003c:
case 0x2000046:
case 0x5810000:
case 0x5810033:
case 0x5810036:
@ -174,7 +173,6 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
case 0x2000026:
case 0x200002c:
case 0x2000030:
case 0x2000046:
case 0x2000067:
case 0x2000084:
case 0x2000086:

View File

@ -63,8 +63,6 @@ static const struct reg_default rt721_sdca_reg_defaults[] = {
RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
@ -91,13 +89,11 @@ static const struct reg_default rt721_sdca_mbq_defaults[] = {
{ 0x2000003, 0x0000 },
{ 0x2000013, 0x8001 },
{ 0x200003c, 0x0000 },
{ 0x2000046, 0x3400 },
{ 0x5b10017, 0x1b07 },
{ 0x5f00044, 0x6040 },
{ 0x5f00045, 0x3333 },
{ 0x5f00048, 0x0000 },
{ 0x6100005, 0x0005 },
{ 0x6100006, 0x0000 },
{ 0x6100007, 0x0007 },
{ 0x610000d, 0x0051 },
{ 0x6100010, 0x0180 },

View File

@ -1652,6 +1652,17 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave
&soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai));
}
static const struct dmi_system_id dmi_dell_mono_spk_quirk_table[] = {
{
.ident = "Dell Pro 5 Micro P5M1270",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0EBA"),
}
},
{}
};
static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722)
{
unsigned int mic_func_status;
@ -1741,6 +1752,13 @@ static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
if (rt722->hw_vid == RT722_VB)
regmap_write(rt722->regmap, 0x2f54, 0x00);
/* This Dell machine only uses the mono speaker */
if (dmi_check_system(dmi_dell_mono_spk_quirk_table)) {
rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL2, 0x391a);
rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
RT722_MISC_CTRL2, 0x0010, 0x0010);
}
/* clear flag */
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),

View File

@ -68,9 +68,11 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_ANALOG_BIAS_CTL3 0x04
#define RT722_MISC_CTRL1 0x07
#define RT722_JD_CTRL1 0x09
#define RT722_MISC_CTRL2 0x0d
#define RT722_LDO2_3_CTL1 0x0e
#define RT722_LDO1_CTL 0x1a
#define RT722_HP_JD_CTRL 0x24
#define RT722_CLSD_CTRL2 0x38
#define RT722_CLSD_CTRL6 0x3c
#define RT722_COMBO_JACK_AUTO_CTL1 0x45
#define RT722_COMBO_JACK_AUTO_CTL2 0x46

View File

@ -318,6 +318,9 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
u8 divisor;
if (!rate)
return -EINVAL;
divisor = DIV_ROUND_UP(parent_rate, rate);
if (divisor > AIC32X4_DIV_MAX)
return -EINVAL;
@ -331,8 +334,11 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw,
{
unsigned long divisor;
if (!req->rate)
return -EINVAL;
divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate);
if (divisor > AIC32X4_DIV_MAX)
if (!divisor || divisor > AIC32X4_DIV_MAX)
return -EINVAL;
req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor);

View File

@ -163,9 +163,12 @@ static void wm8958_micd_set_rate(struct snd_soc_component *component)
static int configure_aif_clock(struct snd_soc_component *component, int aif)
{
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct clk *mclk = NULL;
bool switch_mclk;
int rate;
int reg1 = 0;
int offset;
int ret;
if (aif)
offset = 4;
@ -174,11 +177,13 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
switch (wm8994->sysclk[aif]) {
case WM8994_SYSCLK_MCLK1:
mclk = wm8994->mclk[WM8994_MCLK1].clk;
rate = wm8994->mclk_rate[0];
break;
case WM8994_SYSCLK_MCLK2:
reg1 |= 0x8;
mclk = wm8994->mclk[WM8994_MCLK2].clk;
rate = wm8994->mclk_rate[1];
break;
@ -196,6 +201,20 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
return -EINVAL;
}
/*
* If the AIF clock is running, enable the new MCLK before switching
* to it and release the previous one afterwards.
*/
switch_mclk = wm8994->aif_clk_on[aif] && mclk != wm8994->aif_mclk[aif];
if (switch_mclk) {
ret = clk_prepare_enable(mclk);
if (ret < 0) {
dev_err(component->dev,
"Failed to enable MCLK for AIF%d\n", aif + 1);
return ret;
}
}
if (rate >= 13500000) {
rate /= 2;
reg1 |= WM8994_AIF1CLK_DIV;
@ -210,6 +229,11 @@ static int configure_aif_clock(struct snd_soc_component *component, int aif)
WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
reg1);
if (switch_mclk) {
clk_disable_unprepare(wm8994->aif_mclk[aif]);
wm8994->aif_mclk[aif] = mclk;
}
return 0;
}
@ -1059,13 +1083,25 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl
unsigned int offset, val, clk_idx;
int ret;
/*
* The AIF clock source may be changed while the AIF clock is
* running, so release the MCLK which has been enabled for it
* instead of the one currently selected.
*/
if (!enable) {
clk_disable_unprepare(wm8994->aif_mclk[aif]);
wm8994->aif_mclk[aif] = NULL;
wm8994->aif_clk_on[aif] = false;
return 0;
}
if (aif)
offset = 4;
else
offset = 0;
val = snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1 + offset);
val &= WM8994_AIF1CLK_SRC_MASK;
val = (val & WM8994_AIF1CLK_SRC_MASK) >> WM8994_AIF1CLK_SRC_SHIFT;
switch (val) {
case 0:
@ -1075,19 +1111,18 @@ static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enabl
clk_idx = WM8994_MCLK2;
break;
default:
wm8994->aif_clk_on[aif] = true;
return 0;
}
if (enable) {
ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
if (ret < 0) {
dev_err(component->dev, "Failed to enable MCLK%d\n",
clk_idx);
return ret;
}
} else {
clk_disable_unprepare(wm8994->mclk[clk_idx].clk);
ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk);
if (ret < 0) {
dev_err(component->dev, "Failed to enable MCLK%d\n",
clk_idx + 1);
return ret;
}
wm8994->aif_mclk[aif] = wm8994->mclk[clk_idx].clk;
wm8994->aif_clk_on[aif] = true;
return 0;
}
@ -2199,6 +2234,44 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
return 0;
}
/*
* If SYSCLK is less than 50kHz adjust AIFnCLK dividers for detection,
* restoring the original ones once SYSCLK is fast enough again.
*/
static void wm8994_update_aif_clk_rate(struct snd_soc_component *component)
{
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
/* Don't overwrite the saved dividers with the 128fs ones */
if (!wm8994->aifdiv[0]) {
dev_dbg(component->dev, "Configuring AIFs for 128fs\n");
wm8994->aifdiv[0] = snd_soc_component_read(component,
WM8994_AIF1_RATE)
& WM8994_AIF1CLK_RATE_MASK;
wm8994->aifdiv[1] = snd_soc_component_read(component,
WM8994_AIF2_RATE)
& WM8994_AIF1CLK_RATE_MASK;
}
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
} else if (wm8994->aifdiv[0]) {
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK,
wm8994->aifdiv[0]);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK,
wm8994->aifdiv[1]);
wm8994->aifdiv[0] = 0;
wm8994->aifdiv[1] = 0;
}
}
static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
unsigned int freq_in, unsigned int freq_out)
{
@ -2433,33 +2506,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
configure_clock(component);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
dev_dbg(component->dev, "Configuring AIFs for 128fs\n");
wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE)
& WM8994_AIF1CLK_RATE_MASK;
wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE)
& WM8994_AIF1CLK_RATE_MASK;
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
} else if (wm8994->aifdiv[0]) {
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK,
wm8994->aifdiv[0]);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK,
wm8994->aifdiv[1]);
wm8994->aifdiv[0] = 0;
wm8994->aifdiv[1] = 0;
}
wm8994_update_aif_clk_rate(component);
return 0;
}
@ -2519,7 +2566,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
case WM8994_SYSCLK_MCLK1:
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK1, &freq);
if (ret < 0)
return ret;
@ -2532,7 +2579,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
/* TODO: Set GPIO AF */
wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq);
ret = wm8994_set_mclk_rate(wm8994, WM8994_MCLK2, &freq);
if (ret < 0)
return ret;
@ -2577,33 +2624,7 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
configure_clock(component);
/*
* If SYSCLK will be less than 50kHz adjust AIFnCLK dividers
* for detection.
*/
if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) {
dev_dbg(component->dev, "Configuring AIFs for 128fs\n");
wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE)
& WM8994_AIF1CLK_RATE_MASK;
wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE)
& WM8994_AIF1CLK_RATE_MASK;
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK, 0x1);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK, 0x1);
} else if (wm8994->aifdiv[0]) {
snd_soc_component_update_bits(component, WM8994_AIF1_RATE,
WM8994_AIF1CLK_RATE_MASK,
wm8994->aifdiv[0]);
snd_soc_component_update_bits(component, WM8994_AIF2_RATE,
WM8994_AIF2CLK_RATE_MASK,
wm8994->aifdiv[1]);
wm8994->aifdiv[0] = 0;
wm8994->aifdiv[1] = 0;
}
wm8994_update_aif_clk_rate(component);
return 0;
}

View File

@ -81,6 +81,8 @@ struct wm8994_priv {
struct wm_hubs_data hubs;
struct wm8994 *wm8994;
struct clk_bulk_data mclk[WM8994_NUM_MCLK];
struct clk *aif_mclk[2];
bool aif_clk_on[2];
int sysclk[2];
int sysclk_rate[2];
int mclk_rate[2];

View File

@ -1115,11 +1115,19 @@ static int wsa881x_probe(struct sdw_slave *pdev,
if (!wsa881x)
return -ENOMEM;
wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW);
/*
* The powerdown GPIO may be shared between several amplifiers. Request
* it asserted, so the shared GPIO proxy treats powerdown as the default
* state and keeps the line de-asserted as long as any amplifier needs
* it, then power up this amplifier.
*/
wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH);
if (IS_ERR(wsa881x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
"Shutdown Control GPIO not found\n");
gpiod_set_value_cansleep(wsa881x->sd_n, 0);
dev_set_drvdata(dev, wsa881x);
wsa881x->slave = pdev;
wsa881x->dev = dev;
@ -1154,7 +1162,7 @@ static int wsa881x_runtime_suspend(struct device *dev)
struct regmap *regmap = dev_get_regmap(dev, NULL);
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
gpiod_direction_output(wsa881x->sd_n, 1);
gpiod_set_value_cansleep(wsa881x->sd_n, 1);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
@ -1169,11 +1177,11 @@ static int wsa881x_runtime_resume(struct device *dev)
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
int ret;
gpiod_direction_output(wsa881x->sd_n, 0);
gpiod_set_value_cansleep(wsa881x->sd_n, 0);
ret = sdw_slave_wait_for_init(slave, WSA881X_PROBE_TIMEOUT);
if (ret) {
gpiod_direction_output(wsa881x->sd_n, 1);
gpiod_set_value_cansleep(wsa881x->sd_n, 1);
return ret;
}
@ -1182,7 +1190,7 @@ static int wsa881x_runtime_resume(struct device *dev)
if (ret) {
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
gpiod_direction_output(wsa881x->sd_n, 1);
gpiod_set_value_cansleep(wsa881x->sd_n, 1);
return ret;
}

View File

@ -456,10 +456,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
* outside of valid range.
* outside of the two-point trim range. Do not narrow this to ambient: a
* speaker that has been playing easily runs well above 45 degC.
*/
#define WSA883X_LOW_TEMP_THRESHOLD 5
#define WSA883X_HIGH_TEMP_THRESHOLD 45
#define WSA883X_LOW_TEMP_THRESHOLD WSA883X_T1_TEMP
#define WSA883X_HIGH_TEMP_THRESHOLD WSA883X_T2_TEMP
struct wsa883x_priv {
struct regmap *regmap;
@ -551,7 +552,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = {
.num = WSA883X_PORT_VISENSE + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 1,
.max_ch = 2,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
}
@ -572,7 +573,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
},
[WSA883X_PORT_VISENSE] = {
.num = WSA883X_PORT_VISENSE + 1,
.ch_mask = 0x1,
.ch_mask = 0x3,
},
};
@ -1430,7 +1431,7 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
unsigned int dmeas_msb = 0, dmeas_lsb = 0;
int d1, d2, dmeas;
unsigned int mask;
int ret, range;
int ret;
long val;
guard(mutex)(&wsa883x->sp_lock);
@ -1493,8 +1494,8 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
}
val = WSA883X_T1_TEMP + (((dmeas - d1) * (WSA883X_T2_TEMP - WSA883X_T1_TEMP)) / (d2 - d1));
range = WSA883X_HIGH_TEMP_THRESHOLD - WSA883X_LOW_TEMP_THRESHOLD;
if (in_range(val, WSA883X_LOW_TEMP_THRESHOLD, range)) {
if (val >= WSA883X_LOW_TEMP_THRESHOLD &&
val <= WSA883X_HIGH_TEMP_THRESHOLD) {
wsa883x->temperature = val;
*temp = val * 1000;
ret = 0;
@ -1668,7 +1669,9 @@ static int wsa883x_probe(struct sdw_slave *pdev,
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
goto err;
ret = devm_snd_soc_register_component(dev,
&wsa883x_component_drv,

View File

@ -720,10 +720,11 @@
/*
* Device will report senseless data in many cases, so discard any measurements
* outside of valid range.
* outside of the two-point trim range. Do not narrow this to ambient: a
* speaker that has been playing easily runs well above 45 degC.
*/
#define WSA884X_LOW_TEMP_THRESHOLD 5
#define WSA884X_HIGH_TEMP_THRESHOLD 45
#define WSA884X_LOW_TEMP_THRESHOLD WSA884X_T1_TEMP
#define WSA884X_HIGH_TEMP_THRESHOLD WSA884X_T2_TEMP
struct wsa884x_priv {
struct regmap *regmap;
@ -858,7 +859,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.num = WSA884X_PORT_VISENSE + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
.max_ch = 1,
.max_ch = 2,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
@ -891,7 +892,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
},
[WSA884X_PORT_VISENSE] = {
.num = WSA884X_PORT_VISENSE + 1,
.ch_mask = 0x1,
.ch_mask = 0x3,
},
[WSA884X_PORT_CPS] = {
.num = WSA884X_PORT_CPS + 1,
@ -1929,8 +1930,8 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
dev_dbg(wsa884x->dev, "Measured temp %ld (dmeas=%d, d1=%d, d2=%d)\n",
val, dmeas, d1, d2);
if ((val > WSA884X_LOW_TEMP_THRESHOLD) &&
(val < WSA884X_HIGH_TEMP_THRESHOLD)) {
if (val >= WSA884X_LOW_TEMP_THRESHOLD &&
val <= WSA884X_HIGH_TEMP_THRESHOLD) {
wsa884x->temperature = val;
*temp = val * 1000;
ret = 0;
@ -2124,7 +2125,9 @@ static int wsa884x_probe(struct sdw_slave *pdev,
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
return devm_snd_soc_register_component(dev,
&wsa884x_component_drv,

View File

@ -153,6 +153,9 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
}
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
/* Fractional audio PLLs may sit a few Hz off nominal */
#define FSL_RATE_CONSTR_TOL_HZ 100
/**
* fsl_asoc_constrain_rates - constrain rates according to clocks
*
@ -172,6 +175,7 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
{
int i, j, k = 0;
u64 clk_rate[3];
u32 rate, rem;
*target_constr = *original_constr;
if (pll8k_clk || pll11k_clk || ext_clk) {
@ -181,10 +185,25 @@ void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
clk_rate[0] = clk_get_rate(pll8k_clk);
clk_rate[1] = clk_get_rate(pll11k_clk);
clk_rate[2] = clk_get_rate(ext_clk);
rate = original_constr->list[i];
for (j = 0; j < 3; j++) {
if (clk_rate[j] != 0 &&
do_div(clk_rate[j], original_constr->list[i]) == 0) {
target_rates[k++] = original_constr->list[i];
if (clk_rate[j] == 0)
continue;
rem = do_div(clk_rate[j], rate);
/*
* The audio PLLs are fractional and cannot
* always reproduce their nominal rate: after
* any clk_set_rate() they may sit a few Hz
* off (observed on i.MX8M Plus: audio_pll1
* reads 393215997 after restoring 393216000),
* which must not disqualify the entire rate
* family. Accept near-integer ratios; the
* other family misses by tens of kHz, so a
* small tolerance cannot mix families.
*/
if (rem <= FSL_RATE_CONSTR_TOL_HZ ||
rem >= rate - FSL_RATE_CONSTR_TOL_HZ) {
target_rates[k++] = rate;
target_constr->count++;
break;
}

View File

@ -178,6 +178,16 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
return 0;
}
static int imx_rpmsg_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct rpmsg_info *info = dev_get_drvdata(component->dev);
flush_workqueue(info->rpmsg_wq);
return 0;
}
static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@ -285,9 +295,6 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
struct rpmsg_info *info = dev_get_drvdata(component->dev);
struct rpmsg_msg *msg;
/* Flush work in workqueue to make TX_CLOSE is the last message */
flush_workqueue(info->rpmsg_wq);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
msg = &info->msg[TX_CLOSE];
msg->s_msg.header.cmd = TX_CLOSE;
@ -624,6 +631,7 @@ static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
.open = imx_rpmsg_pcm_open,
.close = imx_rpmsg_pcm_close,
.hw_params = imx_rpmsg_pcm_hw_params,
.hw_free = imx_rpmsg_pcm_hw_free,
.trigger = imx_rpmsg_pcm_trigger,
.pointer = imx_rpmsg_pcm_pointer,
.ack = imx_rpmsg_pcm_ack,

View File

@ -70,6 +70,23 @@ static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
},
};
static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* DMIC Endpoint */
{
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device cs35l56_2_lr_adr[] = {
{
.adr = 0x00023001FA355601ull,
@ -301,6 +318,35 @@ static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = {
}
};
/*
* MSI Raider 16 Max HX B2WI (MS-2651): RT713 (jack/dmic, "VB" SDCA variant)
* on link 0, two RT1320 speaker amps sharing link 2 (unique_id 0 = left,
* unique_id 1 = right).
*/
static const struct snd_soc_acpi_adr_device rt713_vb_0_adr[] = {
{
.adr = 0x000030025D071301ull,
.num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
.endpoints = jack_dmic_endpoints,
.name_prefix = "rt713"
}
};
static const struct snd_soc_acpi_adr_device rt1320_2_lr_adr[] = {
{
.adr = 0x000230025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1320-1"
},
{
.adr = 0x000231025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1320-2"
}
};
static const struct snd_soc_acpi_link_adr arl_n_mrd_es9356_link1[] = {
{
.mask = BIT(1),
@ -454,6 +500,20 @@ static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = {
{}
};
static const struct snd_soc_acpi_link_adr arl_rt713_vb_l0_rt1320_l2[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt713_vb_0_adr),
.adr_d = rt713_vb_0_adr,
},
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt1320_2_lr_adr),
.adr_d = rt1320_2_lr_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = {
{
.mask = BIT(0),
@ -482,6 +542,15 @@ static const struct snd_soc_acpi_link_adr arl_rt712_l0_rt1320_l3[] = {
{}
};
static const struct snd_soc_acpi_link_adr arl_rt712_l0[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt712_0_agg_adr),
.adr_d = rt712_0_agg_adr,
},
{}
};
static const struct snd_soc_acpi_codecs arl_essx_83x6 = {
.num_codecs = 3,
.codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
@ -617,6 +686,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(2),
.links = arl_rt713_vb_l0_rt1320_l2,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-arl-dummy.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(2),
.links = arl_rt722_l0_rt1320_l2,
@ -646,6 +723,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = arl_rt712_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-dummy.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = arl_cs42l43_l0,

View File

@ -393,6 +393,7 @@ struct q6afe {
struct q6core_svc_api_info ainfo;
struct mutex lock;
struct aprv2_ibasic_rsp_result_t result;
uint32_t vote_result;
wait_queue_head_t wait;
struct list_head port_list;
spinlock_t port_list_lock;
@ -1010,13 +1011,14 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da
const struct aprv2_ibasic_rsp_result_t *res;
const struct apr_hdr *hdr = &data->hdr;
struct q6afe_port *port;
uint32_t *vote_res;
if (!data->payload_size)
return 0;
res = data->payload;
switch (hdr->opcode) {
case APR_BASIC_RSP_RESULT: {
res = data->payload;
if (res->status) {
dev_err(afe->dev, "cmd = 0x%x returned error = 0x%x\n",
res->opcode, res->status);
@ -1043,8 +1045,10 @@ static int q6afe_callback(struct apr_device *adev, const struct apr_resp_pkt *da
}
break;
case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST:
vote_res = data->payload;
afe->result.opcode = hdr->opcode;
afe->result.status = res->status;
afe->result.status = 0;
afe->vote_result = *vote_res;
wake_up(&afe->wait);
break;
default:
@ -1955,6 +1959,8 @@ int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST);
if (ret)
dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id);
else
*client_handle = afe->vote_result;
return ret;
}

View File

@ -322,15 +322,15 @@ int q6apm_alloc_fragments(struct q6apm_graph *graph, unsigned int dir, phys_addr
}
EXPORT_SYMBOL_GPL(q6apm_alloc_fragments);
static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id,
static int __q6apm_unmap_memory_fixed_region(struct q6apm *apm, unsigned int graph_id,
bool is_pos_buf)
{
struct apm_cmd_shared_mem_unmap_regions *cmd;
struct q6apm *apm = dev_get_drvdata(dev->parent);
struct audioreach_graph_info *info;
uint32_t mem_map_handle;
uint32_t pos_mask = is_pos_buf ? APM_MMAP_TOKEN_MAP_TYPE_POS_BUF : 0;
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(sizeof(*cmd),
APM_CMD_SHARED_MEM_UNMAP_REGIONS, graph_id);
APM_CMD_SHARED_MEM_UNMAP_REGIONS, (graph_id | pos_mask));
if (IS_ERR(pkt))
return PTR_ERR(pkt);
@ -357,13 +357,15 @@ static int __q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int gr
int q6apm_unmap_memory_fixed_region(struct device *dev, unsigned int graph_id)
{
return __q6apm_unmap_memory_fixed_region(dev, graph_id, false);
return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent),
graph_id, false);
}
EXPORT_SYMBOL_GPL(q6apm_unmap_memory_fixed_region);
int q6apm_unmap_pos_buffer(struct device *dev, unsigned int graph_id)
{
return __q6apm_unmap_memory_fixed_region(dev, graph_id, true);
return __q6apm_unmap_memory_fixed_region(dev_get_drvdata(dev->parent),
graph_id, true);
}
EXPORT_SYMBOL_GPL(q6apm_unmap_pos_buffer);
@ -850,6 +852,21 @@ static int q6apm_audio_probe(struct snd_soc_component *component)
static void q6apm_audio_remove(struct snd_soc_component *component)
{
struct q6apm *apm = dev_get_drvdata(component->dev);
struct audioreach_graph_info *info;
int id;
/*
* The card core removes components before freeing the PCMs, so
* q6apm-dai's pcm_free() runs after the topology (and graph_info)
* is gone and can no longer unmap. Release the DSP mappings here,
* otherwise the next pcm_new() fails to map the same buffer again.
*/
idr_for_each_entry(&apm->graph_info_idr, info, id) {
__q6apm_unmap_memory_fixed_region(apm, id, false);
__q6apm_unmap_memory_fixed_region(apm, id, true);
}
/* remove topology */
snd_soc_tplg_component_remove(component);
}
@ -960,17 +977,23 @@ static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op)
wake_up(&apm->wait);
break;
case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
apm->result.opcode = hdr->opcode;
apm->result.status = 0;
rsp = data->payload;
info = idr_find(&apm->graph_info_idr, hdr->token);
if (info)
info->mem_map_handle = 0;
else
apm->result.opcode = result->opcode;
apm->result.status = result->status;
if (result->status)
dev_err(dev, "Error (%d) Processing 0x%08x cmd\n", result->status,
result->opcode);
graph_id = hdr->token & APM_MMAP_TOKEN_GID_MASK;
is_pos_buf = hdr->token & APM_MMAP_TOKEN_MAP_TYPE_POS_BUF;
info = idr_find(&apm->graph_info_idr, graph_id);
if (info && !result->status) {
if (is_pos_buf)
info->pos_buf_mem_map_handle = 0;
else
info->mem_map_handle = 0;
}
wake_up(&apm->wait);
break;
default:

View File

@ -312,22 +312,37 @@ audioreach_get_module_priv_data(const struct snd_soc_tplg_private *private)
for (sz = 0; sz < le32_to_cpu(private->size); ) {
const struct snd_soc_tplg_vendor_array *mod_array;
u32 array_size;
mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);
array_size = le32_to_cpu(mod_array->size);
if (le32_to_cpu(mod_array->type) == SND_SOC_AR_TPLG_MODULE_CFG_TYPE) {
struct audioreach_module_priv_data *pdata;
u32 nwords;
pdata = kzalloc_flex(*pdata, data,
le32_to_cpu(mod_array->size));
/* size is the byte count of the __le32 data[] */
nwords = array_size / sizeof(__le32);
/* the blob must lie within the private data */
if (sizeof(*pdata) + (u64)array_size >
le32_to_cpu(private->size) - sz)
return ERR_PTR(-EINVAL);
pdata = kzalloc_flex(*pdata, data, nwords);
if (!pdata)
return ERR_PTR(-ENOMEM);
memcpy(pdata, ((u8 *)private->data + sz), struct_size(pdata, data,
le32_to_cpu(mod_array->size)));
memcpy(pdata, ((u8 *)private->data + sz),
struct_size(pdata, data, nwords));
return pdata;
}
sz = sz + le32_to_cpu(mod_array->size);
/* a zero size would never advance the walk */
if (!array_size)
return ERR_PTR(-EINVAL);
sz = sz + array_size;
}
return NULL;
@ -611,6 +626,12 @@ static int audioreach_widget_load_module_common(struct snd_soc_component *compon
return mod ? PTR_ERR(mod) : -ENODEV;
mod->data = audioreach_get_module_priv_data(&tplg_w->priv);
if (IS_ERR(mod->data)) {
int rc = PTR_ERR(mod->data);
mod->data = NULL;
return rc;
}
dobj = &w->dobj;
dobj->private = mod;

View File

@ -39,6 +39,16 @@ static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
case USB_RX:
return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack,
&data->usb_offload_jack_setup);
case RX_CODEC_DMA_RX_0:
/*
* The RX macro digital gain goes up to +40 dB. Userspace that
* maps full volume to the top of the range (PipeWire, PulseAudio)
* would clip there, so stop the interpolator gains at 0 dB.
*/
snd_soc_limit_volume(rtd->card, "RX_RX0 Digital Volume", 84);
snd_soc_limit_volume(rtd->card, "RX_RX1 Digital Volume", 84);
snd_soc_limit_volume(rtd->card, "RX_RX2 Digital Volume", 84);
fallthrough;
default:
return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
}

View File

@ -25,7 +25,8 @@
*/
#define MCLK1_RATE 24000000U
#define MCLK2_RATE 32768U
#define DEFAULT_FLL1_RATE 11289600U
/* Matches the codec's default AIF1 configuration: 48kHz, 256fs */
#define DEFAULT_FLL1_RATE 12288000U
struct midas_priv {
struct gpio_desc *gpio_fm_sel;
@ -34,6 +35,7 @@ struct midas_priv {
struct gpio_desc *gpio_headset_key;
struct iio_channel *adc_headset_detect;
unsigned int fll1_rate;
unsigned int aif1clk_rate;
struct snd_soc_jack headset_jack;
};
@ -171,11 +173,12 @@ static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate)
if (!rate)
rate = priv->fll1_rate;
/*
* If no new rate is requested, set FLL1 to a sane default for jack
* detection.
* If no new rate is requested, set FLL1 to the rate the codec's AIF1
* is currently configured for, as its digital paths can be powered
* up without an active stream.
*/
if (!rate)
rate = DEFAULT_FLL1_RATE;
rate = priv->aif1clk_rate ?: DEFAULT_FLL1_RATE;
if (rate != priv->fll1_rate && priv->fll1_rate) {
/* while reconfiguring, switch to MCLK2 for SYSCLK */
@ -241,6 +244,7 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct midas_priv *priv = snd_soc_card_get_drvdata(rtd->card);
unsigned int pll_out;
/* AIF1CLK should be at least 3MHz for "optimal performance" */
@ -249,6 +253,8 @@ static int midas_aif1_hw_params(struct snd_pcm_substream *substream,
else
pll_out = params_rate(params) * 256;
priv->aif1clk_rate = pll_out;
return midas_start_fll1(rtd, pll_out);
}

View File

@ -27,8 +27,11 @@
static int class_read_prop(struct sdw_slave *sdw)
{
struct sdw_slave_prop *prop = &sdw->prop;
int ret;
sdw_slave_read_prop(sdw);
ret = sdw_slave_read_prop(sdw);
if (ret)
return ret;
prop->use_domain_irq = true;
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
@ -194,8 +197,10 @@ int sdca_class_probe(struct sdw_slave *sdw,
pm_runtime_get_noresume(dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
if (ret) {
pm_runtime_put_sync(dev);
return ret;
}
queue_work(system_long_wq, &drv->boot_work);
@ -260,6 +265,7 @@ int sdca_class_system_suspend(struct sdca_class_drv *drv)
ret = pm_runtime_force_suspend(drv->dev);
if (ret) {
dev_err(drv->dev, "failed to force suspend: %d\n", ret);
enable_irq(drv->sdw->irq);
return ret;
}

View File

@ -7,6 +7,7 @@
config SND_SOC_SOF_AMD_TOPLEVEL
tristate "SOF support for AMD audio DSPs"
depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
depends on ACPI
depends on X86 || COMPILE_TEST
help
This adds support for Sound Open Firmware for AMD platforms.

View File

@ -15,8 +15,6 @@
#include "hda.h"
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
#include "../../codecs/hdac_hda.h"
#define CODEC_PROBE_RETRIES 3
#define IDISP_VID_INTEL 0x80860000
@ -148,7 +146,6 @@ static struct hda_codec *hda_codec_device_init(struct hdac_bus *bus, int addr, i
/* probe individual codec */
static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
{
struct hdac_hda_priv *hda_priv;
struct hda_bus *hbus = sof_to_hbus(sdev);
struct hda_codec *codec;
u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) |
@ -168,27 +165,15 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address)
dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n",
address, resp);
hda_priv = devm_kzalloc(sdev->dev, sizeof(*hda_priv), GFP_KERNEL);
if (!hda_priv)
return -ENOMEM;
codec = hda_codec_device_init(&hbus->core, address, HDA_DEV_LEGACY);
ret = PTR_ERR_OR_ZERO(codec);
if (ret < 0)
return ret;
hda_priv->codec = codec;
hda_priv->dev_index = address;
dev_set_drvdata(&codec->core.dev, hda_priv);
if ((resp & 0xFFFF0000) == IDISP_VID_INTEL) {
if (!hbus->core.audio_component) {
dev_dbg(sdev->dev,
"iDisp hw present but no driver\n");
ret = -ENOENT;
goto out;
}
hda_priv->need_display_power = true;
if ((resp & 0xFFFF0000) == IDISP_VID_INTEL && !hbus->core.audio_component) {
dev_dbg(sdev->dev, "iDisp hw present but no driver\n");
ret = -ENOENT;
goto out;
}
if (is_generic_config(hbus))

View File

@ -349,6 +349,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev)
bus->chip_init = false;
}
EXPORT_SYMBOL_NS(hda_dsp_ctrl_stop_chip, "SND_SOC_SOF_INTEL_HDA_COMMON");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SOF helpers for HDaudio platforms");

View File

@ -939,6 +939,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
hda_dsp_ctrl_ppcap_enable(sdev, false);
}
free_ipc_irq:
hda_dsp_ctrl_stop_chip(sdev);
free_irq(sdev->ipc_irq, sdev);
free_irq_vector:
if (sdev->msi_enabled)
@ -1006,6 +1007,9 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
snd_dma_free_pages(&hda->iccmax_dmab);
skip_disable_dsp:
/* stop CORB/RIRB and position buffer DMA before the buffers are freed */
hda_dsp_ctrl_stop_chip(sdev);
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);

View File

@ -6,6 +6,7 @@
//
// Author: Maruthi Srinivas Bayyavarapu <maruthis@xilinx.com>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
@ -55,8 +56,8 @@
#define CFG_S2MM_XFER_SHIFT 29
#define CFG_S2MM_PKG_MASK BIT(28)
#define AUD_CTRL_DATA_WIDTH_SHIFT 16
#define AUD_CTRL_ACTIVE_CH_SHIFT 19
#define AUD_CTRL_DATA_WIDTH_MASK GENMASK(18, 16)
#define AUD_CTRL_ACTIVE_CH_MASK GENMASK(22, 19)
#define PERIOD_CFG_PERIODS_SHIFT 16
#define PERIODS_MIN 2
@ -506,28 +507,29 @@ static int xlnx_formatter_pcm_hw_params(struct snd_soc_component *component,
writel(high, stream_data->mmio + XLNX_AUD_BUFF_ADDR_MSB);
val = readl(stream_data->mmio + XLNX_AUD_CTRL);
val &= ~(AUD_CTRL_DATA_WIDTH_MASK | AUD_CTRL_ACTIVE_CH_MASK);
bits_per_sample = params_width(params);
switch (bits_per_sample) {
case 8:
val |= (BIT_DEPTH_8 << AUD_CTRL_DATA_WIDTH_SHIFT);
val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_8);
break;
case 16:
val |= (BIT_DEPTH_16 << AUD_CTRL_DATA_WIDTH_SHIFT);
val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_16);
break;
case 20:
val |= (BIT_DEPTH_20 << AUD_CTRL_DATA_WIDTH_SHIFT);
val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_20);
break;
case 24:
val |= (BIT_DEPTH_24 << AUD_CTRL_DATA_WIDTH_SHIFT);
val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_24);
break;
case 32:
val |= (BIT_DEPTH_32 << AUD_CTRL_DATA_WIDTH_SHIFT);
val |= FIELD_PREP(AUD_CTRL_DATA_WIDTH_MASK, BIT_DEPTH_32);
break;
default:
return -EINVAL;
}
val |= active_ch << AUD_CTRL_ACTIVE_CH_SHIFT;
val |= FIELD_PREP(AUD_CTRL_ACTIVE_CH_MASK, active_ch);
writel(val, stream_data->mmio + XLNX_AUD_CTRL);
val = (params_periods(params) << PERIOD_CFG_PERIODS_SHIFT)

View File

@ -1642,9 +1642,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
err = -ENODEV;
if (err < 0) {
if (!atomic_read(&ep->chip->shutdown))
usb_audio_err(ep->chip,
"cannot submit urb %d, error %d: %s\n",
i, err, usb_error_string(err));
usb_audio_err_ratelimited(ep->chip,
"cannot submit urb %d, error %d: %s\n",
i, err, usb_error_string(err));
goto __error;
}
set_bit(i, &ep->active_mask);

View File

@ -285,13 +285,29 @@ static void playback_work(struct work_struct *work)
/* fill packet with data or silence */
urb->iso_frame_desc[0].length =
frames * ua->playback.frame_bytes;
if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) {
/*
* The packet size comes from the capture stream,
* i.e. from the device, while the ring buffer can
* be as small as two tiny periods. The single wrap
* in copy_playback_data() is only valid for
* frames <= buffer_size, so copy at most one
* buffer and send silence for the rest.
*/
unsigned int copy_frames = min(frames,
ua->playback.substream->runtime->buffer_size);
do_period_elapsed |= copy_playback_data(&ua->playback,
urb,
frames);
else
copy_frames);
memset(urb->transfer_buffer +
copy_frames * ua->playback.frame_bytes, 0,
(frames - copy_frames) *
ua->playback.frame_bytes);
} else {
memset(urb->transfer_buffer, 0,
urb->iso_frame_desc[0].length);
}
/* and off you go ... */
err = usb_submit_urb(urb, GFP_ATOMIC);
@ -320,6 +336,13 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb,
runtime = stream->substream->runtime;
frame_bytes = stream->frame_bytes;
/*
* A packet can carry up to max_packet_bytes, the device's
* wMaxPacketSize, which may exceed the whole ring buffer; keep what
* fits so the single wrap below stays valid.
*/
if (unlikely(frames > runtime->buffer_size))
frames = runtime->buffer_size;
dest = runtime->dma_area + stream->buffer_pos * frame_bytes;
if (stream->buffer_pos + frames <= runtime->buffer_size) {
memcpy(dest, urb->transfer_buffer, frames * frame_bytes);

View File

@ -1343,6 +1343,18 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
bytes -= adj;
subs->stream_offset_adj -= adj;
}
/*
* A packet can be as large as the endpoint's maxpacksize, which
* the device chooses, while the ring buffer can be as small as
* two 64-byte periods. The hwptr_done wrap below handles one
* wrap only, so never take more than one buffer from a packet.
*/
if (unlikely(bytes > subs->buffer_bytes)) {
dev_warn_ratelimited(&subs->dev->dev,
"%u-byte packet exceeds %u-byte buffer, truncating\n",
bytes, subs->buffer_bytes);
bytes = subs->buffer_bytes;
}
frames = bytes / stride;
if (!subs->txfr_quirk)
bytes = frames * stride;
@ -1553,8 +1565,14 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
for (i = 0; i < ctx->packets; i++) {
counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
/*
* Bound by the ring buffer as well as by the URB:
* copy_to_urb() wraps hwptr_done once, so one URB must
* never carry more than the whole buffer.
*/
if (counts < 0 ||
(frames + counts) * stride > ctx->buffer_size)
(frames + counts) * stride > ctx->buffer_size ||
(frames + counts) * stride > subs->buffer_bytes)
break;
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;

View File

@ -2359,6 +2359,69 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
{
/*
* Novation UltraNova: vendor-specific class descriptors, fixed
* audio and MIDI layouts. Only the audio and MIDI interfaces
* (0 and 1) are grabbed; the vendor control interfaces (2/3)
* are left unbound for userspace access.
*/
USB_DEVICE_INTERFACE_NUMBER(0x1235, 0x0011, 0),
QUIRK_DRIVER_INFO {
.vendor_name = "Novation",
.product_name = "UltraNova",
QUIRK_DATA_COMPOSITE {
{
QUIRK_DATA_AUDIOFORMAT(0) {
/* playback */
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels = 4,
.iface = 0,
.altsetting = 1,
.altset_idx = 1,
.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
.endpoint = 0x01,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_SYNC,
.maxpacksize = 392,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.rate_min = 44100,
.rate_max = 48000,
.nr_rates = 2,
.rate_table = (unsigned int[]) {
44100, 48000
}
}
},
{
QUIRK_DATA_AUDIOFORMAT(0) {
/* capture */
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
.channels = 2,
.iface = 0,
.altsetting = 1,
.altset_idx = 1,
.attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
.endpoint = 0x82,
.ep_idx = 1,
.ep_attr = USB_ENDPOINT_XFER_ISOC,
.maxpacksize = 294,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000,
.rate_min = 44100,
.rate_max = 48000,
.nr_rates = 2,
.rate_table = (unsigned int[]) {
44100, 48000
}
}
},
{ QUIRK_DATA_RAW_BYTES(1) },
QUIRK_COMPOSITE_END
}
}
},
{
USB_DEVICE(0x1235, 0x001e),
QUIRK_DRIVER_INFO {
@ -3005,6 +3068,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rates = SNDRV_PCM_RATE_96000,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
.datainterval = 1,
.rate_min = 96000,
.rate_max = 96000,
.nr_rates = 1,
@ -3027,6 +3091,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.rates = SNDRV_PCM_RATE_96000,
.ep_attr = USB_ENDPOINT_XFER_ISOC |
USB_ENDPOINT_SYNC_ASYNC,
.datainterval = 1,
.rate_min = 96000,
.rate_max = 96000,
.nr_rates = 1,

View File

@ -1794,7 +1794,7 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
/* the quirk seems breaking on some Behringer devices */
switch (id) {
case USB_ID(0x1397, 0x0508): /* UMC204HD */
case USB_ID(0x1397, 0x0509): /* UMC204HD */
case USB_ID(0x1397, 0x0509): /* UMC404HD */
return 0;
}
return snd_usb_cm1a_boot_quirk(dev);
@ -2496,6 +2496,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x0124, 0x0c36, /* RYDZ Technology USB Headphone */
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x03f0, 0x0c56, /* HP Elite x3 Lap Dock */
QUIRK_FLAG_FIXED_RATE),
DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */
@ -2659,6 +2661,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */
QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0db0, 0xa073, /* MSI MAG X570S Tomahawk/Torpedo Max (ALC4080) */
QUIRK_FLAG_SKIP_IFACE_SETUP),
DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */