mirror of
https://github.com/torvalds/linux.git
synced 2026-05-21 13:27:57 +02:00
sound fixes for 6.8-rc3
A collection of fixes, mostly device-specific ones. - Minor PCM core fix for name strings - ASoC Qualcomm fixes, including DAI support extensions - ASoC AMD platform updates - ASoC Allwinner platform updates - Various ASoC codec fixes for WSA, WCD, ES8326 drivers - Various HD-audio and USB-audio fixes and quirks - A series of fixes for Cirrus CS35L56 codecs -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmW75mMOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE8QCA/7Boy4WRzbrsYcz+ZWd323rWgqFj8/iN2E9hZn LDYAF8edHhz/P4KQgj9DYNGU/jUEqyXcSuZxSUCtHYFw9qvLqNI9n5hniavroA8Q nX23LwYKwKnRg4A+Gq1T5oBQyO0+zbaM1P+0BaF21M4YcoV8EjJMOQSfZtsJlPSy aQp2ydkvUeHLWYpSmJoz5cfEAhirMwwekz+L0pDbMnEdxqpyYsvo+mgckTVztU2R 12oDkngT0rpQ9kiiT7HlPQEiyyEaqK8hnJSG5NB921f4Dy6mSa9lvHXV+dC7kB9m JfhR4cRr0UuHADrfp7Y6wGJY8r+haEQf5miyPBNGycDLrUh2FSNpolMqZLSN6Oso sznDlBmiPibEvEUhAz4SR5R7yrbmd0J1RAJ1+XmrNvGBdB6Iwtxi6e4MJIia6Y2d 8s27meaio0sBivLmux2eL1PuJ4YhcH59KCHtnkYR2IYdSeiOpQjEJo5lel52ghzw wGpTDCVGu7kLb0LPYtRdlelL8ZETnyeeovWlPUqzarFyR7Oh8axbPCeLMPBGAwbo nKuvhNKvmIFOTkLFs4NXyIwDc1Zrc7oaQog28LRsHR3R+2NfrhmLmpxrkBkyasc5 rp1DXN4+XTQPQAOFm06XWusJHTYTKo6+T5lu0w9NAbleNM1PpGuvXpzG16ePDK4K VtS3z8Y= =V9RO -----END PGP SIGNATURE----- Merge tag 'sound-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound fixes from Takashi Iwai: "A collection of fixes, mostly device-specific ones: - Minor PCM core fix for name strings - ASoC Qualcomm fixes, including DAI support extensions - ASoC AMD platform updates - ASoC Allwinner platform updates - Various ASoC codec fixes for WSA, WCD, ES8326 drivers - Various HD-audio and USB-audio fixes and quirks - A series of fixes for Cirrus CS35L56 codecs" * tag 'sound-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (63 commits) ALSA: usb-audio: Ignore clock selector errors for single connection ALSA: hda/realtek: Enable headset mic on Vaio VJFE-ADL ALSA: hda: cs35l56: Remove unused test stub function ALSA: hda: cs35l56: Firmware file must match the version of preloaded firmware ALSA: hda: cs35l56: Fix filename string field layout ALSA: hda: cs35l56: Fix order of searching for firmware files ASoC: cs35l56: Allow more time for firmware to boot ASoC: cs35l56: Load tunings for the correct speaker models ASoC: cs35l56: Firmware file must match the version of preloaded firmware ASoC: cs35l56: Fix misuse of wm_adsp 'part' string for silicon revision ASoC: cs35l56: Fix for initializing ASP1 mixer registers ALSA: hda: cs35l56: Initialize all ASP1 registers ASoC: cs35l56: Fix default SDW TX mixer registers ASoC: cs35l56: Fix to ensure ASP1 registers match cache ASoC: cs35l56: Remove buggy checks from cs35l56_is_fw_reload_needed() ASoC: cs35l56: Don't add the same register patch multiple times ASoC: cs35l56: cs35l56_component_remove() must clean up wm_adsp ASoC: cs35l56: cs35l56_component_remove() must clear cs35l56->component ASoC: wm_adsp: Don't overwrite fwf_name with the default ASoC: wm_adsp: Fix firmware file search order ...
This commit is contained in:
commit
01370ceb2a
|
|
@ -22,6 +22,7 @@ properties:
|
|||
- const: allwinner,sun6i-a31-spdif
|
||||
- const: allwinner,sun8i-h3-spdif
|
||||
- const: allwinner,sun50i-h6-spdif
|
||||
- const: allwinner,sun50i-h616-spdif
|
||||
- items:
|
||||
- const: allwinner,sun8i-a83t-spdif
|
||||
- const: allwinner,sun8i-h3-spdif
|
||||
|
|
@ -62,6 +63,8 @@ allOf:
|
|||
enum:
|
||||
- allwinner,sun6i-a31-spdif
|
||||
- allwinner,sun8i-h3-spdif
|
||||
- allwinner,sun50i-h6-spdif
|
||||
- allwinner,sun50i-h616-spdif
|
||||
|
||||
then:
|
||||
required:
|
||||
|
|
@ -73,7 +76,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- allwinner,sun8i-h3-spdif
|
||||
- allwinner,sun50i-h6-spdif
|
||||
- allwinner,sun50i-h616-spdif
|
||||
|
||||
then:
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@
|
|||
#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_0 0x25E2040
|
||||
#define CS35L56_DSP1_AHBM_WINDOW_DEBUG_1 0x25E2044
|
||||
#define CS35L56_DSP1_XMEM_UNPACKED24_0 0x2800000
|
||||
#define CS35L56_DSP1_FW_VER 0x2800010
|
||||
#define CS35L56_DSP1_HALO_STATE_A1 0x2801E58
|
||||
#define CS35L56_DSP1_HALO_STATE 0x28021E0
|
||||
#define CS35L56_DSP1_PM_CUR_STATE_A1 0x2804000
|
||||
|
|
@ -241,7 +242,7 @@
|
|||
|
||||
#define CS35L56_CONTROL_PORT_READY_US 2200
|
||||
#define CS35L56_HALO_STATE_POLL_US 1000
|
||||
#define CS35L56_HALO_STATE_TIMEOUT_US 50000
|
||||
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
|
||||
#define CS35L56_RESET_PULSE_MIN_US 1100
|
||||
#define CS35L56_WAKE_HOLD_TIME_US 1000
|
||||
|
||||
|
|
@ -272,6 +273,7 @@ extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
|
|||
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
|
||||
|
||||
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command);
|
||||
int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base);
|
||||
|
|
@ -284,7 +286,10 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
|
|||
int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
|
||||
void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
|
||||
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
|
||||
bool *fw_missing, unsigned int *fw_version);
|
||||
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_get_bclk_freq_id(unsigned int freq);
|
||||
void cs35l56_fill_supply_names(struct regulator_bulk_data *data);
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,10 @@ static const char * const snd_pcm_format_names[] = {
|
|||
FORMAT(DSD_U32_LE),
|
||||
FORMAT(DSD_U16_BE),
|
||||
FORMAT(DSD_U32_BE),
|
||||
FORMAT(S20_LE),
|
||||
FORMAT(S20_BE),
|
||||
FORMAT(U20_LE),
|
||||
FORMAT(U20_BE),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ static const struct cs35l41_config cs35l41_config_table[] = {
|
|||
{ "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
|
||||
{ "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
|
||||
{ "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
|
||||
{ "10431683", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
|
||||
{ "104316A3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
|
||||
{ "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
|
||||
{ "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
|
||||
{ "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
|
||||
|
|
@ -410,6 +412,8 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
|
|||
{ "CSC3551", "10431533", generic_dsd_config },
|
||||
{ "CSC3551", "10431573", generic_dsd_config },
|
||||
{ "CSC3551", "10431663", generic_dsd_config },
|
||||
{ "CSC3551", "10431683", generic_dsd_config },
|
||||
{ "CSC3551", "104316A3", generic_dsd_config },
|
||||
{ "CSC3551", "104316D3", generic_dsd_config },
|
||||
{ "CSC3551", "104316F3", generic_dsd_config },
|
||||
{ "CSC3551", "104317F3", generic_dsd_config },
|
||||
|
|
|
|||
|
|
@ -30,14 +30,23 @@
|
|||
* ASP1_RX_WL = 24 bits per sample
|
||||
* ASP1_TX_WL = 24 bits per sample
|
||||
* ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled
|
||||
*
|
||||
* Override any Windows-specific mixer settings applied by the firmware.
|
||||
*/
|
||||
static const struct reg_sequence cs35l56_hda_dai_config[] = {
|
||||
{ CS35L56_ASP1_CONTROL1, 0x00000021 },
|
||||
{ CS35L56_ASP1_CONTROL2, 0x20200200 },
|
||||
{ CS35L56_ASP1_CONTROL3, 0x00000003 },
|
||||
{ CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
|
||||
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
|
||||
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
|
||||
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
|
||||
{ CS35L56_ASP1_ENABLES1, 0x00000000 },
|
||||
{ CS35L56_ASP1TX1_INPUT, 0x00000018 },
|
||||
{ CS35L56_ASP1TX2_INPUT, 0x00000019 },
|
||||
{ CS35L56_ASP1TX3_INPUT, 0x00000020 },
|
||||
{ CS35L56_ASP1TX4_INPUT, 0x00000028 },
|
||||
|
||||
};
|
||||
|
||||
static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
|
||||
|
|
@ -133,6 +142,10 @@ static int cs35l56_hda_runtime_resume(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
@ -384,7 +397,7 @@ static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
|
|||
|
||||
static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
|
||||
const struct firmware **firmware, char **filename,
|
||||
const char *dir, const char *system_name,
|
||||
const char *base_name, const char *system_name,
|
||||
const char *amp_name,
|
||||
const char *filetype)
|
||||
{
|
||||
|
|
@ -392,17 +405,13 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
|
|||
int ret = 0;
|
||||
|
||||
if (system_name && amp_name)
|
||||
*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s-%s.%s", dir,
|
||||
cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
|
||||
*filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name,
|
||||
system_name, amp_name, filetype);
|
||||
else if (system_name)
|
||||
*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s.%s", dir,
|
||||
cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
|
||||
*filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name,
|
||||
system_name, filetype);
|
||||
else
|
||||
*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc.%s", dir,
|
||||
cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
|
||||
filetype);
|
||||
*filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype);
|
||||
|
||||
if (!*filename)
|
||||
return -ENOMEM;
|
||||
|
|
@ -435,8 +444,8 @@ static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char cirrus_dir[] = "cirrus/";
|
||||
static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
|
||||
unsigned int preloaded_fw_ver,
|
||||
const struct firmware **wmfw_firmware,
|
||||
char **wmfw_filename,
|
||||
const struct firmware **coeff_firmware,
|
||||
|
|
@ -444,55 +453,73 @@ static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
|
|||
{
|
||||
const char *system_name = cs35l56->system_name;
|
||||
const char *amp_name = cs35l56->amp_name;
|
||||
char base_name[37];
|
||||
int ret;
|
||||
|
||||
if (preloaded_fw_ver) {
|
||||
snprintf(base_name, sizeof(base_name),
|
||||
"cirrus/cs35l56-%02x%s-%06x-dsp1-misc",
|
||||
cs35l56->base.rev,
|
||||
cs35l56->base.secured ? "-s" : "",
|
||||
preloaded_fw_ver & 0xffffff);
|
||||
} else {
|
||||
snprintf(base_name, sizeof(base_name),
|
||||
"cirrus/cs35l56-%02x%s-dsp1-misc",
|
||||
cs35l56->base.rev,
|
||||
cs35l56->base.secured ? "-s" : "");
|
||||
}
|
||||
|
||||
if (system_name && amp_name) {
|
||||
if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
|
||||
cirrus_dir, system_name, amp_name, "wmfw")) {
|
||||
base_name, system_name, amp_name, "wmfw")) {
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name, amp_name, "bin");
|
||||
base_name, system_name, amp_name, "bin");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (system_name) {
|
||||
if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
|
||||
cirrus_dir, system_name, NULL, "wmfw")) {
|
||||
base_name, system_name, NULL, "wmfw")) {
|
||||
if (amp_name)
|
||||
cs35l56_hda_request_firmware_file(cs35l56,
|
||||
coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
base_name, system_name,
|
||||
amp_name, "bin");
|
||||
if (!*coeff_firmware)
|
||||
cs35l56_hda_request_firmware_file(cs35l56,
|
||||
coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
base_name, system_name,
|
||||
NULL, "bin");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for system-specific bin files without wmfw before
|
||||
* falling back to generic firmware
|
||||
*/
|
||||
if (amp_name)
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
base_name, system_name, amp_name, "bin");
|
||||
if (!*coeff_firmware)
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
base_name, system_name, NULL, "bin");
|
||||
|
||||
if (*coeff_firmware)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
|
||||
cirrus_dir, NULL, NULL, "wmfw");
|
||||
base_name, NULL, NULL, "wmfw");
|
||||
if (!ret) {
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, NULL, NULL, "bin");
|
||||
base_name, NULL, NULL, "bin");
|
||||
return;
|
||||
}
|
||||
|
||||
/* When a firmware file is not found must still search for the coeff files */
|
||||
if (system_name) {
|
||||
if (amp_name)
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name, amp_name, "bin");
|
||||
if (!*coeff_firmware)
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name, NULL, "bin");
|
||||
}
|
||||
|
||||
if (!*coeff_firmware)
|
||||
cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, NULL, NULL, "bin");
|
||||
base_name, NULL, NULL, "bin");
|
||||
}
|
||||
|
||||
static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
|
||||
|
|
@ -526,7 +553,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
|
|||
const struct firmware *wmfw_firmware = NULL;
|
||||
char *coeff_filename = NULL;
|
||||
char *wmfw_filename = NULL;
|
||||
unsigned int firmware_missing;
|
||||
unsigned int preloaded_fw_ver;
|
||||
bool firmware_missing;
|
||||
int ret = 0;
|
||||
|
||||
/* Prepare for a new DSP power-up */
|
||||
|
|
@ -537,24 +565,21 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
|
|||
|
||||
pm_runtime_get_sync(cs35l56->base.dev);
|
||||
|
||||
ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
|
||||
if (ret) {
|
||||
dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
|
||||
goto err_pm_put;
|
||||
}
|
||||
|
||||
firmware_missing &= CS35L56_FIRMWARE_MISSING;
|
||||
|
||||
/*
|
||||
* Firmware can only be downloaded if the CS35L56 is secured or is
|
||||
* running from the built-in ROM. If it is secured the BIOS will have
|
||||
* downloaded firmware, and the wmfw/bin files will only contain
|
||||
* tunings that are safe to download with the firmware running.
|
||||
* The firmware can only be upgraded if it is currently running
|
||||
* from the built-in ROM. If not, the wmfw/bin must be for the
|
||||
* version of firmware that is running on the chip.
|
||||
*/
|
||||
if (cs35l56->base.secured || firmware_missing) {
|
||||
cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
|
||||
&coeff_firmware, &coeff_filename);
|
||||
}
|
||||
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
|
||||
if (ret)
|
||||
goto err_pm_put;
|
||||
|
||||
if (firmware_missing)
|
||||
preloaded_fw_ver = 0;
|
||||
|
||||
cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver,
|
||||
&wmfw_firmware, &wmfw_filename,
|
||||
&coeff_firmware, &coeff_filename);
|
||||
|
||||
/*
|
||||
* If the BIOS didn't patch the firmware a bin file is mandatory to
|
||||
|
|
@ -569,12 +594,12 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
|
|||
mutex_lock(&cs35l56->base.irq_lock);
|
||||
|
||||
/*
|
||||
* When the device is running in secure mode the firmware files can
|
||||
* only contain insecure tunings and therefore we do not need to
|
||||
* shutdown the firmware to apply them and can use the lower cost
|
||||
* reinit sequence instead.
|
||||
* If the firmware hasn't been patched it must be shutdown before
|
||||
* doing a full patch and reset afterwards. If it is already
|
||||
* running a patched version the firmware files only contain
|
||||
* tunings and we can use the lower cost reinit sequence instead.
|
||||
*/
|
||||
if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) {
|
||||
if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
|
||||
ret = cs35l56_firmware_shutdown(&cs35l56->base);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
|
@ -593,7 +618,7 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
|
|||
if (coeff_filename)
|
||||
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
|
||||
|
||||
if (cs35l56->base.secured) {
|
||||
if (!firmware_missing) {
|
||||
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
|
||||
if (ret)
|
||||
goto err_powered_up;
|
||||
|
|
@ -976,6 +1001,9 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
|
|||
|
||||
regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
|
||||
ARRAY_SIZE(cs35l56_hda_dai_config));
|
||||
ret = cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* By default only enable one ASP1TXn, where n=amplifier index,
|
||||
|
|
@ -1035,16 +1063,6 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
|
|||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_KUNIT_TEST)
|
||||
/* Hooks to export static function to KUnit test */
|
||||
|
||||
int cs35l56_hda_test_hook_get_speaker_id(struct device *dev, int amp_index, int num_amps)
|
||||
{
|
||||
return cs35l56_hda_get_speaker_id(dev, amp_index, num_amps);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_hda_test_hook_get_speaker_id, SND_HDA_SCODEC_CS35L56);
|
||||
#endif
|
||||
|
||||
MODULE_DESCRIPTION("CS35L56 HDA Driver");
|
||||
MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
|
||||
MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
|
||||
|
|
|
|||
|
|
@ -1729,9 +1729,11 @@ static int default_bdl_pos_adj(struct azx *chip)
|
|||
/* some exceptions: Atoms seem problematic with value 1 */
|
||||
if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
switch (chip->pci->device) {
|
||||
case 0x0f04: /* Baytrail */
|
||||
case 0x2284: /* Braswell */
|
||||
case PCI_DEVICE_ID_INTEL_HDA_BYT:
|
||||
case PCI_DEVICE_ID_INTEL_HDA_BSW:
|
||||
return 32;
|
||||
case PCI_DEVICE_ID_INTEL_HDA_APL:
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,7 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
|
|||
spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
|
||||
spec->scodecs[CS8409_CODEC1]->codec = codec;
|
||||
spec->num_scodecs = 2;
|
||||
spec->gen.suppress_vmaster = 1;
|
||||
|
||||
codec->patch_ops = cs8409_dolphin_patch_ops;
|
||||
|
||||
|
|
|
|||
|
|
@ -439,6 +439,10 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
|
|||
alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
|
||||
fallthrough;
|
||||
case 0x10ec0215:
|
||||
case 0x10ec0285:
|
||||
case 0x10ec0289:
|
||||
alc_update_coef_idx(codec, 0x36, 1<<13, 0);
|
||||
fallthrough;
|
||||
case 0x10ec0230:
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0235:
|
||||
|
|
@ -452,9 +456,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
|
|||
case 0x10ec0283:
|
||||
case 0x10ec0286:
|
||||
case 0x10ec0288:
|
||||
case 0x10ec0285:
|
||||
case 0x10ec0298:
|
||||
case 0x10ec0289:
|
||||
case 0x10ec0300:
|
||||
alc_update_coef_idx(codec, 0x10, 1<<9, 0);
|
||||
break;
|
||||
|
|
@ -9577,7 +9579,7 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cs35l41_fixup_i2c_two,
|
||||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
|
||||
.chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
|
||||
},
|
||||
[ALC287_FIXUP_TAS2781_I2C] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
|
|
@ -9604,6 +9606,8 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
[ALC287_FIXUP_THINKPAD_I2S_SPK] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc287_fixup_bind_dacs,
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
|
||||
},
|
||||
[ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
|
|
@ -9653,6 +9657,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
|
||||
SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
|
||||
|
|
@ -9732,6 +9737,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
|
||||
SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2),
|
||||
SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
|
||||
SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
|
||||
SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
|
||||
SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
|
||||
|
|
@ -9852,6 +9858,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
|
||||
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
|
||||
|
|
@ -9957,6 +9964,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
||||
SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
|
||||
|
|
@ -10322,6 +10331,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
|
||||
SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
|
||||
SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
|
||||
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
|
||||
SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
|
||||
|
|
|
|||
|
|
@ -505,6 +505,13 @@ static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
clk_set_rate(drvdata->wclk, srate);
|
||||
clk_set_rate(drvdata->bclk, srate * ch * format);
|
||||
if (!drvdata->soc_mclk) {
|
||||
ret = acp_clk_enable(drvdata, srate, ch * format);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1464,8 +1471,13 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
|
|||
if (drv_data->amp_cpu_id == I2S_SP) {
|
||||
links[i].name = "acp-amp-codec";
|
||||
links[i].id = AMP_BE_ID;
|
||||
links[i].cpus = sof_sp_virtual;
|
||||
links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
|
||||
if (drv_data->platform == RENOIR) {
|
||||
links[i].cpus = sof_sp;
|
||||
links[i].num_cpus = ARRAY_SIZE(sof_sp);
|
||||
} else {
|
||||
links[i].cpus = sof_sp_virtual;
|
||||
links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
|
||||
}
|
||||
links[i].platforms = sof_component;
|
||||
links[i].num_platforms = ARRAY_SIZE(sof_component);
|
||||
links[i].dpcm_playback = 1;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
|
|||
.hs_codec_id = RT5682S,
|
||||
.amp_codec_id = RT1019,
|
||||
.dmic_codec_id = DMIC,
|
||||
.platform = RENOIR,
|
||||
.tdm_mode = false,
|
||||
};
|
||||
|
||||
|
|
@ -58,6 +59,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
|
|||
.hs_codec_id = RT5682S,
|
||||
.amp_codec_id = MAX98360A,
|
||||
.dmic_codec_id = DMIC,
|
||||
.platform = RENOIR,
|
||||
.tdm_mode = false,
|
||||
};
|
||||
|
||||
|
|
@ -68,6 +70,7 @@ static struct acp_card_drvdata sof_nau8825_data = {
|
|||
.hs_codec_id = NAU8825,
|
||||
.amp_codec_id = MAX98360A,
|
||||
.dmic_codec_id = DMIC,
|
||||
.platform = REMBRANDT,
|
||||
.soc_mclk = true,
|
||||
.tdm_mode = false,
|
||||
};
|
||||
|
|
@ -79,6 +82,7 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
|
|||
.hs_codec_id = RT5682S,
|
||||
.amp_codec_id = RT1019,
|
||||
.dmic_codec_id = DMIC,
|
||||
.platform = REMBRANDT,
|
||||
.soc_mclk = true,
|
||||
.tdm_mode = false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -354,6 +354,14 @@ static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
|
|||
},
|
||||
.driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
|
||||
},
|
||||
.driver_data = (void *)(ES83XX_ENABLE_DMIC),
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
|
||||
|
|
|
|||
|
|
@ -297,6 +297,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VF"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// Copyright (C) 2023 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -12,6 +13,15 @@
|
|||
#include "cs35l56.h"
|
||||
|
||||
static const struct reg_sequence cs35l56_patch[] = {
|
||||
/*
|
||||
* Firmware can change these to non-defaults to satisfy SDCA.
|
||||
* Ensure that they are at known defaults.
|
||||
*/
|
||||
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
|
||||
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
|
||||
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
|
||||
{ CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
|
||||
|
||||
/* These are not reset by a soft-reset, so patch to defaults. */
|
||||
{ CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
|
||||
{ CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
|
||||
|
|
@ -34,10 +44,9 @@ static const struct reg_default cs35l56_reg_defaults[] = {
|
|||
{ CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
|
||||
{ CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
|
||||
{ CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
|
||||
{ CS35L56_ASP1TX1_INPUT, 0x00000018 },
|
||||
{ CS35L56_ASP1TX2_INPUT, 0x00000019 },
|
||||
{ CS35L56_ASP1TX3_INPUT, 0x00000020 },
|
||||
{ CS35L56_ASP1TX4_INPUT, 0x00000028 },
|
||||
|
||||
/* no defaults for ASP1TX mixer */
|
||||
|
||||
{ CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
|
||||
{ CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
|
||||
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
|
||||
|
|
@ -195,6 +204,47 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The firmware boot sequence can overwrite the ASP1 config registers so that
|
||||
* they don't match regmap's view of their values. Rewrite the values from the
|
||||
* regmap cache into the hardware registers.
|
||||
*/
|
||||
int cs35l56_force_sync_asp1_registers_from_cache(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct reg_sequence asp1_regs[] = {
|
||||
{ .reg = CS35L56_ASP1_ENABLES1 },
|
||||
{ .reg = CS35L56_ASP1_CONTROL1 },
|
||||
{ .reg = CS35L56_ASP1_CONTROL2 },
|
||||
{ .reg = CS35L56_ASP1_CONTROL3 },
|
||||
{ .reg = CS35L56_ASP1_FRAME_CONTROL1 },
|
||||
{ .reg = CS35L56_ASP1_FRAME_CONTROL5 },
|
||||
{ .reg = CS35L56_ASP1_DATA_CONTROL1 },
|
||||
{ .reg = CS35L56_ASP1_DATA_CONTROL5 },
|
||||
};
|
||||
int i, ret;
|
||||
|
||||
/* Read values from regmap cache into a write sequence */
|
||||
for (i = 0; i < ARRAY_SIZE(asp1_regs); ++i) {
|
||||
ret = regmap_read(cs35l56_base->regmap, asp1_regs[i].reg, &asp1_regs[i].def);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Write the values cache-bypassed so that they will be written to silicon */
|
||||
ret = regmap_multi_reg_write_bypassed(cs35l56_base->regmap, asp1_regs,
|
||||
ARRAY_SIZE(asp1_regs));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(cs35l56_base->dev, "Failed to sync ASP1 registers: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_force_sync_asp1_registers_from_cache, SND_SOC_CS35L56_SHARED);
|
||||
|
||||
int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command)
|
||||
{
|
||||
unsigned int val;
|
||||
|
|
@ -400,17 +450,6 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
|
|||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Nothing to re-patch if we haven't done any patching yet. */
|
||||
if (!cs35l56_base->fw_patched)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If we have control of RESET we will have asserted it so the firmware
|
||||
* will need re-patching.
|
||||
*/
|
||||
if (cs35l56_base->reset_gpio)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
|
||||
* can't be used here to test for memory retention.
|
||||
|
|
@ -590,10 +629,35 @@ void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_ds
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED);
|
||||
|
||||
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
|
||||
bool *fw_missing, unsigned int *fw_version)
|
||||
{
|
||||
unsigned int prot_status;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
|
||||
|
||||
ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version);
|
||||
if (ret) {
|
||||
dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, SND_SOC_CS35L56_SHARED);
|
||||
|
||||
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
int ret;
|
||||
unsigned int devid, revid, otpid, secured;
|
||||
unsigned int devid, revid, otpid, secured, fw_ver;
|
||||
bool fw_missing;
|
||||
|
||||
/*
|
||||
* When the system is not using a reset_gpio ensure the device is
|
||||
|
|
@ -652,8 +716,13 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
|
|||
return ret;
|
||||
}
|
||||
|
||||
dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
|
||||
cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid);
|
||||
ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
|
||||
cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
|
||||
fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
|
||||
|
||||
/* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */
|
||||
regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
|
||||
|
|
@ -668,6 +737,41 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED);
|
||||
|
||||
int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
struct gpio_descs *descs;
|
||||
int speaker_id;
|
||||
int i, ret;
|
||||
|
||||
/* Read the speaker type qualifier from the motherboard GPIOs */
|
||||
descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN);
|
||||
if (!descs) {
|
||||
return -ENOENT;
|
||||
} else if (IS_ERR(descs)) {
|
||||
ret = PTR_ERR(descs);
|
||||
return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
|
||||
}
|
||||
|
||||
speaker_id = 0;
|
||||
for (i = 0; i < descs->ndescs; i++) {
|
||||
ret = gpiod_get_value_cansleep(descs->desc[i]);
|
||||
if (ret < 0) {
|
||||
dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i);
|
||||
goto err;
|
||||
}
|
||||
|
||||
speaker_id |= (ret << i);
|
||||
}
|
||||
|
||||
dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
|
||||
ret = speaker_id;
|
||||
err:
|
||||
gpiod_put_array(descs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, SND_SOC_CS35L56_SHARED);
|
||||
|
||||
static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
|
||||
[0x0C] = 128000,
|
||||
[0x0F] = 256000,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,135 @@ static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
return snd_soc_put_volsw(kcontrol, ucontrol);
|
||||
}
|
||||
|
||||
static const unsigned short cs35l56_asp1_mixer_regs[] = {
|
||||
CS35L56_ASP1TX1_INPUT, CS35L56_ASP1TX2_INPUT,
|
||||
CS35L56_ASP1TX3_INPUT, CS35L56_ASP1TX4_INPUT,
|
||||
};
|
||||
|
||||
static const char * const cs35l56_asp1_mux_control_names[] = {
|
||||
"ASP1 TX1 Source", "ASP1 TX2 Source", "ASP1 TX3 Source", "ASP1 TX4 Source"
|
||||
};
|
||||
|
||||
static int cs35l56_dspwait_asp1tx_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
int index = e->shift_l;
|
||||
unsigned int addr, val;
|
||||
int ret;
|
||||
|
||||
/* Wait for mux to be initialized */
|
||||
cs35l56_wait_dsp_ready(cs35l56);
|
||||
flush_work(&cs35l56->mux_init_work);
|
||||
|
||||
addr = cs35l56_asp1_mixer_regs[index];
|
||||
ret = regmap_read(cs35l56->base.regmap, addr, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= CS35L56_ASP_TXn_SRC_MASK;
|
||||
ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_dspwait_asp1tx_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
int item = ucontrol->value.enumerated.item[0];
|
||||
int index = e->shift_l;
|
||||
unsigned int addr, val;
|
||||
bool changed;
|
||||
int ret;
|
||||
|
||||
/* Wait for mux to be initialized */
|
||||
cs35l56_wait_dsp_ready(cs35l56);
|
||||
flush_work(&cs35l56->mux_init_work);
|
||||
|
||||
addr = cs35l56_asp1_mixer_regs[index];
|
||||
val = snd_soc_enum_item_to_val(e, item);
|
||||
|
||||
ret = regmap_update_bits_check(cs35l56->base.regmap, addr,
|
||||
CS35L56_ASP_TXn_SRC_MASK, val, &changed);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
if (changed)
|
||||
snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void cs35l56_mark_asp1_mixer_widgets_dirty(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cs35l56->component);
|
||||
const char *prefix = cs35l56->component->name_prefix;
|
||||
char full_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||
const char *name;
|
||||
struct snd_kcontrol *kcontrol;
|
||||
struct soc_enum *e;
|
||||
unsigned int val[4];
|
||||
int i, item, ret;
|
||||
|
||||
/*
|
||||
* Resume so we can read the registers from silicon if the regmap
|
||||
* cache has not yet been populated.
|
||||
*/
|
||||
ret = pm_runtime_resume_and_get(cs35l56->base.dev);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = regmap_bulk_read(cs35l56->base.regmap, CS35L56_ASP1TX1_INPUT,
|
||||
val, ARRAY_SIZE(val));
|
||||
|
||||
pm_runtime_mark_last_busy(cs35l56->base.dev);
|
||||
pm_runtime_put_autosuspend(cs35l56->base.dev);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cs35l56->base.dev, "Failed to read ASP1 mixer regs: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
snd_soc_card_mutex_lock(dapm->card);
|
||||
WARN_ON(!dapm->card->instantiated);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs35l56_asp1_mux_control_names); ++i) {
|
||||
name = cs35l56_asp1_mux_control_names[i];
|
||||
|
||||
if (prefix) {
|
||||
snprintf(full_name, sizeof(full_name), "%s %s", prefix, name);
|
||||
name = full_name;
|
||||
}
|
||||
|
||||
kcontrol = snd_soc_card_get_kcontrol(dapm->card, name);
|
||||
if (!kcontrol) {
|
||||
dev_warn(cs35l56->base.dev, "Could not find control %s\n", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
e = (struct soc_enum *)kcontrol->private_value;
|
||||
item = snd_soc_enum_val_to_item(e, val[i] & CS35L56_ASP_TXn_SRC_MASK);
|
||||
snd_soc_dapm_mux_update_power(dapm, kcontrol, item, e, NULL);
|
||||
}
|
||||
|
||||
snd_soc_card_mutex_unlock(dapm->card);
|
||||
}
|
||||
|
||||
static void cs35l56_mux_init_work(struct work_struct *work)
|
||||
{
|
||||
struct cs35l56_private *cs35l56 = container_of(work,
|
||||
struct cs35l56_private,
|
||||
mux_init_work);
|
||||
|
||||
cs35l56_mark_asp1_mixer_widgets_dirty(cs35l56);
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
|
||||
|
||||
static const struct snd_kcontrol_new cs35l56_controls[] = {
|
||||
|
|
@ -77,40 +206,44 @@ static const struct snd_kcontrol_new cs35l56_controls[] = {
|
|||
};
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
|
||||
CS35L56_ASP1TX1_INPUT,
|
||||
0, CS35L56_ASP_TXn_SRC_MASK,
|
||||
SND_SOC_NOPM,
|
||||
0, 0,
|
||||
cs35l56_tx_input_texts,
|
||||
cs35l56_tx_input_values);
|
||||
|
||||
static const struct snd_kcontrol_new asp1_tx1_mux =
|
||||
SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
|
||||
SOC_DAPM_ENUM_EXT("ASP1TX1 SRC", cs35l56_asp1tx1_enum,
|
||||
cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
|
||||
CS35L56_ASP1TX2_INPUT,
|
||||
0, CS35L56_ASP_TXn_SRC_MASK,
|
||||
SND_SOC_NOPM,
|
||||
1, 0,
|
||||
cs35l56_tx_input_texts,
|
||||
cs35l56_tx_input_values);
|
||||
|
||||
static const struct snd_kcontrol_new asp1_tx2_mux =
|
||||
SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
|
||||
SOC_DAPM_ENUM_EXT("ASP1TX2 SRC", cs35l56_asp1tx2_enum,
|
||||
cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
|
||||
CS35L56_ASP1TX3_INPUT,
|
||||
0, CS35L56_ASP_TXn_SRC_MASK,
|
||||
SND_SOC_NOPM,
|
||||
2, 0,
|
||||
cs35l56_tx_input_texts,
|
||||
cs35l56_tx_input_values);
|
||||
|
||||
static const struct snd_kcontrol_new asp1_tx3_mux =
|
||||
SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
|
||||
SOC_DAPM_ENUM_EXT("ASP1TX3 SRC", cs35l56_asp1tx3_enum,
|
||||
cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
|
||||
CS35L56_ASP1TX4_INPUT,
|
||||
0, CS35L56_ASP_TXn_SRC_MASK,
|
||||
SND_SOC_NOPM,
|
||||
3, 0,
|
||||
cs35l56_tx_input_texts,
|
||||
cs35l56_tx_input_values);
|
||||
|
||||
static const struct snd_kcontrol_new asp1_tx4_mux =
|
||||
SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
|
||||
SOC_DAPM_ENUM_EXT("ASP1TX4 SRC", cs35l56_asp1tx4_enum,
|
||||
cs35l56_dspwait_asp1tx_get, cs35l56_dspwait_asp1tx_put);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
|
||||
CS35L56_SWIRE_DP3_CH1_INPUT,
|
||||
|
|
@ -148,6 +281,21 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
|
|||
static const struct snd_kcontrol_new sdw1_tx4_mux =
|
||||
SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
|
||||
|
||||
static int cs35l56_asp1_cfg_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* Override register values set by firmware boot */
|
||||
return cs35l56_force_sync_asp1_registers_from_cache(&cs35l56->base);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
|
|
@ -184,6 +332,9 @@ static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
|
||||
SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("ASP1 CFG", SND_SOC_NOPM, 0, 0, cs35l56_asp1_cfg_event,
|
||||
SND_SOC_DAPM_PRE_PMU),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
|
|
@ -251,6 +402,9 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
|
|||
{ "AMP", NULL, "VDD_B" },
|
||||
{ "AMP", NULL, "VDD_AMP" },
|
||||
|
||||
{ "ASP1 Playback", NULL, "ASP1 CFG" },
|
||||
{ "ASP1 Capture", NULL, "ASP1 CFG" },
|
||||
|
||||
{ "ASP1 Playback", NULL, "PLAY" },
|
||||
{ "SDW1 Playback", NULL, "PLAY" },
|
||||
|
||||
|
|
@ -650,7 +804,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
|
||||
static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
@ -662,19 +816,10 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
|
|||
cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
|
||||
}
|
||||
|
||||
static void cs35l56_patch(struct cs35l56_private *cs35l56)
|
||||
static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
|
||||
{
|
||||
unsigned int firmware_missing;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
|
||||
if (ret) {
|
||||
dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
firmware_missing &= CS35L56_FIRMWARE_MISSING;
|
||||
|
||||
/*
|
||||
* Disable SoundWire interrupts to prevent race with IRQ work.
|
||||
* Setting sdw_irq_no_unmask prevents the handler re-enabling
|
||||
|
|
@ -747,23 +892,59 @@ static void cs35l56_dsp_work(struct work_struct *work)
|
|||
struct cs35l56_private *cs35l56 = container_of(work,
|
||||
struct cs35l56_private,
|
||||
dsp_work);
|
||||
unsigned int firmware_version;
|
||||
bool firmware_missing;
|
||||
int ret;
|
||||
|
||||
if (!cs35l56->base.init_done)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(cs35l56->base.dev);
|
||||
|
||||
/*
|
||||
* When the device is running in secure mode the firmware files can
|
||||
* only contain insecure tunings and therefore we do not need to
|
||||
* shutdown the firmware to apply them and can use the lower cost
|
||||
* reinit sequence instead.
|
||||
*/
|
||||
if (cs35l56->base.secured)
|
||||
cs35l56_secure_patch(cs35l56);
|
||||
else
|
||||
cs35l56_patch(cs35l56);
|
||||
ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Populate fw file qualifier with the revision and security state */
|
||||
kfree(cs35l56->dsp.fwf_name);
|
||||
if (firmware_missing) {
|
||||
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
|
||||
} else {
|
||||
/* Firmware files must match the running firmware version */
|
||||
cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
|
||||
"%02x%s-%06x-dsp1",
|
||||
cs35l56->base.rev,
|
||||
cs35l56->base.secured ? "-s" : "",
|
||||
firmware_version);
|
||||
}
|
||||
|
||||
if (!cs35l56->dsp.fwf_name)
|
||||
goto err;
|
||||
|
||||
dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
|
||||
cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
|
||||
|
||||
/*
|
||||
* The firmware cannot be patched if it is already running from
|
||||
* patch RAM. In this case the firmware files are versioned to
|
||||
* match the running firmware version and will only contain
|
||||
* tunings. We do not need to shutdown the firmware to apply
|
||||
* tunings so can use the lower cost reinit sequence instead.
|
||||
*/
|
||||
if (!firmware_missing)
|
||||
cs35l56_reinit_patch(cs35l56);
|
||||
else
|
||||
cs35l56_patch(cs35l56, firmware_missing);
|
||||
|
||||
|
||||
/*
|
||||
* Set starting value of ASP1 mux widgets. Updating a mux takes
|
||||
* the DAPM mutex. Post this to a separate job so that DAPM
|
||||
* power-up can wait for dsp_work to complete without deadlocking
|
||||
* on the DAPM mutex.
|
||||
*/
|
||||
queue_work(cs35l56->dsp_wq, &cs35l56->mux_init_work);
|
||||
err:
|
||||
pm_runtime_mark_last_busy(cs35l56->base.dev);
|
||||
pm_runtime_put_autosuspend(cs35l56->base.dev);
|
||||
}
|
||||
|
|
@ -778,10 +959,19 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
|
|||
|
||||
if (!cs35l56->dsp.system_name &&
|
||||
(snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) {
|
||||
cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
|
||||
GFP_KERNEL,
|
||||
"%04x%04x",
|
||||
vendor, device);
|
||||
/* Append a speaker qualifier if there is a speaker ID */
|
||||
if (cs35l56->speaker_id >= 0) {
|
||||
cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
|
||||
GFP_KERNEL,
|
||||
"%04x%04x-spkid%d",
|
||||
vendor, device,
|
||||
cs35l56->speaker_id);
|
||||
} else {
|
||||
cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
|
||||
GFP_KERNEL,
|
||||
"%04x%04x",
|
||||
vendor, device);
|
||||
}
|
||||
if (!cs35l56->dsp.system_name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -809,6 +999,17 @@ static void cs35l56_component_remove(struct snd_soc_component *component)
|
|||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
cancel_work_sync(&cs35l56->dsp_work);
|
||||
cancel_work_sync(&cs35l56->mux_init_work);
|
||||
|
||||
if (cs35l56->dsp.cs_dsp.booted)
|
||||
wm_adsp_power_down(&cs35l56->dsp);
|
||||
|
||||
wm_adsp2_component_remove(&cs35l56->dsp, component);
|
||||
|
||||
kfree(cs35l56->dsp.fwf_name);
|
||||
cs35l56->dsp.fwf_name = NULL;
|
||||
|
||||
cs35l56->component = NULL;
|
||||
}
|
||||
|
||||
static int cs35l56_set_bias_level(struct snd_soc_component *component,
|
||||
|
|
@ -869,8 +1070,10 @@ int cs35l56_system_suspend(struct device *dev)
|
|||
|
||||
dev_dbg(dev, "system_suspend\n");
|
||||
|
||||
if (cs35l56->component)
|
||||
if (cs35l56->component) {
|
||||
flush_work(&cs35l56->dsp_work);
|
||||
cancel_work_sync(&cs35l56->mux_init_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* The interrupt line is normally shared, but after we start suspending
|
||||
|
|
@ -1021,6 +1224,7 @@ static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
|
|||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
|
||||
INIT_WORK(&cs35l56->mux_init_work, cs35l56_mux_init_work);
|
||||
|
||||
dsp = &cs35l56->dsp;
|
||||
cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp);
|
||||
|
|
@ -1050,7 +1254,13 @@ static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56)
|
|||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
|
||||
/* Append a speaker qualifier if there is a speaker ID */
|
||||
if (cs35l56->speaker_id >= 0)
|
||||
cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
|
||||
prop, cs35l56->speaker_id);
|
||||
else
|
||||
cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL);
|
||||
|
||||
if (cs35l56->dsp.system_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -1065,6 +1275,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
|
|||
|
||||
init_completion(&cs35l56->init_completion);
|
||||
mutex_init(&cs35l56->base.irq_lock);
|
||||
cs35l56->speaker_id = -ENOENT;
|
||||
|
||||
dev_set_drvdata(cs35l56->base.dev, cs35l56);
|
||||
|
||||
|
|
@ -1101,6 +1312,12 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
|
|||
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
|
||||
}
|
||||
|
||||
ret = cs35l56_get_speaker_id(&cs35l56->base);
|
||||
if ((ret < 0) && (ret != -ENOENT))
|
||||
goto err;
|
||||
|
||||
cs35l56->speaker_id = ret;
|
||||
|
||||
ret = cs35l56_get_firmware_uid(cs35l56);
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
|
@ -1152,11 +1369,9 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Populate the DSP information with the revision and security state */
|
||||
cs35l56->dsp.part = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "cs35l56%s-%02x",
|
||||
cs35l56->base.secured ? "s" : "", cs35l56->base.rev);
|
||||
if (!cs35l56->dsp.part)
|
||||
return -ENOMEM;
|
||||
ret = cs35l56_set_patch(&cs35l56->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!cs35l56->base.reset_gpio) {
|
||||
dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n");
|
||||
|
|
@ -1190,10 +1405,6 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cs35l56_set_patch(&cs35l56->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Registers could be dirty after soft reset or SoundWire enumeration */
|
||||
regcache_sync(cs35l56->base.regmap);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct cs35l56_private {
|
|||
struct wm_adsp dsp; /* must be first member */
|
||||
struct cs35l56_base base;
|
||||
struct work_struct dsp_work;
|
||||
struct work_struct mux_init_work;
|
||||
struct workqueue_struct *dsp_wq;
|
||||
struct snd_soc_component *component;
|
||||
struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
|
||||
|
|
@ -44,6 +45,7 @@ struct cs35l56_private {
|
|||
bool sdw_attached;
|
||||
struct completion init_completion;
|
||||
|
||||
int speaker_id;
|
||||
u32 rx_mask;
|
||||
u32 tx_mask;
|
||||
u8 asp_slot_width;
|
||||
|
|
|
|||
186
sound/soc/codecs/es8326.c
Executable file → Normal file
186
sound/soc/codecs/es8326.c
Executable file → Normal file
|
|
@ -45,6 +45,82 @@ struct es8326_priv {
|
|||
int jack_remove_retry;
|
||||
};
|
||||
|
||||
static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int crosstalk_h, crosstalk_l;
|
||||
unsigned int crosstalk;
|
||||
|
||||
regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
|
||||
regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
|
||||
crosstalk_h &= 0x20;
|
||||
crosstalk_l &= 0xf0;
|
||||
crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4;
|
||||
ucontrol->value.integer.value[0] = crosstalk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int crosstalk_h, crosstalk_l;
|
||||
unsigned int crosstalk;
|
||||
|
||||
crosstalk = ucontrol->value.integer.value[0];
|
||||
regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
|
||||
crosstalk_h = (crosstalk & 0x10) << 1;
|
||||
crosstalk_l &= 0x0f;
|
||||
crosstalk_l |= (crosstalk & 0x0f) << 4;
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
|
||||
0x20, crosstalk_h);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int crosstalk_h, crosstalk_l;
|
||||
unsigned int crosstalk;
|
||||
|
||||
regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
|
||||
regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
|
||||
crosstalk_h &= 0x10;
|
||||
crosstalk_l &= 0x0f;
|
||||
crosstalk = crosstalk_h | crosstalk_l;
|
||||
ucontrol->value.integer.value[0] = crosstalk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int crosstalk_h, crosstalk_l;
|
||||
unsigned int crosstalk;
|
||||
|
||||
crosstalk = ucontrol->value.integer.value[0];
|
||||
regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
|
||||
crosstalk_h = crosstalk & 0x10;
|
||||
crosstalk_l &= 0xf0;
|
||||
crosstalk_l |= crosstalk & 0x0f;
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
|
||||
0x10, crosstalk_h);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
|
||||
|
|
@ -102,6 +178,10 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
|
|||
SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
|
||||
0, 0x0f, 0, drc_target_tlv),
|
||||
|
||||
SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
|
||||
es8326_crosstalk1_get, es8326_crosstalk1_set),
|
||||
SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
|
||||
es8326_crosstalk2_get, es8326_crosstalk2_set),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
|
||||
|
|
@ -117,12 +197,6 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
|
|||
SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0),
|
||||
|
||||
/* ADC Digital Mute */
|
||||
SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0),
|
||||
|
||||
/* Analog Power Supply*/
|
||||
SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1),
|
||||
SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1),
|
||||
|
|
@ -142,15 +216,10 @@ static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = {
|
|||
};
|
||||
|
||||
static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
|
||||
{"ADC L1", NULL, "MIC1"},
|
||||
{"ADC R1", NULL, "MIC2"},
|
||||
{"ADC L2", NULL, "MIC3"},
|
||||
{"ADC R2", NULL, "MIC4"},
|
||||
|
||||
{"ADC L", NULL, "ADC L1"},
|
||||
{"ADC R", NULL, "ADC R1"},
|
||||
{"ADC L", NULL, "ADC L2"},
|
||||
{"ADC R", NULL, "ADC R2"},
|
||||
{"ADC L", NULL, "MIC1"},
|
||||
{"ADC R", NULL, "MIC2"},
|
||||
{"ADC L", NULL, "MIC3"},
|
||||
{"ADC R", NULL, "MIC4"},
|
||||
|
||||
{"I2S OUT", NULL, "ADC L"},
|
||||
{"I2S OUT", NULL, "ADC R"},
|
||||
|
|
@ -440,10 +509,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
|
|||
unsigned int offset_l, offset_r;
|
||||
|
||||
if (mute) {
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
|
||||
ES8326_MUTE_MASK, ES8326_MUTE);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0);
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
|
||||
ES8326_MUTE_MASK, ES8326_MUTE);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
|
||||
0x30, 0x00);
|
||||
} else {
|
||||
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
|
||||
0x0F, 0x0F);
|
||||
}
|
||||
} else {
|
||||
if (!es8326->calibrated) {
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
|
||||
|
|
@ -456,11 +531,22 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
|
|||
regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
|
||||
es8326->calibrated = true;
|
||||
}
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
|
||||
regmap_write(es8326->regmap, ES8326_HP_VOL, 0x91);
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
|
||||
ES8326_MUTE_MASK, ~(ES8326_MUTE));
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01);
|
||||
usleep_range(1000, 5000);
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
|
||||
usleep_range(1000, 5000);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
|
||||
ES8326_MUTE_MASK, ~(ES8326_MUTE));
|
||||
} else {
|
||||
msleep(300);
|
||||
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
|
||||
0x0F, 0x00);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -477,23 +563,20 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
|
||||
regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02);
|
||||
usleep_range(5000, 10000);
|
||||
regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
|
||||
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
|
||||
(ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
|
||||
regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
|
||||
regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
|
||||
regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
|
||||
|
||||
regmap_update_bits(es8326->regmap, ES8326_RESET,
|
||||
ES8326_CSM_ON, ES8326_CSM_ON);
|
||||
regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00);
|
||||
break;
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
|
||||
regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00);
|
||||
regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
|
||||
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
|
||||
break;
|
||||
|
|
@ -513,7 +596,7 @@ static const struct snd_soc_dai_ops es8326_ops = {
|
|||
.set_fmt = es8326_set_dai_fmt,
|
||||
.set_sysclk = es8326_set_dai_sysclk,
|
||||
.mute_stream = es8326_mute,
|
||||
.no_capture_mute = 1,
|
||||
.no_capture_mute = 0,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver es8326_dai = {
|
||||
|
|
@ -672,6 +755,8 @@ static void es8326_jack_detect_handler(struct work_struct *work)
|
|||
es8326->hp = 0;
|
||||
}
|
||||
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
|
||||
regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03);
|
||||
/*
|
||||
* Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
|
||||
*/
|
||||
|
|
@ -695,8 +780,11 @@ static void es8326_jack_detect_handler(struct work_struct *work)
|
|||
* Don't report jack status.
|
||||
*/
|
||||
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
|
||||
es8326_enable_micbias(es8326->component);
|
||||
usleep_range(50000, 70000);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
|
||||
regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x08);
|
||||
queue_delayed_work(system_wq, &es8326->jack_detect_work,
|
||||
msecs_to_jiffies(400));
|
||||
es8326->hp = 1;
|
||||
|
|
@ -736,13 +824,10 @@ static void es8326_jack_detect_handler(struct work_struct *work)
|
|||
static irqreturn_t es8326_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct es8326_priv *es8326 = dev_id;
|
||||
struct snd_soc_component *comp = es8326->component;
|
||||
|
||||
if (!es8326->jack)
|
||||
goto out;
|
||||
|
||||
es8326_enable_micbias(comp);
|
||||
|
||||
if (es8326->jack->status & SND_JACK_HEADSET)
|
||||
queue_delayed_work(system_wq, &es8326->jack_detect_work,
|
||||
msecs_to_jiffies(10));
|
||||
|
|
@ -766,14 +851,14 @@ static int es8326_calibrate(struct snd_soc_component *component)
|
|||
if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) {
|
||||
dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n");
|
||||
regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x03);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f);
|
||||
regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x03);
|
||||
regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
|
||||
regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
|
||||
usleep_range(15000, 20000);
|
||||
regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
|
||||
|
|
@ -814,13 +899,13 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
/* reset internal clock state */
|
||||
regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
|
||||
regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
|
||||
usleep_range(10000, 15000);
|
||||
regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xe9);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4b);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xcb);
|
||||
/* set headphone default type and detect pin */
|
||||
regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83);
|
||||
regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
|
||||
regmap_write(es8326->regmap, ES8326_HP_MISC, 0x30);
|
||||
|
||||
/* set internal oscillator as clock source of headpone cp */
|
||||
regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x89);
|
||||
|
|
@ -828,14 +913,15 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
/* clock manager reset release */
|
||||
regmap_write(es8326->regmap, ES8326_RESET, 0x17);
|
||||
/* set headphone detection as half scan mode */
|
||||
regmap_write(es8326->regmap, ES8326_HP_MISC, 0x30);
|
||||
regmap_write(es8326->regmap, ES8326_HP_MISC, 0x3d);
|
||||
regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
|
||||
|
||||
/* enable headphone driver */
|
||||
regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
|
||||
usleep_range(2000, 5000);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xa3);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xb3);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33);
|
||||
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
|
||||
|
||||
regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
|
||||
|
|
@ -844,6 +930,8 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00);
|
||||
/* calibrate for B version */
|
||||
es8326_calibrate(component);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00);
|
||||
/* turn off headphone out */
|
||||
regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00);
|
||||
/* set ADC and DAC in low power mode */
|
||||
|
|
@ -856,6 +944,14 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08);
|
||||
regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
|
||||
|
||||
regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
|
||||
((es8326->version == ES8326_VERSION_B) ?
|
||||
(ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
|
||||
(ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
|
||||
usleep_range(5000, 10000);
|
||||
es8326_enable_micbias(es8326->component);
|
||||
usleep_range(50000, 70000);
|
||||
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
|
||||
regmap_write(es8326->regmap, ES8326_INT_SOURCE,
|
||||
(ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
|
||||
regmap_write(es8326->regmap, ES8326_INTOUT_IO,
|
||||
|
|
@ -864,7 +960,7 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
(ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
|
||||
regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
|
||||
|
||||
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
|
||||
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
|
||||
regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
|
||||
regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK,
|
||||
ES8326_MIC1_SEL);
|
||||
|
|
@ -872,11 +968,7 @@ static int es8326_resume(struct snd_soc_component *component)
|
|||
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK,
|
||||
ES8326_MUTE);
|
||||
|
||||
regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
|
||||
((es8326->version == ES8326_VERSION_B) ?
|
||||
(ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
|
||||
(ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
|
||||
regmap_write(es8326->regmap, ES8326_HP_VOL, 0x11);
|
||||
regmap_write(es8326->regmap, ES8326_ADC_MUTE, 0x0f);
|
||||
|
||||
es8326->jack_remove_retry = 0;
|
||||
es8326->hp = 0;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
#define ES8326_DAC_VOL 0x50
|
||||
#define ES8326_DRC_RECOVERY 0x53
|
||||
#define ES8326_DRC_WINSIZE 0x54
|
||||
#define ES8326_DAC_CROSSTALK 0x55
|
||||
#define ES8326_HPJACK_TIMER 0x56
|
||||
#define ES8326_HPDET_TYPE 0x57
|
||||
#define ES8326_INT_SOURCE 0x58
|
||||
|
|
@ -100,7 +101,7 @@
|
|||
#define ES8326_MUTE (3 << 0)
|
||||
|
||||
/* ES8326_CLK_CTL */
|
||||
#define ES8326_CLK_ON (0x7f << 0)
|
||||
#define ES8326_CLK_ON (0x7e << 0)
|
||||
#define ES8326_CLK_OFF (0 << 0)
|
||||
|
||||
/* ES8326_CLK_INV */
|
||||
|
|
|
|||
|
|
@ -1584,7 +1584,6 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
|
|||
u16 gain_reg;
|
||||
u16 reg;
|
||||
int val;
|
||||
int offset_val = 0;
|
||||
struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (w->shift == WSA_MACRO_COMP1) {
|
||||
|
|
@ -1623,10 +1622,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
|
|||
CDC_WSA_RX1_RX_PATH_MIX_SEC0,
|
||||
CDC_WSA_RX_PGA_HALF_DB_MASK,
|
||||
CDC_WSA_RX_PGA_HALF_DB_ENABLE);
|
||||
offset_val = -2;
|
||||
}
|
||||
val = snd_soc_component_read(component, gain_reg);
|
||||
val += offset_val;
|
||||
snd_soc_component_write(component, gain_reg, val);
|
||||
wsa_macro_config_ear_spkr_gain(component, wsa,
|
||||
event, gain_reg);
|
||||
|
|
@ -1654,10 +1651,6 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
|
|||
CDC_WSA_RX1_RX_PATH_MIX_SEC0,
|
||||
CDC_WSA_RX_PGA_HALF_DB_MASK,
|
||||
CDC_WSA_RX_PGA_HALF_DB_DISABLE);
|
||||
offset_val = 2;
|
||||
val = snd_soc_component_read(component, gain_reg);
|
||||
val += offset_val;
|
||||
snd_soc_component_write(component, gain_reg, val);
|
||||
}
|
||||
wsa_macro_config_ear_spkr_gain(component, wsa,
|
||||
event, gain_reg);
|
||||
|
|
|
|||
|
|
@ -3033,7 +3033,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
|
|||
{
|
||||
struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
|
||||
u16 gain_reg;
|
||||
int offset_val = 0;
|
||||
int val = 0;
|
||||
|
||||
switch (w->reg) {
|
||||
|
|
@ -3073,7 +3072,6 @@ static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
|
|||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
val = snd_soc_component_read(comp, gain_reg);
|
||||
val += offset_val;
|
||||
snd_soc_component_write(comp, gain_reg, val);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
|
|
@ -3294,7 +3292,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
|
|||
u16 gain_reg;
|
||||
u16 reg;
|
||||
int val;
|
||||
int offset_val = 0;
|
||||
|
||||
if (!(snd_soc_dapm_widget_name_cmp(w, "RX INT0 INTERP"))) {
|
||||
reg = WCD9335_CDC_RX0_RX_PATH_CTL;
|
||||
|
|
@ -3337,7 +3334,6 @@ static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
|
|||
case SND_SOC_DAPM_POST_PMU:
|
||||
wcd9335_config_compander(comp, w->shift, event);
|
||||
val = snd_soc_component_read(comp, gain_reg);
|
||||
val += offset_val;
|
||||
snd_soc_component_write(comp, gain_reg, val);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/slimbus.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ struct wcd938x_priv {
|
|||
};
|
||||
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
|
||||
static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, -3000);
|
||||
static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, 0);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000);
|
||||
|
||||
struct wcd938x_mbhc_zdet_param {
|
||||
|
|
@ -3587,10 +3587,8 @@ static int wcd938x_probe(struct platform_device *pdev)
|
|||
mutex_init(&wcd938x->micb_lock);
|
||||
|
||||
ret = wcd938x_populate_dt_data(wcd938x, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wcd938x_add_slave_components(wcd938x, dev, &match);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -739,19 +739,25 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
|
|||
const char *filetype)
|
||||
{
|
||||
struct cs_dsp *cs_dsp = &dsp->cs_dsp;
|
||||
const char *fwf;
|
||||
char *s, c;
|
||||
int ret = 0;
|
||||
|
||||
if (dsp->fwf_name)
|
||||
fwf = dsp->fwf_name;
|
||||
else
|
||||
fwf = dsp->cs_dsp.name;
|
||||
|
||||
if (system_name && asoc_component_prefix)
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,
|
||||
dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
|
||||
fwf, wm_adsp_fw[dsp->fw].file, system_name,
|
||||
asoc_component_prefix, filetype);
|
||||
else if (system_name)
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,
|
||||
dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name,
|
||||
fwf, wm_adsp_fw[dsp->fw].file, system_name,
|
||||
filetype);
|
||||
else
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, dsp->fwf_name,
|
||||
*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf,
|
||||
wm_adsp_fw[dsp->fw].file, filetype);
|
||||
|
||||
if (*filename == NULL)
|
||||
|
|
@ -823,6 +829,23 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
|
|||
}
|
||||
}
|
||||
|
||||
/* Check system-specific bin without wmfw before falling back to generic */
|
||||
if (dsp->wmfw_optional && system_name) {
|
||||
if (asoc_component_prefix)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
asoc_component_prefix, "bin");
|
||||
|
||||
if (!*coeff_firmware)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
NULL, "bin");
|
||||
|
||||
if (*coeff_firmware)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check legacy location */
|
||||
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
|
||||
"", NULL, NULL, "wmfw")) {
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
|
|
@ -830,62 +853,28 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Fall back to generic wmfw and optional matching bin */
|
||||
ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
|
||||
cirrus_dir, NULL, NULL, "wmfw");
|
||||
if (!ret) {
|
||||
if (!ret || dsp->wmfw_optional) {
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, NULL, NULL, "bin");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dsp->wmfw_optional) {
|
||||
if (system_name) {
|
||||
if (asoc_component_prefix)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
asoc_component_prefix, "bin");
|
||||
|
||||
if (!*coeff_firmware)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, system_name,
|
||||
NULL, "bin");
|
||||
}
|
||||
|
||||
if (!*coeff_firmware)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
"", NULL, NULL, "bin");
|
||||
|
||||
if (!*coeff_firmware)
|
||||
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
|
||||
cirrus_dir, NULL, NULL, "bin");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
|
||||
cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file,
|
||||
system_name, asoc_component_prefix);
|
||||
cirrus_dir, dsp->part,
|
||||
dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,
|
||||
wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int wm_adsp_common_init(struct wm_adsp *dsp)
|
||||
{
|
||||
char *p;
|
||||
|
||||
INIT_LIST_HEAD(&dsp->compr_list);
|
||||
INIT_LIST_HEAD(&dsp->buffer_list);
|
||||
|
||||
if (!dsp->fwf_name) {
|
||||
p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->fwf_name = p;
|
||||
for (; *p != 0; ++p)
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1098,7 +1098,11 @@ static int wsa_dev_mode_put(struct snd_kcontrol *kcontrol,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(pa_gain, -300, 150, -300);
|
||||
static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(pa_gain,
|
||||
0, 14, TLV_DB_SCALE_ITEM(-300, 0, 0),
|
||||
15, 29, TLV_DB_SCALE_ITEM(-300, 150, 0),
|
||||
30, 31, TLV_DB_SCALE_ITEM(1800, 0, 0),
|
||||
);
|
||||
|
||||
static int wsa883x_get_swr_port(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
|
|
|
|||
|
|
@ -32,12 +32,14 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
|
|||
case WSA_CODEC_DMA_RX_0:
|
||||
case WSA_CODEC_DMA_RX_1:
|
||||
/*
|
||||
* set limit of 0dB on Digital Volume for Speakers,
|
||||
* this can prevent damage of speakers to some extent without
|
||||
* active speaker protection
|
||||
* Set limit of -3 dB on Digital Volume and 0 dB on PA Volume
|
||||
* to reduce the risk of speaker damage until we have active
|
||||
* speaker protection in place.
|
||||
*/
|
||||
snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 84);
|
||||
snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 84);
|
||||
snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81);
|
||||
snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81);
|
||||
snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17);
|
||||
snd_soc_limit_volume(card, "SpkrRight PA Volume", 17);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define MAX_DEFAULT_CH_MAP_SIZE 7
|
||||
#define MAX_DEFAULT_CH_MAP_SIZE 8
|
||||
static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZE] = {
|
||||
{ .cpu = 0, .codec = 0 },
|
||||
{ .cpu = 1, .codec = 1 },
|
||||
|
|
@ -1046,6 +1046,7 @@ static struct snd_soc_dai_link_ch_map default_ch_map_sync[MAX_DEFAULT_CH_MAP_SIZ
|
|||
{ .cpu = 4, .codec = 4 },
|
||||
{ .cpu = 5, .codec = 5 },
|
||||
{ .cpu = 6, .codec = 6 },
|
||||
{ .cpu = 7, .codec = 7 },
|
||||
};
|
||||
static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZE] = {
|
||||
{ .cpu = 0, .codec = 0 },
|
||||
|
|
@ -1055,6 +1056,7 @@ static struct snd_soc_dai_link_ch_map default_ch_map_1cpu[MAX_DEFAULT_CH_MAP_SIZ
|
|||
{ .cpu = 0, .codec = 4 },
|
||||
{ .cpu = 0, .codec = 5 },
|
||||
{ .cpu = 0, .codec = 6 },
|
||||
{ .cpu = 0, .codec = 7 },
|
||||
};
|
||||
static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_SIZE] = {
|
||||
{ .cpu = 0, .codec = 0 },
|
||||
|
|
@ -1064,6 +1066,7 @@ static struct snd_soc_dai_link_ch_map default_ch_map_1codec[MAX_DEFAULT_CH_MAP_S
|
|||
{ .cpu = 4, .codec = 0 },
|
||||
{ .cpu = 5, .codec = 0 },
|
||||
{ .cpu = 6, .codec = 0 },
|
||||
{ .cpu = 7, .codec = 0 },
|
||||
};
|
||||
static int snd_soc_compensate_channel_connection_map(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_link)
|
||||
|
|
|
|||
|
|
@ -577,6 +577,11 @@ static const struct of_device_id sun4i_spdif_of_match[] = {
|
|||
.compatible = "allwinner,sun50i-h6-spdif",
|
||||
.data = &sun50i_h6_spdif_quirks,
|
||||
},
|
||||
{
|
||||
.compatible = "allwinner,sun50i-h616-spdif",
|
||||
/* Essentially the same as the H6, but without RX */
|
||||
.data = &sun50i_h6_spdif_quirks,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
|
||||
|
|
|
|||
|
|
@ -261,6 +261,8 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
|||
int ret, i, cur, err, pins, clock_id;
|
||||
const u8 *sources;
|
||||
int proto = fmt->protocol;
|
||||
bool readable, writeable;
|
||||
u32 bmControls;
|
||||
|
||||
entity_id &= 0xff;
|
||||
|
||||
|
|
@ -292,11 +294,27 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
|||
sources = GET_VAL(selector, proto, baCSourceID);
|
||||
cur = 0;
|
||||
|
||||
if (proto == UAC_VERSION_3)
|
||||
bmControls = le32_to_cpu(*(__le32 *)(&selector->v3.baCSourceID[0] + pins));
|
||||
else
|
||||
bmControls = *(__u8 *)(&selector->v2.baCSourceID[0] + pins);
|
||||
|
||||
readable = uac_v2v3_control_is_readable(bmControls,
|
||||
UAC2_CX_CLOCK_SELECTOR);
|
||||
writeable = uac_v2v3_control_is_writeable(bmControls,
|
||||
UAC2_CX_CLOCK_SELECTOR);
|
||||
|
||||
if (pins == 1) {
|
||||
ret = 1;
|
||||
goto find_source;
|
||||
}
|
||||
|
||||
/* for now just warn about buggy device */
|
||||
if (!readable)
|
||||
usb_audio_warn(chip,
|
||||
"%s(): clock selector control is not readable, id %d\n",
|
||||
__func__, clock_id);
|
||||
|
||||
/* the entity ID we are looking at is a selector.
|
||||
* find out what it currently selects */
|
||||
ret = uac_clock_selector_get_val(chip, clock_id);
|
||||
|
|
@ -325,17 +343,29 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
|
|||
visited, validate);
|
||||
if (ret > 0) {
|
||||
/* Skip setting clock selector again for some devices */
|
||||
if (chip->quirk_flags & QUIRK_FLAG_SKIP_CLOCK_SELECTOR)
|
||||
if (chip->quirk_flags & QUIRK_FLAG_SKIP_CLOCK_SELECTOR ||
|
||||
!writeable)
|
||||
return ret;
|
||||
err = uac_clock_selector_set_val(chip, entity_id, cur);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
if (pins == 1) {
|
||||
usb_audio_dbg(chip,
|
||||
"%s(): selector returned an error, "
|
||||
"assuming a firmware bug, id %d, ret %d\n",
|
||||
__func__, clock_id, err);
|
||||
return ret;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validate || ret > 0 || !chip->autoclock)
|
||||
return ret;
|
||||
|
||||
find_others:
|
||||
if (!writeable)
|
||||
return -ENXIO;
|
||||
|
||||
/* The current clock source is invalid, try others. */
|
||||
for (i = 1; i <= pins; i++) {
|
||||
if (i == cur)
|
||||
|
|
|
|||
|
|
@ -470,9 +470,11 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
|
|||
int clock)
|
||||
{
|
||||
struct usb_device *dev = chip->dev;
|
||||
struct usb_host_interface *alts;
|
||||
unsigned int *table;
|
||||
unsigned int nr_rates;
|
||||
int i, err;
|
||||
u32 bmControls;
|
||||
|
||||
/* performing the rate verification may lead to unexpected USB bus
|
||||
* behavior afterwards by some unknown reason. Do this only for the
|
||||
|
|
@ -481,6 +483,24 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
|
|||
if (!(chip->quirk_flags & QUIRK_FLAG_VALIDATE_RATES))
|
||||
return 0; /* don't perform the validation as default */
|
||||
|
||||
alts = snd_usb_get_host_interface(chip, fp->iface, fp->altsetting);
|
||||
if (!alts)
|
||||
return 0;
|
||||
|
||||
if (fp->protocol == UAC_VERSION_3) {
|
||||
struct uac3_as_header_descriptor *as = snd_usb_find_csint_desc(
|
||||
alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
|
||||
bmControls = le32_to_cpu(as->bmControls);
|
||||
} else {
|
||||
struct uac2_as_header_descriptor *as = snd_usb_find_csint_desc(
|
||||
alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
|
||||
bmControls = as->bmControls;
|
||||
}
|
||||
|
||||
if (!uac_v2v3_control_is_readable(bmControls,
|
||||
UAC2_AS_VAL_ALT_SETTINGS))
|
||||
return 0;
|
||||
|
||||
table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -1085,7 +1085,7 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
|
|||
}
|
||||
if ((quirk && quirk->type != QUIRK_MIDI_STANDARD_INTERFACE) ||
|
||||
iface->num_altsetting < 2) {
|
||||
usb_audio_info(chip, "Quirk or no altest; falling back to MIDI 1.0\n");
|
||||
usb_audio_info(chip, "Quirk or no altset; falling back to MIDI 1.0\n");
|
||||
goto fallback_to_midi1;
|
||||
}
|
||||
hostif = &iface->altsetting[1];
|
||||
|
|
|
|||
|
|
@ -2031,10 +2031,14 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|||
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_IGNORE_CTL_ERROR),
|
||||
DEVICE_FLG(0x0499, 0x1509, /* Steinberg UR22 */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x0499, 0x3108, /* Yamaha YIT-W12TX */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x04d8, 0xfeea, /* Benchmark DAC1 Pre */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x04e8, 0xa051, /* Samsung USBC Headset (AKG) */
|
||||
QUIRK_FLAG_SKIP_CLOCK_SELECTOR | QUIRK_FLAG_CTL_MSG_DELAY_5M),
|
||||
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
|
||||
QUIRK_FLAG_IFACE_SKIP_CLOSE),
|
||||
DEVICE_FLG(0x054c, 0x0b8c, /* Sony WALKMAN NW-A45 DAC */
|
||||
QUIRK_FLAG_SET_IFACE_FIRST),
|
||||
DEVICE_FLG(0x0556, 0x0014, /* Phoenix Audio TMX320VC */
|
||||
|
|
@ -2073,14 +2077,22 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x0763, 0x2031, /* M-Audio Fast Track C600 */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x07fd, 0x000b, /* MOTU M Series 2nd hardware revision */
|
||||
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
||||
DEVICE_FLG(0x08bb, 0x2702, /* LineX FM Transmitter */
|
||||
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
||||
DEVICE_FLG(0x0951, 0x16ad, /* Kingston HyperX */
|
||||
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
||||
DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */
|
||||
QUIRK_FLAG_CTL_MSG_DELAY_1M),
|
||||
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
DEVICE_FLG(0x0fd9, 0x0008, /* Hauppauge HVR-950Q */
|
||||
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x1395, 0x740a, /* Sennheiser DECT */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x1397, 0x0507, /* Behringer UMC202HD */
|
||||
|
|
@ -2113,6 +2125,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|||
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
|
||||
DEVICE_FLG(0x1901, 0x0191, /* GE B850V3 CP2114 audio interface */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x19f7, 0x0035, /* RODE NT-USB+ */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */
|
||||
QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */
|
||||
|
|
@ -2155,6 +2171,12 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|||
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
||||
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
|
||||
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
||||
DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
|
||||
|
|
@ -2163,22 +2185,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
|||
QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
|
||||
QUIRK_FLAG_ALIGN_TRANSFER),
|
||||
DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
DEVICE_FLG(0x2b53, 0x0023, /* Fiero SC-01 (firmware v1.0.0 @ 48 kHz) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x2b53, 0x0024, /* Fiero SC-01 (firmware v1.0.0 @ 96 kHz) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
|
||||
QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
|
||||
QUIRK_FLAG_IFACE_SKIP_CLOSE),
|
||||
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */
|
||||
QUIRK_FLAG_FIXED_RATE),
|
||||
DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */
|
||||
QUIRK_FLAG_GET_SAMPLE_RATE),
|
||||
|
||||
/* Vendor matches */
|
||||
VENDOR_FLG(0x045e, /* MS Lifecam */
|
||||
|
|
|
|||
|
|
@ -91,8 +91,6 @@ static void virtsnd_event_notify_cb(struct virtqueue *vqueue)
|
|||
virtsnd_event_dispatch(snd, event);
|
||||
virtsnd_event_send(vqueue, event, true, GFP_ATOMIC);
|
||||
}
|
||||
if (unlikely(virtqueue_is_broken(vqueue)))
|
||||
break;
|
||||
} while (!virtqueue_enable_cb(vqueue));
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -303,8 +303,6 @@ void virtsnd_ctl_notify_cb(struct virtqueue *vqueue)
|
|||
virtqueue_disable_cb(vqueue);
|
||||
while ((msg = virtqueue_get_buf(vqueue, &length)))
|
||||
virtsnd_ctl_msg_complete(msg);
|
||||
if (unlikely(virtqueue_is_broken(vqueue)))
|
||||
break;
|
||||
} while (!virtqueue_enable_cb(vqueue));
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,8 +358,6 @@ static inline void virtsnd_pcm_notify_cb(struct virtio_snd_queue *queue)
|
|||
virtqueue_disable_cb(queue->vqueue);
|
||||
while ((msg = virtqueue_get_buf(queue->vqueue, &written_bytes)))
|
||||
virtsnd_pcm_msg_complete(msg, written_bytes);
|
||||
if (unlikely(virtqueue_is_broken(queue->vqueue)))
|
||||
break;
|
||||
} while (!virtqueue_enable_cb(queue->vqueue));
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user