mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
Merge tag 'sound-7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound fixes from Takashi Iwai:
"A collection of device-specific small fixes. At this time, the
majority of changes are about ASoC while we have usual suspects like
HD- and USB-audio quirks. Some highlights below.
ASoC Intel / SoundWire:
- Fix bus and stream resource leaks at error path in avs and hda-ext
- More fixes and refactoring in avs for constraining MSBs, async
handling D0ix
- Add support for TAC5xx2 SoundWire family and NVL MAX98360A RT5682
machines
- Fix uninitialized stream configurations in Realtek SoundWire codecs
- Adjust latency control to fix no-sound issue on RT721-SDCA
ASoC AMD:
- Avoid binding for the acp-da7219-max98357a machine driver
- Add quirks for Acer Nitro AN17-41 and HP 255R G10
- Fix memory leaks in ACP6x
ASoC Codecs & Platforms:
- Fixes for cs35l56 to avoid deadlock, kexec race, and runtime PM
imbalances
- Split stereo streams across mono amps on tas2783-sdw
- Fix pop noise on es8326 and enable_count underflow on es8389
- Various fixes for fsl_micfil, sprd, sti, and publish OF module
aliases
- Fixes & cleanups for Ux500 (MSP/I2S) and AB8500 codecs
HD-audio:
- Fix for channel status notification changes
- Quirks for HP laptops
USB-audio:
- Fix embedded URBs in caiaq, 6fire, hiface, and ua101 drivers
- More hardening in usx2y and us122l drivers
- Quirks for Behringer devices
Misc:
- Add PCI ID for RME HDSPe AIO PCI Express audio card in hdspm
- Fix S/PDIF passthrough on CA20K2 in ctxfi"
* tag 'sound-7.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (65 commits)
ALSA: hdspm: Add a new PCI device ID (1d18:3fc6) for RME HDSPe AIO PCI express audio
ASoC: amd: acp-da7219-max98357a: don't bind on Raven/Picasso boards
ALSA: hda: Report a change when only the channel status bytes move
ALSA: us122l: Prevent write upgrades for read mappings
ALSA: hda/realtek: Add quirk for HP Elite Dragonfly Max G2 speaker
ASoC: cs35l56: Fix race between kexec and snd_soc_register_component()
ASoC: amd: yc: add quirk for Acer Nitro AN17-41 internal mic
ASoC: mt6351: Publish the OF module alias
ASoC: Intel: SST: Publish the PCI module aliases
ASoC: bcm: bcm63xx: Publish the OF module aliases
ALSA: usb-audio: Add quirk flags for Behringer UV1
ALSA: usb-audio: Add boot quirk for Behringer CM1A
ALSA: hda/realtek: Add quirk for HP Omen 16-wd0xxx mute LED
ALSA: usbusx2y: validate URB actual_length in interrupt callback
ALSA: usbusx2y: fix in04_last array size mismatch with in04_buf
ALSA: ctxfi: Fix CA20K2 S/PDIF passthrough
ALSA: usb: 6fire: Avoid embedded URBs
ALSA: usb: hiface: Avoid embedded URBs
ALSA: usb: ua101: Avoid embedded URBs
ALSA: caiaq: Decoupling ep1_in_urb in caiaq dev
...
This commit is contained in:
@@ -7451,6 +7451,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
|
||||
SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
|
||||
SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8890, "HP Elite Dragonfly Max G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
|
||||
SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
|
||||
@@ -7566,6 +7567,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
||||
SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
||||
SND_PCI_QUIRK(0x103c, 0x8ba9, "HP Omen 16-wd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
|
||||
SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX),
|
||||
|
||||
@@ -2277,6 +2277,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
int idx = kcontrol->private_value;
|
||||
struct hda_spdif_out *spdif;
|
||||
hda_nid_t nid;
|
||||
unsigned int old_status;
|
||||
unsigned short val;
|
||||
int change;
|
||||
|
||||
@@ -2285,6 +2286,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
guard(mutex)(&codec->spdif_mutex);
|
||||
spdif = snd_array_elem(&codec->spdif_out, idx);
|
||||
nid = spdif->nid;
|
||||
old_status = spdif->status;
|
||||
spdif->status = ucontrol->value.iec958.status[0] |
|
||||
((unsigned int)ucontrol->value.iec958.status[1] << 8) |
|
||||
((unsigned int)ucontrol->value.iec958.status[2] << 16) |
|
||||
@@ -2295,7 +2297,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
|
||||
spdif->ctls = val;
|
||||
if (change && nid != (u16)-1)
|
||||
set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
|
||||
return change;
|
||||
return change || spdif->status != old_status;
|
||||
}
|
||||
|
||||
#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info
|
||||
|
||||
@@ -90,8 +90,10 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
|
||||
|
||||
for (idx = 0; idx < link_count; idx++) {
|
||||
hlink = kzalloc_obj(*hlink);
|
||||
if (!hlink)
|
||||
if (!hlink) {
|
||||
snd_hdac_ext_link_free_all(bus);
|
||||
return -ENOMEM;
|
||||
}
|
||||
hlink->index = idx;
|
||||
hlink->bus = bus;
|
||||
hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
|
||||
|
||||
@@ -102,8 +102,10 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
|
||||
|
||||
for (i = 0; i < num_stream; i++) {
|
||||
struct hdac_ext_stream *hext_stream = kzalloc_obj(*hext_stream);
|
||||
if (!hext_stream)
|
||||
if (!hext_stream) {
|
||||
snd_hdac_ext_stream_free_all(bus);
|
||||
return -ENOMEM;
|
||||
}
|
||||
tag = ++stream_tag;
|
||||
snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
|
||||
idx++;
|
||||
@@ -111,7 +113,6 @@ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
|
||||
|
||||
|
||||
@@ -994,6 +994,7 @@ static int daio_mgr_dao_init(struct hw *hw, void *blk, unsigned int idx, unsigne
|
||||
/* S/PDIF output */
|
||||
switch ((conf & 0xf)) {
|
||||
case 1:
|
||||
case 9:
|
||||
set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
|
||||
break;
|
||||
case 2:
|
||||
|
||||
@@ -1071,14 +1071,8 @@ struct hdspm {
|
||||
|
||||
|
||||
static const struct pci_device_id snd_hdspm_ids[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_XILINX,
|
||||
.device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.class = 0,
|
||||
.class_mask = 0,
|
||||
.driver_data = 0},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI) },
|
||||
{ PCI_DEVICE(0x1d18, 0x3fc6) }, /* RME HDSPe AIO PCI express audio */
|
||||
{0,}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "acp.h"
|
||||
#include "../codecs/da7219.h"
|
||||
@@ -742,6 +743,18 @@ static const struct regulator_desc acp_da7219_desc = {
|
||||
.n_voltages = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* The ACP3.x+ (Raven/Picasso and later) audio coprocessor is a dedicated PCI
|
||||
* function. Carrizo/Stoney - the only platforms handled by this driver - reach
|
||||
* the ACP through the GPU driver and have no such device.
|
||||
*/
|
||||
#define ACP3X_PCI_DEV_ID 0x15e2
|
||||
|
||||
static const struct pci_device_id acp3x_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP3X_PCI_DEV_ID) },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
static int cz_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@@ -750,6 +763,16 @@ static int cz_probe(struct platform_device *pdev)
|
||||
struct regulator_dev *rdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/*
|
||||
* AMDI5682 is also matched by acp3x-alc5682-max98357 (Raven/Picasso).
|
||||
* If the ACP3.x PCI function is present this is such a board; return
|
||||
* -ENODEV so that driver binds instead.
|
||||
*/
|
||||
if (pci_dev_present(acp3x_pci_ids)) {
|
||||
dev_info(dev, "ACP3.x PCI device present, deferring to acp3x-alc5682-max98357\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
card = (struct snd_soc_card *)acp_soc_is_rltk_max(dev);
|
||||
if (!card)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -104,7 +104,7 @@ static void disable_pdm_interrupts(void __iomem *acp_base)
|
||||
u32 ext_int_ctrl;
|
||||
|
||||
ext_int_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
|
||||
ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
|
||||
rn_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
|
||||
}
|
||||
|
||||
|
||||
@@ -388,6 +388,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro AN17-41"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
@@ -892,6 +899,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "TM2423"),
|
||||
}
|
||||
},
|
||||
{
|
||||
.driver_data = &acp6x_card,
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP 255R 15.6 inch G10 Notebook PC"),
|
||||
}
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -275,9 +275,11 @@ static int acp6x_pdm_dma_close(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
acp6x_disable_pdm_interrupts(adata->acp6x_base);
|
||||
adata->capture_stream = NULL;
|
||||
kfree(runtime->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +285,7 @@ static const struct of_device_id snd_soc_bcm_audio_match[] = {
|
||||
{.compatible = "brcm,bcm63xx-i2s"},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, snd_soc_bcm_audio_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver bcm63xx_i2s_driver = {
|
||||
|
||||
+276
-412
File diff suppressed because it is too large
Load Diff
@@ -386,11 +386,8 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
|
||||
{
|
||||
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
|
||||
|
||||
if (!cs35l56->base.init_done)
|
||||
return 0;
|
||||
|
||||
/* runtime_resume unmasks the interrupt */
|
||||
cs35l56_mask_soundwire_interrupts(cs35l56);
|
||||
if (cs35l56->sdw_attached)
|
||||
cs35l56_mask_soundwire_interrupts(cs35l56);
|
||||
|
||||
return cs35l56_system_suspend(dev);
|
||||
}
|
||||
|
||||
+103
-13
@@ -18,9 +18,11 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -37,6 +39,13 @@
|
||||
#include "wm_adsp.h"
|
||||
#include "cs35l56.h"
|
||||
|
||||
/*
|
||||
* snd_soc_register_component() can call component_probe() on all instances
|
||||
* in a card, so deferred registration must be protected across all instances.
|
||||
*/
|
||||
static DEFINE_MUTEX(cs35l56_component_register_lock);
|
||||
static bool cs35l56_shutting_down;
|
||||
|
||||
void cs35l56_mask_soundwire_interrupts(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
/*
|
||||
@@ -1365,12 +1374,6 @@ static int _cs35l56_component_probe(struct snd_soc_component *component)
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
|
||||
|
||||
if (!wait_for_completion_timeout(&cs35l56->init_completion,
|
||||
msecs_to_jiffies(5000))) {
|
||||
dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cs35l56->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
|
||||
if (!cs35l56->dsp.part)
|
||||
return -ENOMEM;
|
||||
@@ -1939,6 +1942,45 @@ static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l5
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cs35l56_component_register(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_register_component(cs35l56->base.dev,
|
||||
&soc_component_dev_cs35l56,
|
||||
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
|
||||
if (ret < 0) {
|
||||
dev_err(cs35l56->base.dev, "Register codec failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cs35l56->component_registered = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs35l56_component_register_work(struct work_struct *work)
|
||||
{
|
||||
struct cs35l56_private *cs35l56 = container_of(work,
|
||||
struct cs35l56_private,
|
||||
component_register_work);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&cs35l56_component_register_lock);
|
||||
|
||||
if (cs35l56_shutting_down)
|
||||
return;
|
||||
|
||||
PM_RUNTIME_ACQUIRE_AUTOSUSPEND(cs35l56->base.dev, pm_err);
|
||||
ret = PM_RUNTIME_ACQUIRE_ERR(&pm_err);
|
||||
if (ret) {
|
||||
dev_err(cs35l56->base.dev, "register_work failed to get pm_runtime: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
cs35l56_component_register(cs35l56);
|
||||
}
|
||||
|
||||
int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq)
|
||||
{
|
||||
int ret;
|
||||
@@ -1947,6 +1989,7 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq)
|
||||
mutex_init(&cs35l56->base.irq_lock);
|
||||
cs35l56->base.cal_index = -1;
|
||||
cs35l56->speaker_id = -ENOENT;
|
||||
INIT_WORK(&cs35l56->component_register_work, cs35l56_component_register_work);
|
||||
|
||||
dev_set_drvdata(cs35l56->base.dev, cs35l56);
|
||||
|
||||
@@ -2020,12 +2063,17 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56, int irq)
|
||||
if (ret)
|
||||
goto err_remove_wm_adsp;
|
||||
|
||||
ret = snd_soc_register_component(cs35l56->base.dev,
|
||||
&soc_component_dev_cs35l56,
|
||||
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
|
||||
if (ret < 0) {
|
||||
dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
|
||||
goto err_free_irq;
|
||||
/*
|
||||
* Defer calling snd_soc_register_component() on SoundWire to prevent
|
||||
* a deadlock where it calls our component_probe(), which requires the
|
||||
* SoundWire enumeration to complete, but because we are still in probe()
|
||||
* the SoundWire core will not call the update_status() callback. At time
|
||||
* of writing snd_soc_register_component() never returns EPROBE_DEFER.
|
||||
*/
|
||||
if (!cs35l56->sdw_peripheral) {
|
||||
ret = cs35l56_component_register(cs35l56);
|
||||
if (ret < 0)
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2055,6 +2103,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, "SND_SOC_CS35L56_CORE");
|
||||
|
||||
int cs35l56_init(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
bool first_time_init = !cs35l56->base.init_done;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@@ -2131,13 +2180,23 @@ post_soft_reset:
|
||||
cs35l56->base.init_done = true;
|
||||
complete_all(&cs35l56->init_completion);
|
||||
|
||||
if (cs35l56->sdw_peripheral && first_time_init) {
|
||||
/*
|
||||
* Hardware now accessible, queue work to call
|
||||
* snd_soc_register_component().
|
||||
*/
|
||||
queue_work(system_freezable_wq, &cs35l56->component_register_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_init, "SND_SOC_CS35L56_CORE");
|
||||
|
||||
void cs35l56_remove(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
snd_soc_unregister_component(cs35l56->base.dev);
|
||||
cancel_work_sync(&cs35l56->component_register_work);
|
||||
if (cs35l56->component_registered)
|
||||
snd_soc_unregister_component(cs35l56->base.dev);
|
||||
|
||||
cs35l56->base.init_done = false;
|
||||
|
||||
@@ -2172,6 +2231,37 @@ EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static int cs35l56_reboot_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
guard(mutex)(&cs35l56_component_register_lock);
|
||||
cs35l56_shutting_down = true;
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block cs35l56_reboot_notifier = {
|
||||
.notifier_call = cs35l56_reboot_notify,
|
||||
};
|
||||
|
||||
static int __init cs35l56_modinit(void)
|
||||
{
|
||||
/*
|
||||
* Use reboot notifier to prevent race between shutdown and
|
||||
* snd_soc_register_component(). Driver shutdown() callback would
|
||||
* run too late, after device_shutdown() is already walking the
|
||||
* device list that component registration can modify.
|
||||
*/
|
||||
return register_reboot_notifier(&cs35l56_reboot_notifier);
|
||||
}
|
||||
module_init(cs35l56_modinit);
|
||||
|
||||
static void __exit cs35l56_modexit(void)
|
||||
{
|
||||
unregister_reboot_notifier(&cs35l56_reboot_notifier);
|
||||
}
|
||||
module_exit(cs35l56_modexit);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC CS35L56 driver");
|
||||
MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
|
||||
MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
|
||||
|
||||
@@ -32,6 +32,7 @@ struct sdw_slave;
|
||||
struct cs35l56_private {
|
||||
struct wm_adsp dsp; /* must be first member */
|
||||
struct cs35l56_base base;
|
||||
struct work_struct component_register_work;
|
||||
struct work_struct dsp_work;
|
||||
struct workqueue_struct *dsp_wq;
|
||||
struct snd_soc_component *component;
|
||||
@@ -41,6 +42,7 @@ struct cs35l56_private {
|
||||
const char *fallback_fw_suffix;
|
||||
bool soft_resetting;
|
||||
bool sdw_attached;
|
||||
bool component_registered;
|
||||
struct completion init_completion;
|
||||
|
||||
int speaker_id;
|
||||
|
||||
@@ -26,6 +26,7 @@ struct es8326_priv {
|
||||
struct snd_soc_component *component;
|
||||
struct delayed_work jack_detect_work;
|
||||
struct delayed_work button_press_work;
|
||||
struct delayed_work capture_pop_work;
|
||||
struct snd_soc_jack *jack;
|
||||
int irq;
|
||||
/* The lock protects the situation that an irq is generated
|
||||
@@ -628,6 +629,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
|
||||
0x30, 0x00);
|
||||
} else {
|
||||
cancel_delayed_work_sync(&es8326->capture_pop_work);
|
||||
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
|
||||
0x0F, 0x0F);
|
||||
if (es8326->version > ES8326_VERSION_B) {
|
||||
@@ -666,8 +668,9 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
|
||||
regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70);
|
||||
regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00);
|
||||
}
|
||||
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
|
||||
0x0F, 0x00);
|
||||
|
||||
queue_delayed_work(system_dfl_wq, &es8326->capture_pop_work,
|
||||
msecs_to_jiffies(40));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -773,6 +776,15 @@ static void es8326_disable_micbias(struct snd_soc_component *component)
|
||||
snd_soc_dapm_mutex_unlock(dapm);
|
||||
}
|
||||
|
||||
static void es8326_capture_pop_handler(struct work_struct *work)
|
||||
{
|
||||
struct es8326_priv *es8326 =
|
||||
container_of(work, struct es8326_priv, capture_pop_work.work);
|
||||
|
||||
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
|
||||
0x0F, 0x00);
|
||||
}
|
||||
|
||||
/*
|
||||
* For button detection, set the following in soundcard
|
||||
* snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
|
||||
@@ -1140,6 +1152,7 @@ static int es8326_suspend(struct snd_soc_component *component)
|
||||
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
cancel_delayed_work_sync(&es8326->jack_detect_work);
|
||||
cancel_delayed_work_sync(&es8326->capture_pop_work);
|
||||
es8326_disable_micbias(component);
|
||||
es8326->calibrated = false;
|
||||
regmap_write(es8326->regmap, ES8326_CLK_MUX, 0x2d);
|
||||
@@ -1291,6 +1304,8 @@ static int es8326_i2c_probe(struct i2c_client *i2c)
|
||||
es8326_jack_detect_handler);
|
||||
INIT_DELAYED_WORK(&es8326->button_press_work,
|
||||
es8326_jack_button_handler);
|
||||
INIT_DELAYED_WORK(&es8326->capture_pop_work,
|
||||
es8326_capture_pop_handler);
|
||||
/* ES8316 is level-based while ES8326 is edge-based */
|
||||
ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
|
||||
+20
-14
@@ -812,6 +812,23 @@ static int es8389_pcm_hw_free(struct snd_pcm_substream *substream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void es8389_standby(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
|
||||
regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
|
||||
regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
|
||||
usleep_range(70000, 72000);
|
||||
regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
|
||||
regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
|
||||
regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
|
||||
regmap_write(es8389->regmap, ES8389_RESET, 0x3E);
|
||||
regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
|
||||
usleep_range(8000, 8500);
|
||||
regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
|
||||
}
|
||||
|
||||
static int es8389_set_bias_level(struct snd_soc_component *component,
|
||||
enum snd_soc_bias_level level)
|
||||
{
|
||||
@@ -834,18 +851,7 @@ static int es8389_set_bias_level(struct snd_soc_component *component,
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
|
||||
regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
|
||||
regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
|
||||
usleep_range(70000, 72000);
|
||||
regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
|
||||
regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
|
||||
regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
|
||||
regmap_write(es8389->regmap, ES8389_RESET, 0x3E);
|
||||
regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
|
||||
usleep_range(8000, 8500);
|
||||
regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
|
||||
|
||||
es8389_standby(component);
|
||||
clk_disable_unprepare(es8389->mclk);
|
||||
break;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
@@ -1015,7 +1021,7 @@ static int es8389_suspend(struct snd_soc_component *component)
|
||||
{
|
||||
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
|
||||
es8389_standby(component);
|
||||
regcache_cache_only(es8389->regmap, true);
|
||||
regcache_mark_dirty(es8389->regmap);
|
||||
|
||||
@@ -1084,7 +1090,7 @@ static int es8389_probe(struct snd_soc_component *component)
|
||||
|
||||
es8389->hpf_freq = ES8389_HPF_DEFAULT;
|
||||
es8389_init(component);
|
||||
es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
|
||||
es8389_standby(component);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1478,6 +1478,7 @@ static const struct of_device_id mt6351_of_match[] = {
|
||||
{.compatible = "mediatek,mt6351-sound",},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6351_of_match);
|
||||
|
||||
static struct platform_driver mt6351_codec_driver = {
|
||||
.driver = {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/initval.h>
|
||||
#include "rt1318-sdw.h"
|
||||
|
||||
@@ -564,11 +565,10 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1318_sdw_priv *rt1318 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, port, num_channels, ch_mask;
|
||||
int retval, port;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
@@ -582,23 +582,13 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
/* SoundWire specific configuration */
|
||||
/* port 1 for playback */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
port = 1;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
else
|
||||
port = 2;
|
||||
}
|
||||
|
||||
num_channels = params_channels(params);
|
||||
ch_mask = (1 << num_channels) - 1;
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = num_channels;
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
port_config.ch_mask = ch_mask;
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config,
|
||||
|
||||
@@ -3246,7 +3246,7 @@ static int rt1320_sdw_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt1320_sdw_priv *rt1320 =
|
||||
snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
struct sdw_port_config dmic_port_config[2];
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "rt712-sdca.h"
|
||||
|
||||
@@ -1449,11 +1450,10 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, port, num_channels;
|
||||
int retval, port;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s id %d", __func__, dai->name, dai->id);
|
||||
@@ -1471,7 +1471,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
/* SoundWire specific configuration */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (dai->id == RT712_AIF1)
|
||||
port = 1;
|
||||
else if (dai->id == RT712_AIF2)
|
||||
@@ -1479,7 +1478,6 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
if (dai->id == RT712_AIF1)
|
||||
port = 4;
|
||||
else if (dai->id == RT712_AIF3)
|
||||
@@ -1488,13 +1486,8 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = GENMASK(num_channels - 1, 0);
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt712->slave, &stream_config,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "rt721-sdca.h"
|
||||
@@ -206,6 +207,7 @@ static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
|
||||
regmap_write(rt721->regmap,
|
||||
SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
|
||||
RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
|
||||
regmap_write(rt721->regmap, 0x2f5d, 0x1);
|
||||
}
|
||||
|
||||
static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
|
||||
@@ -1268,11 +1270,10 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, port, num_channels;
|
||||
int retval, port;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
@@ -1291,7 +1292,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
* RT721_AIF3 with port = 6 for digital-mic capture
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (dai->id == RT721_AIF1)
|
||||
port = 1;
|
||||
else if (dai->id == RT721_AIF2)
|
||||
@@ -1299,7 +1299,6 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
if (dai->id == RT721_AIF1)
|
||||
port = 2;
|
||||
else if (dai->id == RT721_AIF3)
|
||||
@@ -1307,13 +1306,9 @@ static int rt721_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = GENMASK(num_channels - 1, 0);
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt721->slave, &stream_config,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/sdw.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#include "rt722-sdca.h"
|
||||
@@ -1442,11 +1443,10 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
int retval, port, num_channels;
|
||||
int retval, port;
|
||||
unsigned int sampling_rate;
|
||||
|
||||
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
|
||||
@@ -1465,7 +1465,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
* RT722_AIF3 with port = 6 for digital-mic capture
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (dai->id == RT722_AIF1)
|
||||
port = 1;
|
||||
else if (dai->id == RT722_AIF2)
|
||||
@@ -1473,7 +1472,6 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
if (dai->id == RT722_AIF1)
|
||||
port = 2;
|
||||
else if (dai->id == RT722_AIF3)
|
||||
@@ -1481,13 +1479,9 @@ static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = GENMASK(num_channels - 1, 0);
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt722->slave, &stream_config,
|
||||
|
||||
@@ -936,9 +936,8 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt766_sdca_priv *rt766 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_stream_config stream_config = {0};
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
unsigned int sampling_rate;
|
||||
int retval, port;
|
||||
@@ -957,7 +956,6 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
/* SoundWire specific configuration */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
direction = SDW_DATA_DIR_RX;
|
||||
if (dai->id == RT766_AIF1)
|
||||
port = 3;
|
||||
else if (dai->id == RT766_AIF2)
|
||||
@@ -965,7 +963,6 @@ static int rt766_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
else
|
||||
return -EINVAL;
|
||||
} else {
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
if (dai->id == RT766_AIF1)
|
||||
port = 12;
|
||||
else if (dai->id == RT766_AIF3)
|
||||
|
||||
@@ -1001,6 +1001,31 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream,
|
||||
/* SoundWire specific configuration */
|
||||
snd_sdw_params_to_config(substream, params,
|
||||
&stream_config, &port_config);
|
||||
|
||||
/*
|
||||
* The two mono amps each render one channel of the stereo stream:
|
||||
* snd_sdw_params_to_config() hands every codec the full mask for
|
||||
* playback, which leaves the pair in mirror mode and one channel
|
||||
* unreproduced. Claim a single channel instead, keyed off the
|
||||
* machine-assigned component prefix rather than the SoundWire
|
||||
* address, which is board-specific: soc_sdw_ti_amp.c names the amps
|
||||
* tas2783-1..4.
|
||||
*
|
||||
* Which side an amp then renders does not follow from the bit that
|
||||
* is set - sdw_compute_slave_ports() advances the payload offset by
|
||||
* the popcount of ch_mask and never looks at which bit it is - but
|
||||
* from the amp's position in the codec order of the DAI link, which
|
||||
* on these boards matches the prefix numbering.
|
||||
*/
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||
params_channels(params) == 2 && component->name_prefix) {
|
||||
const char *idx_str = strrchr(component->name_prefix, '-');
|
||||
unsigned long idx;
|
||||
|
||||
if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx)
|
||||
port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1);
|
||||
}
|
||||
|
||||
/* port 1 for playback */
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
port_config.num = 1;
|
||||
|
||||
@@ -953,12 +953,17 @@ static int fsl_micfil_reparent_rootclk(struct fsl_micfil *micfil, unsigned int s
|
||||
/* Get root clock */
|
||||
clk = micfil->mclk;
|
||||
|
||||
/* Disable clock first, for it was enabled by pm_runtime */
|
||||
/* Reparent root clock to the PLL matching this sample rate */
|
||||
fsl_asoc_reparent_pll_clocks(dev, clk, micfil->pll8k_clk,
|
||||
micfil->pll11k_clk, ratio);
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable only once; hw_params can be called multiple times */
|
||||
if (!micfil->mclk_flag) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
micfil->mclk_flag = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -991,8 +996,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
micfil->mclk_flag = true;
|
||||
|
||||
/* floor(K * CLKDIV) */
|
||||
switch (micfil->quality) {
|
||||
case QUALITY_HIGH:
|
||||
@@ -1068,8 +1071,10 @@ static int fsl_micfil_hw_free(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
clk_disable_unprepare(micfil->mclk);
|
||||
micfil->mclk_flag = false;
|
||||
if (micfil->mclk_flag) {
|
||||
clk_disable_unprepare(micfil->mclk);
|
||||
micfil->mclk_flag = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ static const struct pci_device_id intel_sst_ids[] = {
|
||||
{ PCI_DEVICE_DATA(INTEL, SST_TNG, 0) },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_sst_ids);
|
||||
|
||||
static struct pci_driver sst_driver = {
|
||||
.name = SST_DRV_NAME,
|
||||
|
||||
@@ -15,6 +15,22 @@
|
||||
#include "../../../codecs/hda.h"
|
||||
#include "../utils.h"
|
||||
|
||||
static int avs_link_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
|
||||
const struct snd_soc_pcm_stream *stream_info;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
|
||||
codec_dai = snd_soc_rtd_to_codec(rtd, 0);
|
||||
stream_info = snd_soc_dai_get_pcm_stream(codec_dai, substream->stream);
|
||||
|
||||
return snd_pcm_hw_constraint_msbits(substream->runtime, 0, 0, stream_info->sig_bits);
|
||||
}
|
||||
|
||||
static const struct snd_soc_ops avs_link_ops = {
|
||||
.startup = avs_link_startup,
|
||||
};
|
||||
|
||||
static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
|
||||
struct snd_soc_dai_link **links)
|
||||
{
|
||||
@@ -43,6 +59,7 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int
|
||||
dl[i].platforms = platform;
|
||||
dl[i].num_platforms = 1;
|
||||
dl[i].ignore_pmdown_time = 1;
|
||||
dl[i].ops = &avs_link_ops;
|
||||
|
||||
dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
|
||||
dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
|
||||
|
||||
+41
-18
@@ -92,16 +92,28 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
|
||||
{
|
||||
unsigned int cp_streams, pb_streams;
|
||||
unsigned int gcap;
|
||||
int ret;
|
||||
|
||||
gcap = snd_hdac_chip_readw(bus, GCAP);
|
||||
cp_streams = (gcap >> 8) & 0x0F;
|
||||
pb_streams = (gcap >> 12) & 0x0F;
|
||||
bus->num_streams = cp_streams + pb_streams;
|
||||
|
||||
snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
|
||||
snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
ret = snd_hdac_ext_stream_init_all(bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = snd_hdac_ext_stream_init_all(bus, cp_streams, pb_streams, SNDRV_PCM_STREAM_PLAYBACK);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return snd_hdac_bus_alloc_stream_pages(bus);
|
||||
ret = snd_hdac_bus_alloc_stream_pages(bus);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
snd_hdac_ext_stream_free_all(bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
|
||||
@@ -383,6 +395,18 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct
|
||||
struct device *dev = &pci->dev;
|
||||
int ret;
|
||||
|
||||
ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
|
||||
if (!ipc)
|
||||
return -ENOMEM;
|
||||
|
||||
adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
|
||||
if (!adev->modcfg_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = avs_ipc_init(ipc, dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_hdac_ext_bus_init(&bus->core, dev, NULL, &soc_hda_ext_bus_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -394,17 +418,6 @@ static int avs_bus_init(struct avs_dev *adev, struct pci_dev *pci, const struct
|
||||
bus->mixer_assigned = -1;
|
||||
mutex_init(&bus->prepare_mutex);
|
||||
|
||||
ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
|
||||
if (!ipc)
|
||||
return -ENOMEM;
|
||||
ret = avs_ipc_init(ipc, dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adev->modcfg_buf = devm_kzalloc(dev, AVS_MAILBOX_SIZE, GFP_KERNEL);
|
||||
if (!adev->modcfg_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
adev->dev = dev;
|
||||
adev->spec = (const struct avs_spec *)id->driver_data;
|
||||
adev->ipc = ipc;
|
||||
@@ -456,13 +469,14 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
|
||||
|
||||
ret = pcim_request_all_regions(pci, "AVS HDAudio");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_request_regions;
|
||||
|
||||
bus->addr = pci_resource_start(pci, 0);
|
||||
bus->remap_addr = pci_ioremap_bar(pci, 0);
|
||||
if (!bus->remap_addr) {
|
||||
dev_err(bus->dev, "ioremap error\n");
|
||||
return -ENXIO;
|
||||
ret = -ENXIO;
|
||||
goto err_request_regions;
|
||||
}
|
||||
|
||||
adev->dsp_ba = pci_ioremap_bar(pci, 4);
|
||||
@@ -473,8 +487,13 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
|
||||
}
|
||||
|
||||
snd_hdac_bus_parse_capabilities(bus);
|
||||
if (bus->mlcap)
|
||||
snd_hdac_ext_bus_get_ml_capabilities(bus);
|
||||
if (bus->mlcap) {
|
||||
ret = snd_hdac_ext_bus_get_ml_capabilities(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get ml capabilities: %d\n", ret);
|
||||
goto err_ml_cap;
|
||||
}
|
||||
}
|
||||
|
||||
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
|
||||
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
@@ -516,9 +535,13 @@ err_acquire_irq:
|
||||
snd_hdac_bus_free_stream_pages(bus);
|
||||
snd_hdac_ext_stream_free_all(bus);
|
||||
err_init_streams:
|
||||
snd_hdac_ext_link_free_all(bus);
|
||||
err_ml_cap:
|
||||
iounmap(adev->dsp_ba);
|
||||
err_remap_bar4:
|
||||
iounmap(bus->remap_addr);
|
||||
err_request_regions:
|
||||
snd_hdac_ext_bus_exit(bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/string_helpers.h>
|
||||
@@ -236,15 +237,20 @@ static int strace_open(struct inode *inode, struct file *file)
|
||||
if (!try_module_get(adev->dev->driver->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (kfifo_initialized(&adev->trace_fifo))
|
||||
return -EBUSY;
|
||||
if (kfifo_initialized(&adev->trace_fifo)) {
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err;
|
||||
|
||||
file->private_data = adev;
|
||||
return 0;
|
||||
err:
|
||||
module_put(adev->dev->driver->owner);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int strace_release(struct inode *inode, struct file *file)
|
||||
|
||||
@@ -172,7 +172,7 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg
|
||||
|
||||
/* Avoid deadlock as the exception may be the response to SET_D0IX. */
|
||||
if (current_work() != &ipc->d0ix_work.work)
|
||||
cancel_delayed_work_sync(&ipc->d0ix_work);
|
||||
cancel_delayed_work(&ipc->d0ix_work);
|
||||
ipc->in_d0ix = false;
|
||||
/* Re-enabled on recovery completion. */
|
||||
pm_runtime_disable(adev->dev);
|
||||
@@ -395,11 +395,11 @@ static int avs_dsp_do_send_msg(struct avs_dev *adev, struct avs_ipc_msg *request
|
||||
struct avs_ipc *ipc = adev->ipc;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&ipc->msg_mutex);
|
||||
|
||||
if (!ipc->ready)
|
||||
return -EPERM;
|
||||
|
||||
guard(mutex)(&ipc->msg_mutex);
|
||||
|
||||
spin_lock(&ipc->rx_lock);
|
||||
avs_ipc_msg_init(ipc, reply);
|
||||
avs_dsp_send_tx(adev, request, true);
|
||||
|
||||
@@ -836,15 +836,10 @@ static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_mod
|
||||
|
||||
static int avs_path_module_send_init_configs(struct avs_dev *adev, struct avs_path_module *mod)
|
||||
{
|
||||
struct avs_soc_component *acomp;
|
||||
struct avs_tplg_module *template = mod->template;
|
||||
|
||||
acomp = to_avs_soc_component(mod->template->owner->owner->owner->owner->comp);
|
||||
|
||||
u32 num_ids = mod->template->num_config_ids;
|
||||
u32 *ids = mod->template->config_ids;
|
||||
|
||||
for (int i = 0; i < num_ids; i++) {
|
||||
struct avs_tplg_init_config *config = &acomp->tplg->init_configs[ids[i]];
|
||||
for (int i = 0; i < template->num_init_configs; i++) {
|
||||
struct avs_tplg_init_config *config = template->init_configs[i];
|
||||
size_t len = config->length;
|
||||
void *data = config->data;
|
||||
u32 param = config->param;
|
||||
|
||||
+20
-14
@@ -6,6 +6,7 @@
|
||||
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
|
||||
//
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <sound/hda_register.h>
|
||||
@@ -987,13 +988,25 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int avs_request_topology(struct snd_soc_component *component, const char *name,
|
||||
const struct firmware **fw)
|
||||
{
|
||||
char *fullname __free(kfree) = NULL;
|
||||
|
||||
fullname = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, name);
|
||||
if (!fullname)
|
||||
return -ENOMEM;
|
||||
|
||||
return request_firmware(fw, fullname, component->dev);
|
||||
}
|
||||
|
||||
static int avs_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
struct snd_soc_card *card = component->card;
|
||||
struct snd_soc_acpi_mach *mach;
|
||||
struct avs_soc_component *acomp;
|
||||
const struct firmware *fw;
|
||||
struct avs_dev *adev;
|
||||
char *filename;
|
||||
int ret;
|
||||
|
||||
dev_dbg(card->dev, "probing %s card %s\n", component->name, card->name);
|
||||
@@ -1009,13 +1022,7 @@ static int avs_component_probe(struct snd_soc_component *component)
|
||||
goto finalize;
|
||||
|
||||
/* Load specified topology and create debugfs for it. */
|
||||
filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
|
||||
mach->tplg_filename);
|
||||
if (!filename)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = avs_load_topology(component, filename);
|
||||
kfree(filename);
|
||||
ret = avs_request_topology(component, mach->tplg_filename, &fw);
|
||||
if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) {
|
||||
unsigned int vendor_id;
|
||||
|
||||
@@ -1030,18 +1037,17 @@ static int avs_component_probe(struct snd_soc_component *component)
|
||||
"hda-generic-tplg.bin");
|
||||
if (!mach->tplg_filename)
|
||||
return -ENOMEM;
|
||||
filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix,
|
||||
mach->tplg_filename);
|
||||
if (!filename)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename);
|
||||
ret = avs_load_topology(component, filename);
|
||||
kfree(filename);
|
||||
ret = avs_request_topology(component, mach->tplg_filename, &fw);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_tplg_component_load(component, &avs_tplg_ops, fw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = avs_component_load_libraries(acomp);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "libraries loading failed: %d\n", ret);
|
||||
|
||||
@@ -350,6 +350,7 @@ AVS_DEFINE_PTR_PARSER(modcfg_base, struct avs_tplg_modcfg_base, modcfgs_base);
|
||||
AVS_DEFINE_PTR_PARSER(modcfg_ext, struct avs_tplg_modcfg_ext, modcfgs_ext);
|
||||
AVS_DEFINE_PTR_PARSER(pplcfg, struct avs_tplg_pplcfg, pplcfgs);
|
||||
AVS_DEFINE_PTR_PARSER(binding, struct avs_tplg_binding, bindings);
|
||||
AVS_DEFINE_PTR_PARSER(init_config, struct avs_tplg_init_config, init_configs);
|
||||
AVS_DEFINE_PTR_PARSER(nhlt_config, struct avs_tplg_nhlt_config, nhlt_configs);
|
||||
|
||||
static int
|
||||
@@ -1198,7 +1199,7 @@ static const struct avs_tplg_token_parser module_parsers[] = {
|
||||
{
|
||||
.token = AVS_TKN_MOD_INIT_CONFIG_NUM_IDS_U32,
|
||||
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
|
||||
.offset = offsetof(struct avs_tplg_module, num_config_ids),
|
||||
.offset = offsetof(struct avs_tplg_module, num_init_configs),
|
||||
.parse = avs_parse_byte_token,
|
||||
},
|
||||
{
|
||||
@@ -1214,10 +1215,32 @@ static const struct avs_tplg_token_parser init_config_parsers[] = {
|
||||
.token = AVS_TKN_MOD_INIT_CONFIG_ID_U32,
|
||||
.type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
|
||||
.offset = 0,
|
||||
.parse = avs_parse_word_token,
|
||||
.parse = avs_parse_init_config_ptr,
|
||||
},
|
||||
};
|
||||
|
||||
static int avs_tplg_module_init_configs(struct snd_soc_component *comp,
|
||||
struct avs_tplg_module *module,
|
||||
struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
|
||||
{
|
||||
struct avs_tplg_init_config **cfgs;
|
||||
int ret;
|
||||
|
||||
if (!module->num_init_configs)
|
||||
return -EINVAL;
|
||||
|
||||
cfgs = devm_kcalloc(comp->card->dev, module->num_init_configs, sizeof(*cfgs), GFP_KERNEL);
|
||||
if (!cfgs)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = parse_dictionary_entries(comp, tuples, block_size, cfgs, module->num_init_configs,
|
||||
sizeof(*cfgs), AVS_TKN_MOD_INIT_CONFIG_ID_U32,
|
||||
init_config_parsers, ARRAY_SIZE(init_config_parsers));
|
||||
if (!ret)
|
||||
module->init_configs = cfgs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct avs_tplg_module *
|
||||
avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline *owner,
|
||||
struct snd_soc_tplg_vendor_array *tuples, u32 block_size)
|
||||
@@ -1244,27 +1267,11 @@ avs_tplg_module_create(struct snd_soc_component *comp, struct avs_tplg_pipeline
|
||||
block_size -= esize;
|
||||
/* Parse trailing config ids if any. */
|
||||
if (block_size) {
|
||||
u32 num_config_ids = module->num_config_ids;
|
||||
u32 *config_ids;
|
||||
|
||||
if (!num_config_ids)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
config_ids = devm_kcalloc(comp->card->dev, num_config_ids, sizeof(*config_ids),
|
||||
GFP_KERNEL);
|
||||
if (!config_ids)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tuples = avs_tplg_vendor_array_at(tuples, esize);
|
||||
ret = parse_dictionary_entries(comp, tuples, block_size,
|
||||
config_ids, num_config_ids, sizeof(*config_ids),
|
||||
AVS_TKN_MOD_INIT_CONFIG_ID_U32,
|
||||
init_config_parsers,
|
||||
ARRAY_SIZE(init_config_parsers));
|
||||
|
||||
ret = avs_tplg_module_init_configs(comp, module, tuples, block_size);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
module->config_ids = config_ids;
|
||||
}
|
||||
|
||||
module->owner = owner;
|
||||
@@ -2194,7 +2201,7 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_tplg_ops avs_tplg_ops = {
|
||||
const struct snd_soc_tplg_ops avs_tplg_ops = {
|
||||
.io_ops = avs_control_ops,
|
||||
.io_ops_count = ARRAY_SIZE(avs_control_ops),
|
||||
.control_load = avs_control_load,
|
||||
|
||||
@@ -221,8 +221,8 @@ struct avs_tplg_module {
|
||||
u8 domain;
|
||||
struct avs_tplg_modcfg_ext *cfg_ext;
|
||||
u32 ctl_id;
|
||||
u32 num_config_ids;
|
||||
u32 *config_ids;
|
||||
u32 num_init_configs;
|
||||
struct avs_tplg_init_config **init_configs;
|
||||
struct avs_tplg_nhlt_config *nhlt_config;
|
||||
|
||||
struct avs_tplg_pipeline *owner;
|
||||
@@ -230,6 +230,7 @@ struct avs_tplg_module {
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
extern const struct snd_soc_tplg_ops avs_tplg_ops;
|
||||
struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp);
|
||||
|
||||
int avs_load_topology(struct snd_soc_component *comp, const char *filename);
|
||||
|
||||
@@ -533,12 +533,14 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
|
||||
select SND_SOC_CS35L56_SPI
|
||||
select SND_SOC_CS35L56_SDW
|
||||
select SND_SOC_ES9356
|
||||
imply SND_SOC_TAC5XX2_SDW
|
||||
select SND_SOC_DMIC
|
||||
select SND_SOC_INTEL_HDA_DSP_COMMON
|
||||
imply SND_SOC_SDW_MOCKUP
|
||||
help
|
||||
Add support for Intel SoundWire-based platforms connected to
|
||||
MAX98373, RT700, RT711, RT1308 and RT715
|
||||
MAX98373, RT700, RT711, RT1308, RT715, TAC5XX2_SDW family (including
|
||||
TAC5572, TAC5682).
|
||||
If unsure select "N".
|
||||
|
||||
endif
|
||||
|
||||
@@ -908,6 +908,14 @@ static const struct platform_device_id board_ids[] = {
|
||||
SOF_SSP_PORT_BT_OFFLOAD(2) |
|
||||
SOF_BT_OFFLOAD_PRESENT),
|
||||
},
|
||||
{
|
||||
.name = "nvl_rt5682_def",
|
||||
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
|
||||
SOF_SSP_PORT_CODEC(0) |
|
||||
SOF_SSP_PORT_AMP(1) |
|
||||
SOF_SSP_PORT_BT_OFFLOAD(2) |
|
||||
SOF_BT_OFFLOAD_PRESENT),
|
||||
},
|
||||
{
|
||||
.name = "ptl_rt5682_c1_h02",
|
||||
.driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
|
||||
|
||||
@@ -51,10 +51,10 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = {
|
||||
},
|
||||
{
|
||||
.comp_ids = &nvl_rt5682_rt5682s_hp,
|
||||
.drv_name = "sof_rt5682",
|
||||
.sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */
|
||||
.tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
|
||||
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB,
|
||||
.drv_name = "nvl_rt5682_def",
|
||||
.sof_tplg_filename = "sof-nvl", /* the tplg suffix is added at run time */
|
||||
.tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
|
||||
SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
|
||||
},
|
||||
/* place amp/hdmi-in only boards in the end of table */
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
/* Default values if userspace does not set */
|
||||
#define SPRD_COMPR_MIN_FRAGMENT_SIZE SZ_8K
|
||||
#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_128K
|
||||
#define SPRD_COMPR_MAX_FRAGMENT_SIZE SZ_32K
|
||||
#define SPRD_COMPR_MIN_NUM_FRAGMENTS 4
|
||||
#define SPRD_COMPR_MAX_NUM_FRAGMENTS 64
|
||||
|
||||
@@ -271,6 +271,19 @@ static int sprd_platform_compr_set_params(struct snd_soc_component *component,
|
||||
struct sprd_compr_params compr_params = { };
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The stage 0 IRAM buffer and the stage 1 DDR buffer are allocated
|
||||
* with fixed sizes at open time, so the requested fragment size and
|
||||
* fragments must fit into them, otherwise sprd_platform_compr_copy()
|
||||
* would overflow the buffers. Note the compress core only checks the
|
||||
* fragment size and fragments against an u32 overflow, not against
|
||||
* the buffer sizes advertised by get_caps.
|
||||
*/
|
||||
if (params->buffer.fragment_size > SPRD_COMPR_IRAM_BUF_SIZE ||
|
||||
(u64)params->buffer.fragment_size * params->buffer.fragments >
|
||||
SPRD_COMPR_AREA_BUF_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Configure the DMA engine 2-stage transfer mode. Channel 1 set as the
|
||||
* destination channel, and channel 0 set as the source channel, that
|
||||
|
||||
@@ -416,6 +416,8 @@ int uni_reader_init(struct platform_device *pdev,
|
||||
else
|
||||
reader->hw = &uni_reader_pcm_hw;
|
||||
|
||||
spin_lock_init(&reader->irq_lock);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, reader->irq,
|
||||
uni_reader_irq_handler, IRQF_SHARED,
|
||||
dev_name(&pdev->dev), reader);
|
||||
@@ -424,8 +426,6 @@ int uni_reader_init(struct platform_device *pdev,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
spin_lock_init(&reader->irq_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(uni_reader_init);
|
||||
|
||||
+79
-110
@@ -14,7 +14,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mfd/db8500-prcmu.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
@@ -34,8 +34,10 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,
|
||||
if (drvdata->slots > 1) {
|
||||
msp_config->multichannel_configured = 1;
|
||||
|
||||
multi->tx_multichannel_enable = true;
|
||||
multi->rx_multichannel_enable = true;
|
||||
multi->tx_multichannel_enable =
|
||||
msp_config->direction & MSP_DIR_TX;
|
||||
multi->rx_multichannel_enable =
|
||||
msp_config->direction & MSP_DIR_RX;
|
||||
multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED;
|
||||
|
||||
multi->tx_channel_0_enable = drvdata->tx_mask;
|
||||
@@ -57,72 +59,21 @@ static int setup_pcm_multichan(struct snd_soc_dai *dai,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate,
|
||||
struct msp_protdesc *prot_desc)
|
||||
static void setup_frameper(struct snd_soc_dai *dai,
|
||||
struct msp_protdesc *prot_desc)
|
||||
{
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
|
||||
switch (drvdata->slots) {
|
||||
case 1:
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
prot_desc->frame_period =
|
||||
FRAME_PER_SINGLE_SLOT_8_KHZ;
|
||||
break;
|
||||
|
||||
case 16000:
|
||||
prot_desc->frame_period =
|
||||
FRAME_PER_SINGLE_SLOT_16_KHZ;
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
prot_desc->frame_period =
|
||||
FRAME_PER_SINGLE_SLOT_44_1_KHZ;
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
prot_desc->frame_period =
|
||||
FRAME_PER_SINGLE_SLOT_48_KHZ;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported sample-rate (freq = %d)!\n",
|
||||
__func__, rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
prot_desc->frame_period = FRAME_PER_2_SLOTS;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
prot_desc->frame_period = FRAME_PER_8_SLOTS;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
prot_desc->frame_period = FRAME_PER_16_SLOTS;
|
||||
break;
|
||||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported slot-count (slots = %d)!\n",
|
||||
__func__, drvdata->slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prot_desc->clocks_per_frame =
|
||||
prot_desc->frame_period+1;
|
||||
prot_desc->clocks_per_frame = drvdata->slots * drvdata->slot_width;
|
||||
prot_desc->frame_period = prot_desc->clocks_per_frame - 1;
|
||||
|
||||
dev_dbg(dai->dev, "%s: Clocks per frame: %u\n",
|
||||
__func__,
|
||||
prot_desc->clocks_per_frame);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
|
||||
struct msp_protdesc *prot_desc)
|
||||
static int setup_pcm_framing(struct snd_soc_dai *dai,
|
||||
struct msp_protdesc *prot_desc)
|
||||
{
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
|
||||
@@ -163,7 +114,9 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
|
||||
prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16;
|
||||
prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16;
|
||||
|
||||
return setup_frameper(dai, rate, prot_desc);
|
||||
setup_frameper(dai, prot_desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_clocking(struct snd_soc_dai *dai,
|
||||
@@ -177,7 +130,16 @@ static int setup_clocking(struct snd_soc_dai *dai,
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
|
||||
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
msp_config->bclk_inverted = true;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
msp_config->bclk_inverted = true;
|
||||
msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT;
|
||||
msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -192,6 +154,7 @@ static int setup_clocking(struct snd_soc_dai *dai,
|
||||
case SND_SOC_DAIFMT_BC_FC:
|
||||
dev_dbg(dai->dev, "%s: Codec is master.\n", __func__);
|
||||
|
||||
msp_config->clock_provider = false;
|
||||
msp_config->iodelay = 0x20;
|
||||
msp_config->rx_fsync_sel = 0;
|
||||
msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT;
|
||||
@@ -204,6 +167,7 @@ static int setup_clocking(struct snd_soc_dai *dai,
|
||||
case SND_SOC_DAIFMT_BP_FP:
|
||||
dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__);
|
||||
|
||||
msp_config->clock_provider = true;
|
||||
msp_config->tx_clk_sel = TX_CLK_SEL_SRG;
|
||||
msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG;
|
||||
msp_config->rx_clk_sel = RX_CLK_SEL_SRG;
|
||||
@@ -362,7 +326,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = setup_pcm_framing(dai, runtime->rate, prot_desc);
|
||||
ret = setup_pcm_framing(dai, prot_desc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -424,21 +388,21 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
int ret;
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
|
||||
unsigned int configured = is_playback ? PLAYBACK_CONFIGURED :
|
||||
CAPTURE_CONFIGURED;
|
||||
unsigned int dir = is_playback ? MSP_DIR_TX : MSP_DIR_RX;
|
||||
|
||||
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id,
|
||||
snd_pcm_stream_str(substream));
|
||||
|
||||
if (drvdata->vape_opp_constraint == 1) {
|
||||
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
|
||||
"ux500_msp_i2s", 50);
|
||||
drvdata->vape_opp_constraint = 0;
|
||||
}
|
||||
|
||||
if (ux500_msp_i2s_close(drvdata->msp,
|
||||
is_playback ? MSP_DIR_TX : MSP_DIR_RX)) {
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: MSP %d (%s): Unable to close i2s.\n",
|
||||
__func__, dai->id, snd_pcm_stream_str(substream));
|
||||
if (drvdata->configured & configured) {
|
||||
if (ux500_msp_i2s_close(drvdata->msp, dir)) {
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: MSP %d (%s): Unable to close i2s.\n",
|
||||
__func__, dai->id,
|
||||
snd_pcm_stream_str(substream));
|
||||
}
|
||||
drvdata->configured &= ~configured;
|
||||
}
|
||||
|
||||
/* Disable and unprepare clocks */
|
||||
@@ -456,15 +420,23 @@ static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream,
|
||||
static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ux500_msp_config msp_config;
|
||||
bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned int configured = is_playback ? PLAYBACK_CONFIGURED :
|
||||
CAPTURE_CONFIGURED;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__,
|
||||
dai->id, snd_pcm_stream_str(substream), runtime->rate);
|
||||
|
||||
setup_msp_config(substream, dai, &msp_config);
|
||||
if (drvdata->configured & configured)
|
||||
return 0;
|
||||
|
||||
ret = setup_msp_config(substream, dai, &msp_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ux500_msp_i2s_open(drvdata->msp, &msp_config);
|
||||
if (ret < 0) {
|
||||
@@ -473,22 +445,9 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set OPP-level */
|
||||
if ((drvdata->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) &&
|
||||
(drvdata->msp->f_bitclk > 19200000)) {
|
||||
/* If the bit-clock is higher than 19.2MHz, Vape should be
|
||||
* run in 100% OPP. Only when bit-clock is used (MSP master)
|
||||
*/
|
||||
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
|
||||
"ux500-msp-i2s", 100);
|
||||
drvdata->vape_opp_constraint = 1;
|
||||
} else {
|
||||
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
|
||||
"ux500-msp-i2s", 50);
|
||||
drvdata->vape_opp_constraint = 0;
|
||||
}
|
||||
drvdata->configured |= configured;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
@@ -496,7 +455,6 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
unsigned int mask, slots_active;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
|
||||
|
||||
dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n",
|
||||
@@ -504,9 +462,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
1, 2);
|
||||
if (params_channels(params) < 1 || params_channels(params) > 2)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
case SND_SOC_DAIFMT_DSP_B:
|
||||
@@ -518,9 +475,8 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
slots_active = hweight32(mask);
|
||||
dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
|
||||
|
||||
snd_pcm_hw_constraint_single(runtime,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
slots_active);
|
||||
if (!slots_active || params_channels(params) != slots_active)
|
||||
return -EINVAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -553,20 +509,21 @@ static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai,
|
||||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n",
|
||||
__func__, drvdata->fmt);
|
||||
__func__, fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dai->dev,
|
||||
"%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
|
||||
__func__, drvdata->fmt);
|
||||
__func__, fmt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -600,17 +557,23 @@ static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai,
|
||||
__func__, slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->slots = slots;
|
||||
|
||||
if (!(slot_width == 16)) {
|
||||
if (slot_width != 16) {
|
||||
dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n",
|
||||
__func__, slot_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->slot_width = slot_width;
|
||||
|
||||
drvdata->tx_mask = tx_mask & cap;
|
||||
drvdata->rx_mask = rx_mask & cap;
|
||||
if ((tx_mask | rx_mask) & ~cap) {
|
||||
dev_err(dai->dev, "%s: Slot mask exceeds %d slots\n",
|
||||
__func__, slots);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drvdata->slots = slots;
|
||||
drvdata->slot_width = slot_width;
|
||||
drvdata->tx_mask = tx_mask;
|
||||
drvdata->rx_mask = rx_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -716,6 +679,7 @@ static const struct snd_soc_component_driver ux500_msp_component = {
|
||||
static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ux500_msp_i2s_drvdata *drvdata;
|
||||
struct reset_control *reset;
|
||||
int ret = 0;
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev,
|
||||
@@ -729,7 +693,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||
drvdata->tx_mask = 0x01;
|
||||
drvdata->rx_mask = 0x01;
|
||||
drvdata->slot_width = 16;
|
||||
drvdata->master_clk = MSP_INPUT_FREQ_APB;
|
||||
|
||||
drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape");
|
||||
if (IS_ERR(drvdata->reg_vape)) {
|
||||
@@ -739,8 +702,6 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50);
|
||||
|
||||
drvdata->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
|
||||
if (IS_ERR(drvdata->pclk)) {
|
||||
ret = PTR_ERR(drvdata->pclk);
|
||||
@@ -758,9 +719,19 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
drvdata->master_clk = clk_get_rate(drvdata->clk);
|
||||
if (!drvdata->master_clk) {
|
||||
dev_err(&pdev->dev, "MSP clock has no rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reset = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
|
||||
if (IS_ERR(reset))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(reset),
|
||||
"Failed to deassert MSP reset\n");
|
||||
|
||||
ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp);
|
||||
if (!drvdata->msp) {
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: ERROR: Failed to init MSP-struct (%d)!",
|
||||
__func__, ret);
|
||||
@@ -799,8 +770,6 @@ static void ux500_msp_drv_remove(struct platform_device *pdev)
|
||||
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s");
|
||||
|
||||
ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,17 +22,6 @@
|
||||
|
||||
#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
||||
|
||||
#define FRAME_PER_SINGLE_SLOT_8_KHZ 31
|
||||
#define FRAME_PER_SINGLE_SLOT_16_KHZ 124
|
||||
#define FRAME_PER_SINGLE_SLOT_44_1_KHZ 63
|
||||
#define FRAME_PER_SINGLE_SLOT_48_KHZ 49
|
||||
#define FRAME_PER_2_SLOTS 31
|
||||
#define FRAME_PER_8_SLOTS 138
|
||||
#define FRAME_PER_16_SLOTS 277
|
||||
|
||||
#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000
|
||||
#define UX500_MSP1_INTERNAL_CLOCK_FREQ UX500_MSP_INTERNAL_CLOCK_FREQ
|
||||
|
||||
#define UX500_MSP_MIN_CHANNELS 1
|
||||
#define UX500_MSP_MAX_CHANNELS 8
|
||||
|
||||
@@ -47,6 +36,7 @@ struct ux500_msp_i2s_drvdata {
|
||||
struct ux500_msp *msp;
|
||||
struct regulator *reg_vape;
|
||||
unsigned int fmt;
|
||||
unsigned int configured;
|
||||
unsigned int tx_mask;
|
||||
unsigned int rx_mask;
|
||||
int slots;
|
||||
@@ -57,8 +47,6 @@ struct ux500_msp_i2s_drvdata {
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
|
||||
/* Regulators */
|
||||
int vape_opp_constraint;
|
||||
};
|
||||
|
||||
int ux500_msp_dai_set_data_delay(struct snd_soc_dai *dai, int delay);
|
||||
|
||||
+179
-81
@@ -201,10 +201,12 @@ static int configure_protocol(struct ux500_msp *msp,
|
||||
|
||||
/* The code below should not be separated. */
|
||||
temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING;
|
||||
temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol);
|
||||
temp_reg |= MSP_TX_CLKPOL_BIT(!protdesc->tx_clk_pol ^
|
||||
config->bclk_inverted);
|
||||
writel(temp_reg, msp->registers + MSP_GCR);
|
||||
temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING;
|
||||
temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol);
|
||||
temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol ^
|
||||
config->bclk_inverted);
|
||||
writel(temp_reg, msp->registers + MSP_GCR);
|
||||
|
||||
return 0;
|
||||
@@ -212,35 +214,20 @@ static int configure_protocol(struct ux500_msp *msp,
|
||||
|
||||
static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
|
||||
{
|
||||
struct msp_protdesc *protdesc;
|
||||
u64 desired_bitclk;
|
||||
unsigned int bitclk;
|
||||
u32 reg_val_GCR;
|
||||
u32 frame_per = 0;
|
||||
u32 sck_div = 0;
|
||||
u32 frame_width = 0;
|
||||
u32 temp_reg = 0;
|
||||
struct msp_protdesc *protdesc = NULL;
|
||||
u32 sck_div;
|
||||
u32 temp_reg;
|
||||
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR);
|
||||
|
||||
if (config->default_protdesc)
|
||||
protdesc =
|
||||
(struct msp_protdesc *)&prot_descs[config->protocol];
|
||||
else
|
||||
protdesc = (struct msp_protdesc *)&config->protdesc;
|
||||
|
||||
switch (config->protocol) {
|
||||
case MSP_PCM_PROTOCOL:
|
||||
case MSP_PCM_COMPAND_PROTOCOL:
|
||||
frame_width = protdesc->frame_width;
|
||||
sck_div = config->f_inputclk / (config->frame_freq *
|
||||
(protdesc->clocks_per_frame));
|
||||
frame_per = protdesc->frame_period;
|
||||
break;
|
||||
case MSP_I2S_PROTOCOL:
|
||||
frame_width = protdesc->frame_width;
|
||||
sck_div = config->f_inputclk / (config->frame_freq *
|
||||
(protdesc->clocks_per_frame));
|
||||
frame_per = protdesc->frame_period;
|
||||
break;
|
||||
default:
|
||||
dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n",
|
||||
@@ -249,12 +236,35 @@ static int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->default_protdesc)
|
||||
protdesc = (struct msp_protdesc *)&prot_descs[config->protocol];
|
||||
else
|
||||
protdesc = &config->protdesc;
|
||||
|
||||
if (!config->frame_freq || !protdesc->clocks_per_frame)
|
||||
return -EINVAL;
|
||||
|
||||
desired_bitclk = (u64)config->frame_freq * protdesc->clocks_per_frame;
|
||||
if (desired_bitclk > config->f_inputclk)
|
||||
return -EINVAL;
|
||||
bitclk = desired_bitclk;
|
||||
if (config->f_inputclk % bitclk) {
|
||||
dev_err(msp->dev,
|
||||
"Input clock %u cannot generate bit clock %u\n",
|
||||
config->f_inputclk, bitclk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sck_div = config->f_inputclk / bitclk;
|
||||
if (!sck_div || sck_div > SCK_DIV_MASK + 1)
|
||||
return -EINVAL;
|
||||
|
||||
temp_reg = (sck_div - 1) & SCK_DIV_MASK;
|
||||
temp_reg |= FRAME_WIDTH_BITS(frame_width);
|
||||
temp_reg |= FRAME_PERIOD_BITS(frame_per);
|
||||
temp_reg |= FRAME_WIDTH_BITS(protdesc->frame_width);
|
||||
temp_reg |= FRAME_PERIOD_BITS(protdesc->frame_period);
|
||||
writel(temp_reg, msp->registers + MSP_SRG);
|
||||
|
||||
msp->f_bitclk = (config->f_inputclk)/(sck_div + 1);
|
||||
msp->f_bitclk = config->f_inputclk / sck_div;
|
||||
|
||||
/* Enable bit-clock */
|
||||
udelay(100);
|
||||
@@ -344,20 +354,27 @@ static int configure_multichannel(struct ux500_msp *msp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
|
||||
static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config,
|
||||
bool first)
|
||||
{
|
||||
int status = 0;
|
||||
u32 reg_val_DMACR, reg_val_GCR;
|
||||
int status;
|
||||
u32 reg_val_DMACR;
|
||||
|
||||
/* Configure msp with protocol dependent settings */
|
||||
configure_protocol(msp, config);
|
||||
setup_bitclk(msp, config);
|
||||
status = configure_protocol(msp, config);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (first && config->clock_provider) {
|
||||
status = setup_bitclk(msp, config);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (config->multichannel_configured == 1) {
|
||||
status = configure_multichannel(msp, config);
|
||||
if (status)
|
||||
dev_warn(msp->dev,
|
||||
"%s: WARN: configure_multichannel failed (%d)!\n",
|
||||
__func__, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
reg_val_DMACR = readl(msp->registers + MSP_DMACR);
|
||||
@@ -369,11 +386,7 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
|
||||
|
||||
writel(config->iodelay, msp->registers + MSP_IODLY);
|
||||
|
||||
/* Enable frame generation logic */
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR);
|
||||
|
||||
return status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flush_fifo_rx(struct ux500_msp *msp)
|
||||
@@ -411,12 +424,37 @@ static void flush_fifo_tx(struct ux500_msp *msp)
|
||||
writel(reg_val_GCR, msp->registers + MSP_GCR);
|
||||
}
|
||||
|
||||
static bool ux500_msp_config_compatible(struct ux500_msp *msp,
|
||||
struct ux500_msp_config *config)
|
||||
{
|
||||
struct ux500_msp_config *active = &msp->config;
|
||||
|
||||
return active->f_inputclk == config->f_inputclk &&
|
||||
active->tx_clk_sel == config->tx_clk_sel &&
|
||||
active->rx_clk_sel == config->rx_clk_sel &&
|
||||
active->srg_clk_sel == config->srg_clk_sel &&
|
||||
active->rx_fsync_pol == config->rx_fsync_pol &&
|
||||
active->tx_fsync_pol == config->tx_fsync_pol &&
|
||||
active->rx_fsync_sel == config->rx_fsync_sel &&
|
||||
active->tx_fsync_sel == config->tx_fsync_sel &&
|
||||
active->default_protdesc == config->default_protdesc &&
|
||||
active->protocol == config->protocol &&
|
||||
active->frame_freq == config->frame_freq &&
|
||||
active->data_size == config->data_size &&
|
||||
active->def_elem_len == config->def_elem_len &&
|
||||
active->clock_provider == config->clock_provider &&
|
||||
active->bclk_inverted == config->bclk_inverted &&
|
||||
!memcmp(&active->protdesc, &config->protdesc,
|
||||
sizeof(active->protdesc));
|
||||
}
|
||||
|
||||
int ux500_msp_i2s_open(struct ux500_msp *msp,
|
||||
struct ux500_msp_config *config)
|
||||
{
|
||||
u32 old_reg, new_reg, mask;
|
||||
int res;
|
||||
unsigned int tx_sel, rx_sel, tx_busy, rx_busy;
|
||||
bool first;
|
||||
|
||||
if (in_interrupt()) {
|
||||
dev_err(msp->dev,
|
||||
@@ -444,40 +482,68 @@ int ux500_msp_i2s_open(struct ux500_msp *msp,
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0);
|
||||
first = !msp->dir_busy;
|
||||
if (!first && !ux500_msp_config_compatible(msp, config)) {
|
||||
dev_err(msp->dev, "%s: Incompatible duplex configuration\n",
|
||||
__func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* First do the global config register */
|
||||
mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
|
||||
TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
|
||||
RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK |
|
||||
LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
|
||||
if (first) {
|
||||
/* First do the global config register */
|
||||
mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK |
|
||||
TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK |
|
||||
RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK |
|
||||
SRG_CLK_SEL_MASK | LOOPBACK_MASK | TX_EXTRA_DELAY_MASK;
|
||||
|
||||
new_reg = (config->tx_clk_sel | config->rx_clk_sel |
|
||||
config->rx_fsync_pol | config->tx_fsync_pol |
|
||||
config->rx_fsync_sel | config->tx_fsync_sel |
|
||||
config->rx_fifo_config | config->tx_fifo_config |
|
||||
config->srg_clk_sel | config->loopback_enable |
|
||||
config->tx_data_enable);
|
||||
new_reg = config->tx_clk_sel | config->rx_clk_sel |
|
||||
config->rx_fsync_pol | config->tx_fsync_pol |
|
||||
config->rx_fsync_sel | config->tx_fsync_sel |
|
||||
config->rx_fifo_config | config->tx_fifo_config |
|
||||
config->srg_clk_sel | config->loopback_enable |
|
||||
config->tx_data_enable;
|
||||
|
||||
old_reg = readl(msp->registers + MSP_GCR);
|
||||
old_reg &= ~mask;
|
||||
new_reg |= old_reg;
|
||||
writel(new_reg, msp->registers + MSP_GCR);
|
||||
old_reg = readl(msp->registers + MSP_GCR);
|
||||
old_reg &= ~mask;
|
||||
new_reg |= old_reg;
|
||||
writel(new_reg, msp->registers + MSP_GCR);
|
||||
writel(MSP_WMRK_TX_4_ELEMENTS | MSP_WMRK_RX_4_ELEMENTS,
|
||||
msp->registers + MSP_WMRK);
|
||||
}
|
||||
|
||||
res = enable_msp(msp, config);
|
||||
res = enable_msp(msp, config, first);
|
||||
if (res < 0) {
|
||||
dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n",
|
||||
__func__, res);
|
||||
return -EBUSY;
|
||||
if (tx_sel)
|
||||
writel(0, msp->registers + MSP_TCF);
|
||||
if (rx_sel)
|
||||
writel(0, msp->registers + MSP_RCF);
|
||||
if (first) {
|
||||
writel(0, msp->registers + MSP_GCR);
|
||||
writel(0, msp->registers + MSP_DMACR);
|
||||
writel(0, msp->registers + MSP_SRG);
|
||||
writel(0, msp->registers + MSP_MCR);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
msp->dir_busy |= config->direction;
|
||||
if (first) {
|
||||
msp->config = *config;
|
||||
msp->clock_provider = config->clock_provider;
|
||||
}
|
||||
if (config->loopback_enable & 0x80)
|
||||
msp->loopback_enable = 1;
|
||||
|
||||
/* Flush FIFOs */
|
||||
flush_fifo_tx(msp);
|
||||
flush_fifo_rx(msp);
|
||||
if (tx_sel)
|
||||
flush_fifo_tx(msp);
|
||||
if (rx_sel)
|
||||
flush_fifo_rx(msp);
|
||||
|
||||
msp->msp_state = MSP_STATE_CONFIGURED;
|
||||
if (!msp->dir_running)
|
||||
msp->msp_state = MSP_STATE_CONFIGURED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -494,7 +560,6 @@ static void disable_msp_rx(struct ux500_msp *msp)
|
||||
~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT),
|
||||
msp->registers + MSP_IMSC);
|
||||
|
||||
msp->dir_busy &= ~MSP_DIR_RX;
|
||||
}
|
||||
|
||||
static void disable_msp_tx(struct ux500_msp *msp)
|
||||
@@ -510,7 +575,6 @@ static void disable_msp_tx(struct ux500_msp *msp)
|
||||
~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT),
|
||||
msp->registers + MSP_IMSC);
|
||||
|
||||
msp->dir_busy &= ~MSP_DIR_TX;
|
||||
}
|
||||
|
||||
static int disable_msp(struct ux500_msp *msp, unsigned int dir)
|
||||
@@ -520,7 +584,7 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir)
|
||||
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
disable_tx = dir & MSP_DIR_TX;
|
||||
disable_rx = dir & MSP_DIR_TX;
|
||||
disable_rx = dir & MSP_DIR_RX;
|
||||
if (disable_tx && disable_rx) {
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
writel(reg_val_GCR | LOOPBACK_MASK,
|
||||
@@ -553,7 +617,15 @@ static int disable_msp(struct ux500_msp *msp, unsigned int dir)
|
||||
|
||||
int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
|
||||
{
|
||||
u32 reg_val_GCR, enable_bit;
|
||||
u32 reg_val_DMACR, reg_val_GCR, dma_enable_bit, enable_bit;
|
||||
unsigned int dir;
|
||||
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dir = MSP_DIR_TX;
|
||||
else if (direction == SNDRV_PCM_STREAM_CAPTURE)
|
||||
dir = MSP_DIR_RX;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (msp->msp_state == MSP_STATE_IDLE) {
|
||||
dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n",
|
||||
@@ -565,21 +637,44 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
enable_bit = TX_ENABLE;
|
||||
else
|
||||
dma_enable_bit = TX_DMA_ENABLE;
|
||||
} else {
|
||||
enable_bit = RX_ENABLE;
|
||||
dma_enable_bit = RX_DMA_ENABLE;
|
||||
}
|
||||
if (!(msp->dir_busy & dir))
|
||||
return -EINVAL;
|
||||
reg_val_DMACR = readl(msp->registers + MSP_DMACR);
|
||||
writel(reg_val_DMACR | dma_enable_bit,
|
||||
msp->registers + MSP_DMACR);
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
if (msp->clock_provider)
|
||||
enable_bit |= FRAME_GEN_ENABLE;
|
||||
writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR);
|
||||
msp->dir_running |= dir;
|
||||
msp->msp_state = MSP_STATE_RUNNING;
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
if (!(msp->dir_busy & dir))
|
||||
return -EINVAL;
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
disable_msp_tx(msp);
|
||||
else
|
||||
msp->dir_running &= ~MSP_DIR_TX;
|
||||
} else {
|
||||
disable_msp_rx(msp);
|
||||
msp->dir_running &= ~MSP_DIR_RX;
|
||||
}
|
||||
if (!msp->dir_running) {
|
||||
reg_val_GCR = readl(msp->registers + MSP_GCR);
|
||||
writel(reg_val_GCR & ~FRAME_GEN_ENABLE,
|
||||
msp->registers + MSP_GCR);
|
||||
msp->msp_state = MSP_STATE_CONFIGURED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@@ -594,7 +689,18 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
|
||||
|
||||
dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
|
||||
|
||||
if (!dir || dir & ~(MSP_DIR_TX | MSP_DIR_RX) ||
|
||||
(msp->dir_busy & dir) != dir)
|
||||
return -EINVAL;
|
||||
|
||||
status = disable_msp(msp, dir);
|
||||
msp->dir_busy &= ~dir;
|
||||
msp->dir_running &= ~dir;
|
||||
if (msp->dir_busy && !msp->dir_running) {
|
||||
writel(readl(msp->registers + MSP_GCR) & ~FRAME_GEN_ENABLE,
|
||||
msp->registers + MSP_GCR);
|
||||
msp->msp_state = MSP_STATE_CONFIGURED;
|
||||
}
|
||||
if (msp->dir_busy == 0) {
|
||||
/* disable sample rate and frame generators */
|
||||
msp->msp_state = MSP_STATE_IDLE;
|
||||
@@ -618,6 +724,8 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
|
||||
writel(0, msp->registers + MSP_RCE1);
|
||||
writel(0, msp->registers + MSP_RCE2);
|
||||
writel(0, msp->registers + MSP_RCE3);
|
||||
memset(&msp->config, 0, sizeof(msp->config));
|
||||
msp->clock_provider = false;
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -627,7 +735,7 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
|
||||
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
||||
struct ux500_msp **msp_p)
|
||||
{
|
||||
struct resource *res = NULL;
|
||||
struct resource *res;
|
||||
struct ux500_msp *msp;
|
||||
|
||||
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
|
||||
@@ -637,20 +745,10 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
||||
|
||||
msp->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msp->registers = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(msp->registers))
|
||||
return PTR_ERR(msp->registers);
|
||||
msp->tx_rx_addr = res->start + MSP_DR;
|
||||
msp->registers = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (msp->registers == NULL) {
|
||||
dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msp->msp_state = MSP_STATE_IDLE;
|
||||
msp->loopback_enable = 0;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MSP_INPUT_FREQ_APB 48000000
|
||||
|
||||
/*** Stereo mode. Used for APB data accesses as 16 bits accesses (mono),
|
||||
* 32 bits accesses (stereo).
|
||||
***/
|
||||
@@ -64,6 +62,7 @@ enum msp_direction {
|
||||
#define MSP_SRG 0x10
|
||||
#define MSP_FLR 0x14
|
||||
#define MSP_DMACR 0x18
|
||||
#define MSP_WMRK 0x1c
|
||||
|
||||
#define MSP_IMSC 0x20
|
||||
#define MSP_RIS 0x24
|
||||
@@ -230,6 +229,10 @@ enum msp_direction {
|
||||
#define RDMAE_SHIFT 0
|
||||
#define TDMAE_SHIFT 1
|
||||
|
||||
/* FIFO watermark register */
|
||||
#define MSP_WMRK_RX_4_ELEMENTS BIT(0)
|
||||
#define MSP_WMRK_TX_4_ELEMENTS BIT(3)
|
||||
|
||||
/* Interrupt Register */
|
||||
#define RX_SERVICE_INT BIT(0)
|
||||
#define RX_OVERRUN_ERROR_INT BIT(1)
|
||||
@@ -460,6 +463,8 @@ struct ux500_msp_config {
|
||||
enum msp_data_size data_size;
|
||||
unsigned int def_elem_len;
|
||||
unsigned int iodelay;
|
||||
bool clock_provider;
|
||||
bool bclk_inverted;
|
||||
};
|
||||
|
||||
struct ux500_msp {
|
||||
@@ -470,8 +475,11 @@ struct ux500_msp {
|
||||
enum msp_state msp_state;
|
||||
int def_elem_len;
|
||||
unsigned int dir_busy;
|
||||
unsigned int dir_running;
|
||||
int loopback_enable;
|
||||
unsigned int f_bitclk;
|
||||
bool clock_provider;
|
||||
struct ux500_msp_config config;
|
||||
};
|
||||
|
||||
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
|
||||
|
||||
+28
-14
@@ -21,7 +21,6 @@ enum {
|
||||
static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
|
||||
u8 *buffer, void *context, void(*handler)(struct urb *urb))
|
||||
{
|
||||
usb_init_urb(urb);
|
||||
urb->transfer_buffer = buffer;
|
||||
urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
|
||||
urb->complete = handler;
|
||||
@@ -142,6 +141,19 @@ static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb6fire_comm_free(struct comm_runtime *rt)
|
||||
{
|
||||
if (!rt)
|
||||
return;
|
||||
|
||||
if (rt->chip)
|
||||
rt->chip->comm = NULL;
|
||||
|
||||
usb_free_urb(rt->receiver);
|
||||
kfree(rt->receiver_buffer);
|
||||
kfree(rt);
|
||||
}
|
||||
|
||||
int usb6fire_comm_init(struct sfire_chip *chip)
|
||||
{
|
||||
struct comm_runtime *rt = kzalloc_obj(struct comm_runtime);
|
||||
@@ -153,14 +165,18 @@ int usb6fire_comm_init(struct sfire_chip *chip)
|
||||
|
||||
rt->receiver_buffer = kzalloc(COMM_RECEIVER_BUFSIZE, GFP_KERNEL);
|
||||
if (!rt->receiver_buffer) {
|
||||
kfree(rt);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
urb = &rt->receiver;
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
rt->receiver = urb;
|
||||
rt->serial = 1;
|
||||
rt->chip = chip;
|
||||
usb_init_urb(urb);
|
||||
rt->init_urb = usb6fire_comm_init_urb;
|
||||
rt->write8 = usb6fire_comm_write8;
|
||||
rt->write16 = usb6fire_comm_write16;
|
||||
@@ -175,13 +191,15 @@ int usb6fire_comm_init(struct sfire_chip *chip)
|
||||
urb->interval = 1;
|
||||
ret = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
kfree(rt->receiver_buffer);
|
||||
kfree(rt);
|
||||
dev_err(&chip->dev->dev, "cannot create comm data receiver.");
|
||||
return ret;
|
||||
goto error;
|
||||
}
|
||||
chip->comm = rt;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb6fire_comm_free(rt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usb6fire_comm_abort(struct sfire_chip *chip)
|
||||
@@ -189,14 +207,10 @@ void usb6fire_comm_abort(struct sfire_chip *chip)
|
||||
struct comm_runtime *rt = chip->comm;
|
||||
|
||||
if (rt)
|
||||
usb_poison_urb(&rt->receiver);
|
||||
usb_poison_urb(rt->receiver);
|
||||
}
|
||||
|
||||
void usb6fire_comm_destroy(struct sfire_chip *chip)
|
||||
{
|
||||
struct comm_runtime *rt = chip->comm;
|
||||
|
||||
kfree(rt->receiver_buffer);
|
||||
kfree(rt);
|
||||
chip->comm = NULL;
|
||||
usb6fire_comm_free(chip->comm);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ enum /* settings for comm */
|
||||
struct comm_runtime {
|
||||
struct sfire_chip *chip;
|
||||
|
||||
struct urb receiver;
|
||||
struct urb *receiver;
|
||||
u8 *receiver_buffer;
|
||||
|
||||
u8 serial; /* urb serial */
|
||||
|
||||
+30
-13
@@ -66,7 +66,7 @@ static void usb6fire_midi_out_trigger(
|
||||
struct snd_rawmidi_substream *alsa_sub, int up)
|
||||
{
|
||||
struct midi_runtime *rt = alsa_sub->rmidi->private_data;
|
||||
struct urb *urb = &rt->out_urb;
|
||||
struct urb *urb = rt->out_urb;
|
||||
__s8 ret;
|
||||
|
||||
guard(spinlock_irqsave)(&rt->out_lock);
|
||||
@@ -137,6 +137,19 @@ static const struct snd_rawmidi_ops in_ops = {
|
||||
.trigger = usb6fire_midi_in_trigger
|
||||
};
|
||||
|
||||
static void usb6fire_midi_free(struct midi_runtime *rt)
|
||||
{
|
||||
if (!rt)
|
||||
return;
|
||||
|
||||
if (rt->chip)
|
||||
rt->chip->midi = NULL;
|
||||
|
||||
usb_free_urb(rt->out_urb);
|
||||
kfree(rt->out_buffer);
|
||||
kfree(rt);
|
||||
}
|
||||
|
||||
int usb6fire_midi_init(struct sfire_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
@@ -148,8 +161,14 @@ int usb6fire_midi_init(struct sfire_chip *chip)
|
||||
|
||||
rt->out_buffer = kzalloc(MIDI_BUFSIZE, GFP_KERNEL);
|
||||
if (!rt->out_buffer) {
|
||||
kfree(rt);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rt->out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!rt->out_urb) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rt->chip = chip;
|
||||
@@ -160,15 +179,13 @@ int usb6fire_midi_init(struct sfire_chip *chip)
|
||||
spin_lock_init(&rt->in_lock);
|
||||
spin_lock_init(&rt->out_lock);
|
||||
|
||||
comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
|
||||
comm_rt->init_urb(comm_rt, rt->out_urb, rt->out_buffer, rt,
|
||||
usb6fire_midi_out_handler);
|
||||
|
||||
ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
|
||||
if (ret < 0) {
|
||||
kfree(rt->out_buffer);
|
||||
kfree(rt);
|
||||
dev_err(&chip->dev->dev, "unable to create midi.\n");
|
||||
return ret;
|
||||
goto error;
|
||||
}
|
||||
rt->instance->private_data = rt;
|
||||
strscpy(rt->instance->name, "DMX6FireUSB MIDI");
|
||||
@@ -182,6 +199,10 @@ int usb6fire_midi_init(struct sfire_chip *chip)
|
||||
|
||||
chip->midi = rt;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb6fire_midi_free(rt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usb6fire_midi_abort(struct sfire_chip *chip)
|
||||
@@ -189,14 +210,10 @@ void usb6fire_midi_abort(struct sfire_chip *chip)
|
||||
struct midi_runtime *rt = chip->midi;
|
||||
|
||||
if (rt)
|
||||
usb_poison_urb(&rt->out_urb);
|
||||
usb_poison_urb(rt->out_urb);
|
||||
}
|
||||
|
||||
void usb6fire_midi_destroy(struct sfire_chip *chip)
|
||||
{
|
||||
struct midi_runtime *rt = chip->midi;
|
||||
|
||||
kfree(rt->out_buffer);
|
||||
kfree(rt);
|
||||
chip->midi = NULL;
|
||||
usb6fire_midi_free(chip->midi);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ struct midi_runtime {
|
||||
spinlock_t in_lock;
|
||||
spinlock_t out_lock;
|
||||
struct snd_rawmidi_substream *out;
|
||||
struct urb out_urb;
|
||||
struct urb *out_urb;
|
||||
u8 out_serial; /* serial number of out packet */
|
||||
u8 *out_buffer;
|
||||
int buffer_offset;
|
||||
|
||||
+75
-53
@@ -138,8 +138,8 @@ static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
|
||||
rt->stream_state = STREAM_STOPPING;
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb_kill_urb(&rt->in_urbs[i].instance);
|
||||
usb_kill_urb(&rt->out_urbs[i].instance);
|
||||
usb_kill_urb(rt->in_urbs[i].instance);
|
||||
usb_kill_urb(rt->out_urbs[i].instance);
|
||||
}
|
||||
ctrl_rt->usb_streaming = false;
|
||||
ctrl_rt->update_streaming(ctrl_rt);
|
||||
@@ -161,13 +161,13 @@ static int usb6fire_pcm_stream_start(struct pcm_runtime *rt)
|
||||
rt->stream_state = STREAM_STARTING;
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) {
|
||||
packet = &rt->in_urbs[i].packets[k];
|
||||
packet = &rt->in_urbs[i].instance->iso_frame_desc[k];
|
||||
packet->offset = k * rt->in_packet_size;
|
||||
packet->length = rt->in_packet_size;
|
||||
packet->actual_length = 0;
|
||||
packet->status = 0;
|
||||
}
|
||||
ret = usb_submit_urb(&rt->in_urbs[i].instance,
|
||||
ret = usb_submit_urb(rt->in_urbs[i].instance,
|
||||
GFP_ATOMIC);
|
||||
if (ret) {
|
||||
usb6fire_pcm_stream_stop(rt);
|
||||
@@ -197,6 +197,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
||||
unsigned int total_length = 0;
|
||||
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
|
||||
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
|
||||
struct usb_iso_packet_descriptor *isoc;
|
||||
u32 *src = NULL;
|
||||
u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
|
||||
* (alsa_rt->frame_bits >> 3));
|
||||
@@ -207,8 +208,9 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||
/* at least 4 header bytes for valid packet.
|
||||
* after that: 32 bits per sample for analog channels */
|
||||
if (urb->packets[i].actual_length > 4)
|
||||
frame_count = (urb->packets[i].actual_length - 4)
|
||||
isoc = &urb->instance->iso_frame_desc[i];
|
||||
if (isoc->actual_length > 4)
|
||||
frame_count = (isoc->actual_length - 4)
|
||||
/ (rt->in_n_analog << 2);
|
||||
else
|
||||
frame_count = 0;
|
||||
@@ -220,7 +222,7 @@ static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
|
||||
else
|
||||
return;
|
||||
src++; /* skip leading 4 bytes of every packet */
|
||||
total_length += urb->packets[i].length;
|
||||
total_length += isoc->length;
|
||||
for (frame = 0; frame < frame_count; frame++) {
|
||||
memcpy(dest, src, bytes_per_frame);
|
||||
dest += alsa_rt->channels;
|
||||
@@ -244,6 +246,7 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub,
|
||||
int frame_count;
|
||||
struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
|
||||
struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
|
||||
struct usb_iso_packet_descriptor *isoc;
|
||||
u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off
|
||||
* (alsa_rt->frame_bits >> 3));
|
||||
u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
|
||||
@@ -263,8 +266,9 @@ static void usb6fire_pcm_playback(struct pcm_substream *sub,
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||
/* at least 4 header bytes for valid packet.
|
||||
* after that: 32 bits per sample for analog channels */
|
||||
if (urb->packets[i].length > 4)
|
||||
frame_count = (urb->packets[i].length - 4)
|
||||
isoc = &urb->instance->iso_frame_desc[i];
|
||||
if (isoc->length > 4)
|
||||
frame_count = (isoc->length - 4)
|
||||
/ (rt->out_n_analog << 2);
|
||||
else
|
||||
frame_count = 0;
|
||||
@@ -289,6 +293,7 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
|
||||
struct pcm_urb *out_urb = in_urb->peer;
|
||||
struct pcm_runtime *rt = in_urb->chip->pcm;
|
||||
struct pcm_substream *sub;
|
||||
struct usb_iso_packet_descriptor *isoc_out, *isoc_in;
|
||||
bool period_elapsed;
|
||||
int total_length = 0;
|
||||
int frame_count;
|
||||
@@ -299,11 +304,13 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
|
||||
|
||||
if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING)
|
||||
return;
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
|
||||
if (in_urb->packets[i].status) {
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||
isoc_in = &in_urb->instance->iso_frame_desc[i];
|
||||
if (isoc_in->status) {
|
||||
rt->panic = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rt->stream_state == STREAM_DISABLED) {
|
||||
dev_err(&rt->chip->dev->dev,
|
||||
@@ -328,12 +335,13 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
|
||||
|
||||
/* setup out urb structure */
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||
out_urb->packets[i].offset = total_length;
|
||||
out_urb->packets[i].length = (in_urb->packets[i].actual_length
|
||||
- 4) / (rt->in_n_analog << 2)
|
||||
isoc_out = &out_urb->instance->iso_frame_desc[i];
|
||||
isoc_in = &in_urb->instance->iso_frame_desc[i];
|
||||
isoc_out->offset = total_length;
|
||||
isoc_out->length = (isoc_in->actual_length - 4) / (rt->in_n_analog << 2)
|
||||
* (rt->out_n_analog << 2) + 4;
|
||||
out_urb->packets[i].status = 0;
|
||||
total_length += out_urb->packets[i].length;
|
||||
isoc_out->status = 0;
|
||||
total_length += isoc_out->length;
|
||||
}
|
||||
memset(out_urb->buffer, 0, total_length);
|
||||
|
||||
@@ -354,9 +362,10 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
|
||||
|
||||
/* setup the 4th byte of each sample (0x40 for analog channels) */
|
||||
dest = out_urb->buffer;
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
|
||||
if (out_urb->packets[i].length >= 4) {
|
||||
frame_count = (out_urb->packets[i].length - 4)
|
||||
for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
|
||||
isoc_out = &out_urb->instance->iso_frame_desc[i];
|
||||
if (isoc_out->length >= 4) {
|
||||
frame_count = (isoc_out->length - 4)
|
||||
/ (rt->out_n_analog << 2);
|
||||
*(dest++) = 0xaa;
|
||||
*(dest++) = 0xaa;
|
||||
@@ -370,8 +379,10 @@ static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
|
||||
*(dest++) = 0x40;
|
||||
}
|
||||
}
|
||||
usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
|
||||
usb_submit_urb(&in_urb->instance, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
usb_submit_urb(out_urb->instance, GFP_ATOMIC);
|
||||
usb_submit_urb(in_urb->instance, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb)
|
||||
@@ -534,22 +545,25 @@ static const struct snd_pcm_ops pcm_ops = {
|
||||
.pointer = usb6fire_pcm_pointer,
|
||||
};
|
||||
|
||||
static void usb6fire_pcm_init_urb(struct pcm_urb *urb,
|
||||
struct sfire_chip *chip, bool in, int ep,
|
||||
void (*handler)(struct urb *))
|
||||
static int usb6fire_pcm_init_urb(struct pcm_urb *urb,
|
||||
struct sfire_chip *chip, bool in, int ep,
|
||||
void (*handler)(struct urb *))
|
||||
{
|
||||
urb->chip = chip;
|
||||
usb_init_urb(&urb->instance);
|
||||
urb->instance.transfer_buffer = urb->buffer;
|
||||
urb->instance.transfer_buffer_length =
|
||||
urb->instance = usb_alloc_urb(PCM_N_PACKETS_PER_URB, GFP_KERNEL);
|
||||
if (!urb->instance)
|
||||
return -ENOMEM;
|
||||
urb->instance->transfer_buffer = urb->buffer;
|
||||
urb->instance->transfer_buffer_length =
|
||||
PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE;
|
||||
urb->instance.dev = chip->dev;
|
||||
urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep)
|
||||
urb->instance->dev = chip->dev;
|
||||
urb->instance->pipe = in ? usb_rcvisocpipe(chip->dev, ep)
|
||||
: usb_sndisocpipe(chip->dev, ep);
|
||||
urb->instance.interval = 1;
|
||||
urb->instance.complete = handler;
|
||||
urb->instance.context = urb;
|
||||
urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB;
|
||||
urb->instance->interval = 1;
|
||||
urb->instance->complete = handler;
|
||||
urb->instance->context = urb;
|
||||
urb->instance->number_of_packets = PCM_N_PACKETS_PER_URB;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt)
|
||||
@@ -571,14 +585,23 @@ static int usb6fire_pcm_buffers_init(struct pcm_runtime *rt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb6fire_pcm_buffers_destroy(struct pcm_runtime *rt)
|
||||
static void usb6fire_pcm_free(struct pcm_runtime *rt)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!rt)
|
||||
return;
|
||||
|
||||
if (rt->chip)
|
||||
rt->chip->pcm = NULL;
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb_free_urb(rt->out_urbs[i].instance);
|
||||
kfree(rt->out_urbs[i].buffer);
|
||||
usb_free_urb(rt->in_urbs[i].instance);
|
||||
kfree(rt->in_urbs[i].buffer);
|
||||
}
|
||||
kfree(rt);
|
||||
}
|
||||
|
||||
int usb6fire_pcm_init(struct sfire_chip *chip)
|
||||
@@ -593,11 +616,8 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = usb6fire_pcm_buffers_init(rt);
|
||||
if (ret) {
|
||||
usb6fire_pcm_buffers_destroy(rt);
|
||||
kfree(rt);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
rt->chip = chip;
|
||||
rt->stream_state = STREAM_DISABLED;
|
||||
@@ -609,10 +629,14 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
|
||||
spin_lock_init(&rt->capture.lock);
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP,
|
||||
usb6fire_pcm_in_urb_handler);
|
||||
usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP,
|
||||
usb6fire_pcm_out_urb_handler);
|
||||
ret = usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP,
|
||||
usb6fire_pcm_in_urb_handler);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
ret = usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP,
|
||||
usb6fire_pcm_out_urb_handler);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
rt->in_urbs[i].peer = &rt->out_urbs[i];
|
||||
rt->out_urbs[i].peer = &rt->in_urbs[i];
|
||||
@@ -620,10 +644,8 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
|
||||
|
||||
ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm);
|
||||
if (ret < 0) {
|
||||
usb6fire_pcm_buffers_destroy(rt);
|
||||
kfree(rt);
|
||||
dev_err(&chip->dev->dev, "cannot create pcm instance.\n");
|
||||
return ret;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pcm->private_data = rt;
|
||||
@@ -636,6 +658,10 @@ int usb6fire_pcm_init(struct sfire_chip *chip)
|
||||
|
||||
chip->pcm = rt;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb6fire_pcm_free(rt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usb6fire_pcm_abort(struct sfire_chip *chip)
|
||||
@@ -653,8 +679,8 @@ void usb6fire_pcm_abort(struct sfire_chip *chip)
|
||||
snd_pcm_stop_xrun(rt->capture.instance);
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb_poison_urb(&rt->in_urbs[i].instance);
|
||||
usb_poison_urb(&rt->out_urbs[i].instance);
|
||||
usb_poison_urb(rt->in_urbs[i].instance);
|
||||
usb_poison_urb(rt->out_urbs[i].instance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -662,9 +688,5 @@ void usb6fire_pcm_abort(struct sfire_chip *chip)
|
||||
|
||||
void usb6fire_pcm_destroy(struct sfire_chip *chip)
|
||||
{
|
||||
struct pcm_runtime *rt = chip->pcm;
|
||||
|
||||
usb6fire_pcm_buffers_destroy(rt);
|
||||
kfree(rt);
|
||||
chip->pcm = NULL;
|
||||
usb6fire_pcm_free(chip->pcm);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,7 @@ enum /* settings for pcm */
|
||||
struct pcm_urb {
|
||||
struct sfire_chip *chip;
|
||||
|
||||
/* BEGIN DO NOT SEPARATE */
|
||||
struct urb instance;
|
||||
struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB];
|
||||
/* END DO NOT SEPARATE */
|
||||
struct urb *instance;
|
||||
u8 *buffer;
|
||||
|
||||
struct pcm_urb *peer;
|
||||
|
||||
+24
-12
@@ -192,8 +192,8 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb)
|
||||
break;
|
||||
}
|
||||
|
||||
cdev->ep1_in_urb.actual_length = 0;
|
||||
ret = usb_submit_urb(&cdev->ep1_in_urb, GFP_ATOMIC);
|
||||
cdev->ep1_in_urb->actual_length = 0;
|
||||
ret = usb_submit_urb(cdev->ep1_in_urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "unable to submit urb. OOM!?\n");
|
||||
}
|
||||
@@ -408,6 +408,10 @@ static void card_free(struct snd_card *card)
|
||||
#endif
|
||||
snd_usb_caiaq_audio_free(cdev);
|
||||
usb_put_dev(cdev->chip.dev);
|
||||
usb_free_urb(cdev->ep1_in_urb);
|
||||
cdev->ep1_in_urb = NULL;
|
||||
usb_free_urb(cdev->midi_out_urb);
|
||||
cdev->midi_out_urb = NULL;
|
||||
}
|
||||
|
||||
static int create_card(struct usb_device *usb_dev,
|
||||
@@ -457,22 +461,30 @@ static int init_card(struct snd_usb_caiaqdev *cdev)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
usb_init_urb(&cdev->ep1_in_urb);
|
||||
usb_init_urb(&cdev->midi_out_urb);
|
||||
cdev->ep1_in_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!cdev->ep1_in_urb)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_fill_bulk_urb(&cdev->ep1_in_urb, usb_dev,
|
||||
cdev->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!cdev->midi_out_urb) {
|
||||
usb_free_urb(cdev->ep1_in_urb);
|
||||
cdev->ep1_in_urb = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(cdev->ep1_in_urb, usb_dev,
|
||||
usb_rcvbulkpipe(usb_dev, 0x1),
|
||||
cdev->ep1_in_buf, EP1_BUFSIZE,
|
||||
usb_ep1_command_reply_dispatch, cdev);
|
||||
|
||||
usb_fill_bulk_urb(&cdev->midi_out_urb, usb_dev,
|
||||
usb_fill_bulk_urb(cdev->midi_out_urb, usb_dev,
|
||||
usb_sndbulkpipe(usb_dev, 0x1),
|
||||
cdev->midi_out_buf, EP1_BUFSIZE,
|
||||
snd_usb_caiaq_midi_output_done, cdev);
|
||||
|
||||
/* sanity checks of EPs before actually submitting */
|
||||
if (usb_urb_ep_type_check(&cdev->ep1_in_urb) ||
|
||||
usb_urb_ep_type_check(&cdev->midi_out_urb)) {
|
||||
if (usb_urb_ep_type_check(cdev->ep1_in_urb) ||
|
||||
usb_urb_ep_type_check(cdev->midi_out_urb)) {
|
||||
dev_err(dev, "invalid EPs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -480,7 +492,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)
|
||||
init_waitqueue_head(&cdev->ep1_wait_queue);
|
||||
init_waitqueue_head(&cdev->prepare_wait_queue);
|
||||
|
||||
if (usb_submit_urb(&cdev->ep1_in_urb, GFP_KERNEL) != 0)
|
||||
if (usb_submit_urb(cdev->ep1_in_urb, GFP_KERNEL) != 0)
|
||||
return -EIO;
|
||||
|
||||
err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
|
||||
@@ -530,7 +542,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev)
|
||||
return 0;
|
||||
|
||||
err_kill_urb:
|
||||
usb_kill_urb(&cdev->ep1_in_urb);
|
||||
usb_kill_urb(cdev->ep1_in_urb);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -576,8 +588,8 @@ static void snd_disconnect(struct usb_interface *intf)
|
||||
#endif
|
||||
snd_usb_caiaq_audio_disconnect(cdev);
|
||||
|
||||
usb_kill_urb(&cdev->ep1_in_urb);
|
||||
usb_kill_urb(&cdev->midi_out_urb);
|
||||
usb_kill_urb(cdev->ep1_in_urb);
|
||||
usb_kill_urb(cdev->midi_out_urb);
|
||||
|
||||
snd_card_free_when_closed(card);
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ struct snd_usb_caiaq_cb_info;
|
||||
struct snd_usb_caiaqdev {
|
||||
struct snd_usb_audio chip;
|
||||
|
||||
struct urb ep1_in_urb;
|
||||
struct urb midi_out_urb;
|
||||
struct urb *ep1_in_urb;
|
||||
struct urb *midi_out_urb;
|
||||
struct urb **data_urbs_in;
|
||||
struct urb **data_urbs_out;
|
||||
struct snd_usb_caiaq_cb_info *data_cb_info;
|
||||
|
||||
@@ -43,7 +43,7 @@ static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substre
|
||||
{
|
||||
struct snd_usb_caiaqdev *cdev = substream->rmidi->private_data;
|
||||
if (cdev->midi_out_active) {
|
||||
usb_kill_urb(&cdev->midi_out_urb);
|
||||
usb_kill_urb(cdev->midi_out_urb);
|
||||
cdev->midi_out_active = 0;
|
||||
}
|
||||
return 0;
|
||||
@@ -64,9 +64,9 @@ static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *cdev,
|
||||
return;
|
||||
|
||||
cdev->midi_out_buf[2] = len;
|
||||
cdev->midi_out_urb.transfer_buffer_length = len+3;
|
||||
cdev->midi_out_urb->transfer_buffer_length = len+3;
|
||||
|
||||
ret = usb_submit_urb(&cdev->midi_out_urb, GFP_ATOMIC);
|
||||
ret = usb_submit_urb(cdev->midi_out_urb, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
dev_err(dev,
|
||||
"snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
|
||||
|
||||
+23
-21
@@ -24,7 +24,7 @@
|
||||
struct pcm_urb {
|
||||
struct hiface_chip *chip;
|
||||
|
||||
struct urb instance;
|
||||
struct urb *instance;
|
||||
struct usb_anchor submitted;
|
||||
u8 *buffer;
|
||||
};
|
||||
@@ -193,7 +193,7 @@ static void hiface_pcm_stream_stop(struct pcm_runtime *rt)
|
||||
if (!time)
|
||||
usb_kill_anchored_urbs(
|
||||
&rt->out_urbs[i].submitted);
|
||||
usb_kill_urb(&rt->out_urbs[i].instance);
|
||||
usb_kill_urb(rt->out_urbs[i].instance);
|
||||
}
|
||||
|
||||
rt->stream_state = STREAM_DISABLED;
|
||||
@@ -215,9 +215,9 @@ static int hiface_pcm_stream_start(struct pcm_runtime *rt)
|
||||
rt->stream_state = STREAM_STARTING;
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE);
|
||||
usb_anchor_urb(&rt->out_urbs[i].instance,
|
||||
usb_anchor_urb(rt->out_urbs[i].instance,
|
||||
&rt->out_urbs[i].submitted);
|
||||
ret = usb_submit_urb(&rt->out_urbs[i].instance,
|
||||
ret = usb_submit_urb(rt->out_urbs[i].instance,
|
||||
GFP_ATOMIC);
|
||||
if (ret) {
|
||||
hiface_pcm_stream_stop(rt);
|
||||
@@ -334,7 +334,7 @@ static void hiface_pcm_out_urb_handler(struct urb *usb_urb)
|
||||
if (do_period_elapsed)
|
||||
snd_pcm_period_elapsed(sub->instance);
|
||||
|
||||
ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
|
||||
ret = usb_submit_urb(out_urb->instance, GFP_ATOMIC);
|
||||
if (ret < 0)
|
||||
goto out_fail;
|
||||
|
||||
@@ -492,16 +492,18 @@ static int hiface_pcm_init_urb(struct pcm_urb *urb,
|
||||
void (*handler)(struct urb *))
|
||||
{
|
||||
urb->chip = chip;
|
||||
usb_init_urb(&urb->instance);
|
||||
urb->instance = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb->instance)
|
||||
return -ENOMEM;
|
||||
|
||||
urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL);
|
||||
if (!urb->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
usb_fill_bulk_urb(&urb->instance, chip->dev,
|
||||
usb_fill_bulk_urb(urb->instance, chip->dev,
|
||||
usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer,
|
||||
PCM_PACKET_SIZE, handler, urb);
|
||||
if (usb_urb_ep_type_check(&urb->instance))
|
||||
if (usb_urb_ep_type_check(urb->instance))
|
||||
return -EINVAL;
|
||||
init_usb_anchor(&urb->submitted);
|
||||
|
||||
@@ -520,24 +522,26 @@ void hiface_pcm_abort(struct hiface_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
static void hiface_pcm_destroy(struct hiface_chip *chip)
|
||||
static void hiface_pcm_destroy(struct pcm_runtime *rt)
|
||||
{
|
||||
struct pcm_runtime *rt = chip->pcm;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++)
|
||||
kfree(rt->out_urbs[i].buffer);
|
||||
if (!rt)
|
||||
return;
|
||||
|
||||
kfree(chip->pcm);
|
||||
chip->pcm = NULL;
|
||||
if (rt->chip)
|
||||
rt->chip->pcm = NULL;
|
||||
|
||||
for (i = 0; i < PCM_N_URBS; i++) {
|
||||
usb_free_urb(rt->out_urbs[i].instance);
|
||||
kfree(rt->out_urbs[i].buffer);
|
||||
}
|
||||
kfree(rt);
|
||||
}
|
||||
|
||||
static void hiface_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
struct pcm_runtime *rt = pcm->private_data;
|
||||
|
||||
if (rt)
|
||||
hiface_pcm_destroy(rt->chip);
|
||||
hiface_pcm_destroy(pcm->private_data);
|
||||
}
|
||||
|
||||
int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
|
||||
@@ -587,8 +591,6 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (i = 0; i < PCM_N_URBS; i++)
|
||||
kfree(rt->out_urbs[i].buffer);
|
||||
kfree(rt);
|
||||
hiface_pcm_destroy(rt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+54
-48
@@ -109,10 +109,10 @@ struct ua101 {
|
||||
unsigned int buffer_pos;
|
||||
unsigned int queue_length;
|
||||
struct ua101_urb {
|
||||
struct urb urb;
|
||||
struct usb_iso_packet_descriptor iso_frame_desc[1];
|
||||
struct urb *urb;
|
||||
struct list_head ready_list;
|
||||
} *urbs[MAX_QUEUE_LENGTH];
|
||||
struct ua101 *ua;
|
||||
} urbs[MAX_QUEUE_LENGTH];
|
||||
struct {
|
||||
unsigned int size;
|
||||
void *addr;
|
||||
@@ -167,15 +167,15 @@ static void abort_usb_playback(struct ua101 *ua)
|
||||
wake_up(&ua->alsa_playback_wait);
|
||||
}
|
||||
|
||||
static void playback_urb_complete(struct urb *usb_urb)
|
||||
static void playback_urb_complete(struct urb *urb)
|
||||
{
|
||||
struct ua101_urb *urb = (struct ua101_urb *)usb_urb;
|
||||
struct ua101 *ua = urb->urb.context;
|
||||
struct ua101_urb *ua_urb = urb->context;
|
||||
struct ua101 *ua = ua_urb->ua;
|
||||
|
||||
if (unlikely(urb->urb.status == -ENOENT || /* unlinked */
|
||||
urb->urb.status == -ENODEV || /* device removed */
|
||||
urb->urb.status == -ECONNRESET || /* unlinked */
|
||||
urb->urb.status == -ESHUTDOWN)) { /* device disabled */
|
||||
if (unlikely(urb->status == -ENOENT || /* unlinked */
|
||||
urb->status == -ENODEV || /* device removed */
|
||||
urb->status == -ECONNRESET || /* unlinked */
|
||||
urb->status == -ESHUTDOWN)) { /* device disabled */
|
||||
abort_usb_playback(ua);
|
||||
abort_alsa_playback(ua);
|
||||
return;
|
||||
@@ -184,18 +184,19 @@ static void playback_urb_complete(struct urb *usb_urb)
|
||||
if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) {
|
||||
/* append URB to FIFO */
|
||||
guard(spinlock_irqsave)(&ua->lock);
|
||||
list_add_tail(&urb->ready_list, &ua->ready_playback_urbs);
|
||||
list_add_tail(&ua_urb->ready_list, &ua->ready_playback_urbs);
|
||||
if (ua->rate_feedback_count > 0)
|
||||
queue_work(system_highpri_wq, &ua->playback_work);
|
||||
ua->playback.substream->runtime->delay -=
|
||||
urb->urb.iso_frame_desc[0].length /
|
||||
urb->iso_frame_desc[0].length /
|
||||
ua->playback.frame_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static void first_playback_urb_complete(struct urb *urb)
|
||||
{
|
||||
struct ua101 *ua = urb->context;
|
||||
struct ua101_urb *ua_urb = urb->context;
|
||||
struct ua101 *ua = ua_urb->ua;
|
||||
|
||||
urb->complete = playback_urb_complete;
|
||||
playback_urb_complete(urb);
|
||||
@@ -248,7 +249,8 @@ static void playback_work(struct work_struct *work)
|
||||
{
|
||||
struct ua101 *ua = container_of(work, struct ua101, playback_work);
|
||||
unsigned int frames;
|
||||
struct ua101_urb *urb;
|
||||
struct ua101_urb *ua_urb;
|
||||
struct urb *urb;
|
||||
bool do_period_elapsed = false;
|
||||
int err;
|
||||
|
||||
@@ -275,23 +277,24 @@ static void playback_work(struct work_struct *work)
|
||||
ua->rate_feedback_count--;
|
||||
|
||||
/* take URB out of FIFO */
|
||||
urb = list_first_entry(&ua->ready_playback_urbs,
|
||||
struct ua101_urb, ready_list);
|
||||
list_del(&urb->ready_list);
|
||||
ua_urb = list_first_entry(&ua->ready_playback_urbs,
|
||||
struct ua101_urb, ready_list);
|
||||
list_del(&ua_urb->ready_list);
|
||||
urb = ua_urb->urb;
|
||||
|
||||
/* fill packet with data or silence */
|
||||
urb->urb.iso_frame_desc[0].length =
|
||||
urb->iso_frame_desc[0].length =
|
||||
frames * ua->playback.frame_bytes;
|
||||
if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
|
||||
do_period_elapsed |= copy_playback_data(&ua->playback,
|
||||
&urb->urb,
|
||||
urb,
|
||||
frames);
|
||||
else
|
||||
memset(urb->urb.transfer_buffer, 0,
|
||||
urb->urb.iso_frame_desc[0].length);
|
||||
memset(urb->transfer_buffer, 0,
|
||||
urb->iso_frame_desc[0].length);
|
||||
|
||||
/* and off you go ... */
|
||||
err = usb_submit_urb(&urb->urb, GFP_ATOMIC);
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (unlikely(err < 0)) {
|
||||
abort_usb_playback(ua);
|
||||
abort_alsa_playback(ua);
|
||||
@@ -342,7 +345,8 @@ static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb,
|
||||
|
||||
static void capture_urb_complete(struct urb *urb)
|
||||
{
|
||||
struct ua101 *ua = urb->context;
|
||||
struct ua101_urb *ua_urb = urb->context;
|
||||
struct ua101 *ua = ua_urb->ua;
|
||||
struct ua101_stream *stream = &ua->capture;
|
||||
unsigned int frames, write_ptr;
|
||||
bool do_period_elapsed;
|
||||
@@ -413,7 +417,8 @@ stream_stopped:
|
||||
|
||||
static void first_capture_urb_complete(struct urb *urb)
|
||||
{
|
||||
struct ua101 *ua = urb->context;
|
||||
struct ua101_urb *ua_urb = urb->context;
|
||||
struct ua101 *ua = ua_urb->ua;
|
||||
|
||||
urb->complete = capture_urb_complete;
|
||||
capture_urb_complete(urb);
|
||||
@@ -427,7 +432,7 @@ static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < stream->queue_length; ++i) {
|
||||
int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL);
|
||||
int err = usb_submit_urb(stream->urbs[i].urb, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
dev_err(&ua->dev->dev, "USB request error %d: %s\n",
|
||||
err, usb_error_string(err));
|
||||
@@ -442,8 +447,8 @@ static void kill_stream_urbs(struct ua101_stream *stream)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < stream->queue_length; ++i)
|
||||
if (stream->urbs[i])
|
||||
usb_kill_urb(&stream->urbs[i]->urb);
|
||||
if (stream->urbs[i].urb)
|
||||
usb_kill_urb(stream->urbs[i].urb);
|
||||
}
|
||||
|
||||
static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index)
|
||||
@@ -508,7 +513,7 @@ static int start_usb_capture(struct ua101 *ua)
|
||||
return err;
|
||||
|
||||
clear_bit(CAPTURE_URB_COMPLETED, &ua->states);
|
||||
ua->capture.urbs[0]->urb.complete = first_capture_urb_complete;
|
||||
ua->capture.urbs[0].urb->complete = first_capture_urb_complete;
|
||||
ua->rate_feedback_start = 0;
|
||||
ua->rate_feedback_count = 0;
|
||||
|
||||
@@ -550,7 +555,7 @@ static int start_usb_playback(struct ua101 *ua)
|
||||
return err;
|
||||
|
||||
clear_bit(PLAYBACK_URB_COMPLETED, &ua->states);
|
||||
ua->playback.urbs[0]->urb.complete =
|
||||
ua->playback.urbs[0].urb->complete =
|
||||
first_playback_urb_complete;
|
||||
scoped_guard(spinlock_irq, &ua->lock) {
|
||||
INIT_LIST_HEAD(&ua->ready_playback_urbs);
|
||||
@@ -580,7 +585,7 @@ static int start_usb_playback(struct ua101 *ua)
|
||||
add_with_wraparound(ua, &ua->rate_feedback_start, 1);
|
||||
ua->rate_feedback_count--;
|
||||
}
|
||||
urb = &ua->playback.urbs[i]->urb;
|
||||
urb = ua->playback.urbs[i].urb;
|
||||
urb->iso_frame_desc[0].length =
|
||||
frames * ua->playback.frame_bytes;
|
||||
memset(urb->transfer_buffer, 0,
|
||||
@@ -1059,7 +1064,7 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream,
|
||||
void (*urb_complete)(struct urb *))
|
||||
{
|
||||
unsigned max_packet_size = stream->max_packet_bytes;
|
||||
struct ua101_urb *urb;
|
||||
struct urb *urb;
|
||||
unsigned int b, u = 0;
|
||||
|
||||
for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) {
|
||||
@@ -1070,23 +1075,24 @@ static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream,
|
||||
while (size >= max_packet_size) {
|
||||
if (u >= stream->queue_length)
|
||||
goto bufsize_error;
|
||||
urb = kmalloc_obj(*urb);
|
||||
urb = usb_alloc_urb(1, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
usb_init_urb(&urb->urb);
|
||||
urb->urb.dev = ua->dev;
|
||||
urb->urb.pipe = stream->usb_pipe;
|
||||
urb->urb.transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
urb->urb.transfer_buffer = addr;
|
||||
urb->urb.transfer_dma = dma;
|
||||
urb->urb.transfer_buffer_length = max_packet_size;
|
||||
urb->urb.number_of_packets = 1;
|
||||
urb->urb.interval = 1;
|
||||
urb->urb.context = ua;
|
||||
urb->urb.complete = urb_complete;
|
||||
urb->urb.iso_frame_desc[0].offset = 0;
|
||||
urb->urb.iso_frame_desc[0].length = max_packet_size;
|
||||
stream->urbs[u++] = urb;
|
||||
urb->dev = ua->dev;
|
||||
urb->pipe = stream->usb_pipe;
|
||||
urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
|
||||
urb->transfer_buffer = addr;
|
||||
urb->transfer_dma = dma;
|
||||
urb->transfer_buffer_length = max_packet_size;
|
||||
urb->number_of_packets = 1;
|
||||
urb->interval = 1;
|
||||
urb->context = &stream->urbs[u];
|
||||
urb->complete = urb_complete;
|
||||
urb->iso_frame_desc[0].offset = 0;
|
||||
urb->iso_frame_desc[0].length = max_packet_size;
|
||||
stream->urbs[u].ua = ua;
|
||||
stream->urbs[u].urb = urb;
|
||||
u++;
|
||||
size -= max_packet_size;
|
||||
addr += max_packet_size;
|
||||
dma += max_packet_size;
|
||||
@@ -1104,8 +1110,8 @@ static void free_stream_urbs(struct ua101_stream *stream)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < stream->queue_length; ++i) {
|
||||
kfree(stream->urbs[i]);
|
||||
stream->urbs[i] = NULL;
|
||||
usb_free_urb(stream->urbs[i].urb);
|
||||
stream->urbs[i].urb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -849,6 +849,27 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A post configuration device descriptor read is needed to make the CM1A
|
||||
* operational after reenumeration.
|
||||
*/
|
||||
static int snd_usb_cm1a_boot_quirk(struct usb_device *dev)
|
||||
{
|
||||
struct usb_device_descriptor *desc __free(kfree) = kmalloc_obj(*desc);
|
||||
int err;
|
||||
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "failed to read device descriptor: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some sound cards from Native Instruments are in fact compliant to the USB
|
||||
* audio standard of version 2 and other approved USB standards, even though
|
||||
@@ -1681,6 +1702,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
|
||||
switch (id) {
|
||||
case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */
|
||||
return snd_usb_motu_m_series_boot_quirk(dev);
|
||||
case USB_ID(0x1397, 0x1234): /* Behringer CM1A */
|
||||
return snd_usb_cm1a_boot_quirk(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2390,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
|
||||
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x1397, 0x050c, /* Behringer Flow 8 */
|
||||
QUIRK_FLAG_IFB_SILENCE_ON_EMPTY),
|
||||
DEVICE_FLG(0x1397, 0x0510, /* Behringer UV1 */
|
||||
QUIRK_FLAG_PLAYBACK_FIRST | QUIRK_FLAG_GENERIC_IMPLICIT_FB),
|
||||
DEVICE_FLG(0x13e5, 0x0001, /* Serato Phono */
|
||||
QUIRK_FLAG_IGNORE_CTL_ERROR),
|
||||
DEVICE_FLG(0x152a, 0x85dd, /* SMSL USB DAC */
|
||||
|
||||
@@ -180,8 +180,11 @@ static int usb_stream_hwdep_mmap(struct snd_hwdep *hw,
|
||||
guard(mutex)(&us122l->mutex);
|
||||
s = us122l->sk.s;
|
||||
read = offset < s->read_size;
|
||||
if (read && area->vm_flags & VM_WRITE)
|
||||
return -EPERM;
|
||||
if (read) {
|
||||
if (area->vm_flags & VM_WRITE)
|
||||
return -EPERM;
|
||||
vm_flags_clear(area, VM_MAYWRITE);
|
||||
}
|
||||
/* if userspace tries to mmap beyond end of our buffer, fail */
|
||||
if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) {
|
||||
dev_warn(hw->card->dev, "%s: size %lu > %u\n", __func__,
|
||||
|
||||
@@ -189,6 +189,9 @@ static void i_usx2y_in04_int(struct urb *urb)
|
||||
return;
|
||||
}
|
||||
|
||||
if (urb->actual_length < USX2Y_IN04_SIZE)
|
||||
goto resubmit;
|
||||
|
||||
if (us428ctls) {
|
||||
diff = -1;
|
||||
if (us428ctls->ctl_snapshot_last == -2) {
|
||||
@@ -196,7 +199,7 @@ static void i_usx2y_in04_int(struct urb *urb)
|
||||
memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));
|
||||
us428ctls->ctl_snapshot_last = -1;
|
||||
} else {
|
||||
for (i = 0; i < 21; i++) {
|
||||
for (i = 0; i < USX2Y_IN04_SIZE; i++) {
|
||||
if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {
|
||||
if (diff < 0)
|
||||
diff = i;
|
||||
@@ -253,6 +256,7 @@ static void i_usx2y_in04_int(struct urb *urb)
|
||||
if (err)
|
||||
dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n", err);
|
||||
|
||||
resubmit:
|
||||
urb->dev = usx2y->dev;
|
||||
usb_submit_urb(urb, GFP_ATOMIC);
|
||||
}
|
||||
@@ -305,7 +309,7 @@ int usx2y_in04_init(struct usx2ydev *usx2y)
|
||||
goto error;
|
||||
}
|
||||
|
||||
usx2y->in04_buf = kmalloc(21, GFP_KERNEL);
|
||||
usx2y->in04_buf = kzalloc(USX2Y_IN04_SIZE, GFP_KERNEL);
|
||||
if (!usx2y->in04_buf) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
@@ -313,7 +317,7 @@ int usx2y_in04_init(struct usx2ydev *usx2y)
|
||||
|
||||
init_waitqueue_head(&usx2y->in04_wait_queue);
|
||||
usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4),
|
||||
usx2y->in04_buf, 21,
|
||||
usx2y->in04_buf, USX2Y_IN04_SIZE,
|
||||
i_usx2y_in04_int, usx2y,
|
||||
10);
|
||||
if (usb_urb_ep_type_check(usx2y->in04_urb)) {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../midi.h"
|
||||
#include "usbus428ctldefs.h"
|
||||
|
||||
#define USX2Y_IN04_SIZE sizeof(struct us428_ctls)
|
||||
|
||||
#define NRURBS 2
|
||||
|
||||
/* Default value used for nr of packs per urb.
|
||||
@@ -55,7 +57,7 @@ struct usx2ydev {
|
||||
int stride;
|
||||
struct urb *in04_urb;
|
||||
void *in04_buf;
|
||||
char in04_last[24];
|
||||
char in04_last[USX2Y_IN04_SIZE];
|
||||
unsigned int in04_int_calls;
|
||||
struct snd_usx2y_urb_seq *us04;
|
||||
wait_queue_head_t in04_wait_queue;
|
||||
|
||||
Reference in New Issue
Block a user