[alsa-devel] [PATCH v3 0/6] ASoC: Intel: ACPI support and machines
This is third rev of patch series which fixes the modules split as discussed in ML. The core part is now module along with PCI and ACPI one. Added fix reported by Dan as well. Also added the BYTCR machine driver and MERR machine driver
Updates in v3: - Fix export fo few more symbols - fix cleanup of probe and setup code - add MERR machine code
Subhransu S. Prusty (1): ASoC: Intel: add BYTCR machine driver with RT5640
Vinod Koul (5): ASoC: Intel: mrfld - remove unnecessary check for pointer ASoC: Intel: mrfld - create separate module for pci part ASoC: Intel: mrfld - add shim save restore ASoC: Intel: mrfld- add ACPI module ASoC: Intel: Add Merrifield machine driver
sound/soc/intel/Kconfig | 37 +++- sound/soc/intel/Makefile | 4 + sound/soc/intel/bytcr_dpcm_rt5640.c | 258 ++++++++++++++++++++++ sound/soc/intel/merr_dpcm_wm8958.c | 416 +++++++++++++++++++++++++++++++++++ sound/soc/intel/sst-atom-controls.c | 2 +- sound/soc/intel/sst/Makefile | 8 +- sound/soc/intel/sst/sst.c | 254 +++++---------------- sound/soc/intel/sst/sst.h | 9 +- sound/soc/intel/sst/sst_acpi.c | 377 +++++++++++++++++++++++++++++++ sound/soc/intel/sst/sst_pci.c | 209 ++++++++++++++++++ sound/soc/intel/sst/sst_pvt.c | 3 + 11 files changed, 1378 insertions(+), 199 deletions(-) create mode 100644 sound/soc/intel/bytcr_dpcm_rt5640.c create mode 100644 sound/soc/intel/merr_dpcm_wm8958.c create mode 100644 sound/soc/intel/sst/sst_acpi.c create mode 100644 sound/soc/intel/sst/sst_pci.c
the 'platform' pointer in sst_map_modules_to_pipe() is deref in caller function so we need to check for it in this function
Reported-by: Dan Carpenter dan.carpenter@oracle.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst-atom-controls.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c index 309a8f3..90aa5c0 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/sst-atom-controls.c @@ -1351,7 +1351,7 @@ static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) int ret = 0;
list_for_each_entry(w, &platform->component.card->widgets, list) { - if (platform && is_sst_dapm_widget(w) && (w->priv)) { + if (is_sst_dapm_widget(w) && (w->priv)) { struct sst_ids *ids = w->priv;
dev_dbg(platform->dev, "widget type=%d name=%s\n",
On Tue, Nov 04, 2014 at 04:25:15PM +0530, Vinod Koul wrote:
the 'platform' pointer in sst_map_modules_to_pipe() is deref in caller function so we need to check for it in this function
Applied, thanks.
Now the SST_IPC will support both ACPI and PCI, separate into core module and PCI module. This also move probe function into PCI module and exports the required symbols from core module
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Kconfig | 6 +- sound/soc/intel/sst/Makefile | 8 +- sound/soc/intel/sst/sst.c | 212 +++------------------------------------- sound/soc/intel/sst/sst.h | 6 + sound/soc/intel/sst/sst_pci.c | 209 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst/sst_pvt.c | 1 + 6 files changed, 243 insertions(+), 199 deletions(-) create mode 100644 sound/soc/intel/sst/sst_pci.c
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index ae7f872..c963a5d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -3,7 +3,7 @@ config SND_MFLD_MACHINE depends on INTEL_SCU_IPC select SND_SOC_SN95031 select SND_SST_MFLD_PLATFORM - select SND_SST_IPC + select SND_SST_IPC_PCI help This adds support for ASoC machine driver for Intel(R) MID Medfield platform used as alsa device in audio substem in Intel(R) MID devices @@ -16,6 +16,10 @@ config SND_SST_MFLD_PLATFORM config SND_SST_IPC tristate
+config SND_SST_IPC_PCI + tristate + select SND_SST_IPC + config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile index 4d0e79b..b8aa1d3 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/sst/Makefile @@ -1,3 +1,7 @@ -snd-intel-sst-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o +snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o +snd-intel-sst-pci-objs += sst_pci.o + + +obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o +obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
-obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst.o diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 2bfb404..8753754 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -20,20 +20,14 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/module.h> -#include <linux/pci.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/firmware.h> #include <linux/pm_runtime.h> #include <linux/pm_qos.h> #include <linux/async.h> -#include <linux/delay.h> -#include <linux/acpi.h> #include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <sound/compress_driver.h> -#include <asm/intel-mid.h> #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" @@ -242,6 +236,7 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx,
return 0; } +EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
int sst_context_init(struct intel_sst_drv *ctx) { @@ -260,6 +255,7 @@ int sst_context_init(struct intel_sst_drv *ctx) return -EINVAL;
sst_init_locks(ctx); + sst_set_fw_state_locked(ctx, SST_RESET);
/* pvt_id 0 reserved for async messages */ ctx->pvt_id = 1; @@ -307,12 +303,22 @@ int sst_context_init(struct intel_sst_drv *ctx) } pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + + dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name); + ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name, + ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); + if (ret) { + dev_err(ctx->dev, "Firmware download failed:%d\n", ret); + goto do_free_mem; + } + sst_register(ctx->dev); return 0;
do_free_mem: destroy_workqueue(ctx->post_msg_wq); return ret; } +EXPORT_SYMBOL_GPL(sst_context_init);
void sst_context_cleanup(struct intel_sst_drv *ctx) { @@ -331,6 +337,7 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) sst_memcpy_free_resources(ctx); ctx = NULL; } +EXPORT_SYMBOL_GPL(sst_context_cleanup);
void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { @@ -339,175 +346,7 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) pm_runtime_allow(ctx->dev); pm_runtime_put_noidle(ctx->dev); } - -static int sst_platform_get_resources(struct intel_sst_drv *ctx) -{ - int ddr_base, ret = 0; - struct pci_dev *pci = ctx->pci; - ret = pci_request_regions(pci, SST_DRV_NAME); - if (ret) - return ret; - - /* map registers */ - /* DDR base */ - if (ctx->dev_id == SST_MRFLD_PCI_ID) { - ctx->ddr_base = pci_resource_start(pci, 0); - /* check that the relocated IMR base matches with FW Binary */ - ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); - if (!ctx->pdata->lib_info) { - dev_err(ctx->dev, "lib_info pointer NULL\n"); - ret = -EINVAL; - goto do_release_regions; - } - if (ddr_base != ctx->pdata->lib_info->mod_base) { - dev_err(ctx->dev, - "FW LSP DDR BASE does not match with IFWI\n"); - ret = -EINVAL; - goto do_release_regions; - } - ctx->ddr_end = pci_resource_end(pci, 0); - - ctx->ddr = pcim_iomap(pci, 0, - pci_resource_len(pci, 0)); - if (!ctx->ddr) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); - } else { - ctx->ddr = NULL; - } - /* SHIM */ - ctx->shim_phy_add = pci_resource_start(pci, 1); - ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); - if (!ctx->shim) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); - - /* Shared SRAM */ - ctx->mailbox_add = pci_resource_start(pci, 2); - ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); - if (!ctx->mailbox) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); - - /* IRAM */ - ctx->iram_end = pci_resource_end(pci, 3); - ctx->iram_base = pci_resource_start(pci, 3); - ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); - if (!ctx->iram) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); - - /* DRAM */ - ctx->dram_end = pci_resource_end(pci, 4); - ctx->dram_base = pci_resource_start(pci, 4); - ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); - if (!ctx->dram) { - ret = -EINVAL; - goto do_release_regions; - } - dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); -do_release_regions: - pci_release_regions(pci); - return 0; -} -/* -* intel_sst_probe - PCI probe function -* -* @pci: PCI device structure -* @pci_id: PCI device ID structure -* -*/ -static int intel_sst_probe(struct pci_dev *pci, - const struct pci_device_id *pci_id) -{ - int ret = 0; - struct intel_sst_drv *sst_drv_ctx; - struct sst_platform_info *sst_pdata = pci->dev.platform_data; - - dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); - - ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); - if (ret < 0) - return ret; - - sst_drv_ctx->pdata = sst_pdata; - sst_drv_ctx->irq_num = pci->irq; - - ret = sst_context_init(sst_drv_ctx); - if (ret < 0) - goto do_free_drv_ctx; - - - /* Init the device */ - ret = pcim_enable_device(pci); - if (ret) { - dev_err(sst_drv_ctx->dev, - "device can't be enabled. Returned err: %d\n", ret); - goto do_destroy_wq; - } - sst_drv_ctx->pci = pci_dev_get(pci); - - ret = sst_platform_get_resources(sst_drv_ctx); - if (ret < 0) - goto do_destroy_wq; - - sst_set_fw_state_locked(sst_drv_ctx, SST_RESET); - snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), - "%s%04x%s", "fw_sst_", - sst_drv_ctx->dev_id, ".bin"); - dev_dbg(sst_drv_ctx->dev, - "Requesting FW %s now...\n", sst_drv_ctx->firmware_name); - ret = request_firmware_nowait(THIS_MODULE, 1, - sst_drv_ctx->firmware_name, sst_drv_ctx->dev, - GFP_KERNEL, sst_drv_ctx, sst_firmware_load_cb); - - if (ret) { - dev_err(sst_drv_ctx->dev, - "Firmware load failed with error: %d\n", ret); - goto do_release_regions; - } - - - pci_set_drvdata(pci, sst_drv_ctx); - sst_configure_runtime_pm(sst_drv_ctx); - sst_register(sst_drv_ctx->dev); - - return ret; - -do_release_regions: - pci_release_regions(pci); -do_destroy_wq: - destroy_workqueue(sst_drv_ctx->post_msg_wq); -do_free_drv_ctx: - dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); - return ret; -} - -/** -* intel_sst_remove - PCI remove function -* -* @pci: PCI device structure -* -* This function is called by OS when a device is unloaded -* This frees the interrupt etc -*/ -static void intel_sst_remove(struct pci_dev *pci) -{ - struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); - - sst_context_cleanup(sst_drv_ctx); - pci_dev_put(sst_drv_ctx->pci); - pci_release_regions(pci); - pci_set_drvdata(pci, NULL); -} +EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
static int intel_sst_runtime_suspend(struct device *dev) { @@ -546,27 +385,8 @@ static int intel_sst_runtime_resume(struct device *dev) return ret; }
-static const struct dev_pm_ops intel_sst_pm = { +const struct dev_pm_ops intel_sst_pm = { .runtime_suspend = intel_sst_runtime_suspend, .runtime_resume = intel_sst_runtime_resume, }; - -/* PCI Routines */ -static struct pci_device_id intel_sst_ids[] = { - { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, - { 0, } -}; - -static struct pci_driver sst_driver = { - .name = SST_DRV_NAME, - .id_table = intel_sst_ids, - .probe = intel_sst_probe, - .remove = intel_sst_remove, -#ifdef CONFIG_PM - .driver = { - .pm = &intel_sst_pm, - }, -#endif -}; - -module_pci_driver(sst_driver); +EXPORT_SYMBOL_GPL(intel_sst_pm); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index b65b9c0..3ee555e 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -40,6 +40,7 @@ #define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 #define MRFLD_FW_BSS_RESET_BIT 0
+extern const struct dev_pm_ops intel_sst_pm; enum sst_states { SST_FW_LOADING = 1, SST_FW_RUNNING, @@ -537,4 +538,9 @@ void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, int sst_register(struct device *); int sst_unregister(struct device *);
+int sst_alloc_drv_context(struct intel_sst_drv **ctx, + struct device *dev, unsigned int dev_id); +int sst_context_init(struct intel_sst_drv *ctx); +void sst_context_cleanup(struct intel_sst_drv *ctx); +void sst_configure_runtime_pm(struct intel_sst_drv *ctx); #endif diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c new file mode 100644 index 0000000..3a0b3bf --- /dev/null +++ b/sound/soc/intel/sst/sst_pci.c @@ -0,0 +1,209 @@ +/* + * sst_pci.c - SST (LPE) driver init file for pci enumeration. + * + * Copyright (C) 2008-14 Intel Corp + * Authors: Vinod Koul vinod.koul@intel.com + * Harsha Priya priya.harsha@intel.com + * Dharageswari R dharageswari.r@intel.com + * KP Jeeja jeeja.kp@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/fs.h> +#include <linux/firmware.h> +#include <linux/pm_runtime.h> +#include <sound/core.h> +#include <sound/soc.h> +#include <asm/platform_sst_audio.h> +#include "../sst-mfld-platform.h" +#include "sst.h" + +static int sst_platform_get_resources(struct intel_sst_drv *ctx) +{ + int ddr_base, ret = 0; + struct pci_dev *pci = ctx->pci; + + ret = pci_request_regions(pci, SST_DRV_NAME); + if (ret) + return ret; + + /* map registers */ + /* DDR base */ + if (ctx->dev_id == SST_MRFLD_PCI_ID) { + ctx->ddr_base = pci_resource_start(pci, 0); + /* check that the relocated IMR base matches with FW Binary */ + ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base); + if (!ctx->pdata->lib_info) { + dev_err(ctx->dev, "lib_info pointer NULL\n"); + ret = -EINVAL; + goto do_release_regions; + } + if (ddr_base != ctx->pdata->lib_info->mod_base) { + dev_err(ctx->dev, + "FW LSP DDR BASE does not match with IFWI\n"); + ret = -EINVAL; + goto do_release_regions; + } + ctx->ddr_end = pci_resource_end(pci, 0); + + ctx->ddr = pcim_iomap(pci, 0, + pci_resource_len(pci, 0)); + if (!ctx->ddr) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr); + } else { + ctx->ddr = NULL; + } + /* SHIM */ + ctx->shim_phy_add = pci_resource_start(pci, 1); + ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1)); + if (!ctx->shim) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim); + + /* Shared SRAM */ + ctx->mailbox_add = pci_resource_start(pci, 2); + ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2)); + if (!ctx->mailbox) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox); + + /* IRAM */ + ctx->iram_end = pci_resource_end(pci, 3); + ctx->iram_base = pci_resource_start(pci, 3); + ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3)); + if (!ctx->iram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram); + + /* DRAM */ + ctx->dram_end = pci_resource_end(pci, 4); + ctx->dram_base = pci_resource_start(pci, 4); + ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4)); + if (!ctx->dram) { + ret = -EINVAL; + goto do_release_regions; + } + dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram); +do_release_regions: + pci_release_regions(pci); + return 0; +} + +/* + * intel_sst_probe - PCI probe function + * + * @pci: PCI device structure + * @pci_id: PCI device ID structure + * + */ +static int intel_sst_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int ret = 0; + struct intel_sst_drv *sst_drv_ctx; + struct sst_platform_info *sst_pdata = pci->dev.platform_data; + + dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device); + ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device); + if (ret < 0) + return ret; + + sst_drv_ctx->pdata = sst_pdata; + sst_drv_ctx->irq_num = pci->irq; + snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name), + "%s%04x%s", "fw_sst_", + sst_drv_ctx->dev_id, ".bin"); + + ret = sst_context_init(sst_drv_ctx); + if (ret < 0) + return ret; + + /* Init the device */ + ret = pcim_enable_device(pci); + if (ret) { + dev_err(sst_drv_ctx->dev, + "device can't be enabled. Returned err: %d\n", ret); + goto do_free_drv_ctx; + } + sst_drv_ctx->pci = pci_dev_get(pci); + ret = sst_platform_get_resources(sst_drv_ctx); + if (ret < 0) + goto do_free_drv_ctx; + + pci_set_drvdata(pci, sst_drv_ctx); + sst_configure_runtime_pm(sst_drv_ctx); + + return ret; + +do_free_drv_ctx: + sst_context_cleanup(sst_drv_ctx); + dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret); + return ret; +} + +/** + * intel_sst_remove - PCI remove function + * + * @pci: PCI device structure + * + * This function is called by OS when a device is unloaded + * This frees the interrupt etc + */ +static void intel_sst_remove(struct pci_dev *pci) +{ + struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); + + sst_context_cleanup(sst_drv_ctx); + pci_dev_put(sst_drv_ctx->pci); + pci_release_regions(pci); + pci_set_drvdata(pci, NULL); +} + +/* PCI Routines */ +static struct pci_device_id intel_sst_ids[] = { + { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, + { 0, } +}; + +static struct pci_driver sst_driver = { + .name = SST_DRV_NAME, + .id_table = intel_sst_ids, + .probe = intel_sst_probe, + .remove = intel_sst_remove, +#ifdef CONFIG_PM + .driver = { + .pm = &intel_sst_pm, + }, +#endif +}; + +module_pci_driver(sst_driver); + +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver"); +MODULE_AUTHOR("Vinod Koul vinod.koul@intel.com"); +MODULE_AUTHOR("Harsha Priya priya.harsha@intel.com"); +MODULE_AUTHOR("Dharageswari R dharageswari.r@intel.com"); +MODULE_AUTHOR("KP Jeeja jeeja.kp@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c index 1c2e081..9a5df19 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/sst/sst_pvt.c @@ -433,6 +433,7 @@ u32 relocate_imr_addr_mrfld(u32 base_addr) base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); return base_addr; } +EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, struct ipc_post *msg)
On Tue, Nov 04, 2014 at 04:25:16PM +0530, Vinod Koul wrote:
Now the SST_IPC will support both ACPI and PCI, separate into core module and PCI module. This also move probe function into PCI module and exports the required symbols from core module
Applied, thanks.
In ACPI platform we need to save few registers of Shim on suspend and restore them on resume, so add handlers to do this
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/sst/sst.c | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 8753754..b97c231 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -339,6 +339,34 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) } EXPORT_SYMBOL_GPL(sst_context_cleanup);
+static inline void sst_save_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + + shim_regs->imrx = sst_shim_read64(shim, SST_IMRX), + + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +static inline void sst_restore_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + + /* + * we only need to restore IMRX for this case, rest will be + * initialize by FW or driver when firmware is loaded + */ + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
On Tue, Nov 04, 2014 at 04:25:17PM +0530, Vinod Koul wrote:
In ACPI platform we need to save few registers of Shim on suspend and restore them on resume, so add handlers to do this
Applied, thanks.
Add the last ACPI module support which also uses core module like the PCI part
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Kconfig | 4 + sound/soc/intel/sst/Makefile | 4 +- sound/soc/intel/sst/sst.c | 20 ++ sound/soc/intel/sst/sst.h | 3 +- sound/soc/intel/sst/sst_acpi.c | 377 ++++++++++++++++++++++++++++++++++++++++ sound/soc/intel/sst/sst_pvt.c | 2 + 6 files changed, 406 insertions(+), 4 deletions(-) create mode 100644 sound/soc/intel/sst/sst_acpi.c
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c963a5d..edff4a3 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -20,6 +20,10 @@ config SND_SST_IPC_PCI tristate select SND_SST_IPC
+config SND_SST_IPC_ACPI + tristate + select SND_SST_IPC + config SND_SOC_INTEL_SST tristate "ASoC support for Intel(R) Smart Sound Technology" select SND_SOC_INTEL_SST_ACPI if ACPI diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile index b8aa1d3..fd21726 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/sst/Makefile @@ -1,7 +1,7 @@ snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o snd-intel-sst-pci-objs += sst_pci.o - +snd-intel-sst-acpi-objs += sst_acpi.o
obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o - +obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index b97c231..c17a79b 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c @@ -181,6 +181,7 @@ int sst_driver_ops(struct intel_sst_drv *sst)
switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: sst->tstamp = SST_TIME_STAMP_MRFLD; sst->ops = &mrfld_ops; return 0; @@ -323,7 +324,11 @@ EXPORT_SYMBOL_GPL(sst_context_init); void sst_context_cleanup(struct intel_sst_drv *ctx) { pm_runtime_get_noresume(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI) + pm_runtime_disable(ctx->dev); +#else pm_runtime_forbid(ctx->dev); +#endif sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); flush_scheduled_work(); @@ -371,8 +376,19 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); pm_runtime_use_autosuspend(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI) + /* + * For acpi devices, the actual physical device state is + * initially active. So change the state to active before + * enabling the pm + */ + pm_runtime_set_active(ctx->dev); + pm_runtime_enable(ctx->dev); + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); +#else pm_runtime_allow(ctx->dev); pm_runtime_put_noidle(ctx->dev); +#endif } EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
@@ -395,6 +411,10 @@ static int intel_sst_runtime_suspend(struct device *dev) synchronize_irq(ctx->irq_num); flush_workqueue(ctx->post_msg_wq);
+ /* save the shim registers because PMC doesn't save state */ + if (ctx->dev_id == SST_BYT_ACPI_ID) + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); + return ret; }
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h index 3ee555e..683dc71 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/sst/sst.h @@ -29,6 +29,7 @@ /* driver names */ #define SST_DRV_NAME "intel_sst_driver" #define SST_MRFLD_PCI_ID 0x119A +#define SST_BYT_ACPI_ID 0x80860F28
#define SST_SUSPEND_DELAY 2000 #define FW_CONTEXT_MEM (64*1024) @@ -508,8 +509,6 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst, size_t mbox_data_len, const void *mbox_data, void **data, bool large, bool fill_dsp, bool sync, bool response);
-void sst_save_shim64(struct intel_sst_drv *ctx, void __iomem *shim, - struct sst_shim_regs64 *shim_regs); void sst_process_pending_msg(struct work_struct *work); int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx); void sst_init_stream(struct stream_info *stream, diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c new file mode 100644 index 0000000..1f5fde2 --- /dev/null +++ b/sound/soc/intel/sst/sst_acpi.c @@ -0,0 +1,377 @@ +/* + * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. + * + * Copyright (c) 2013, Intel Corporation. + * + * Authors: Ramesh Babu K V Ramesh.Babu@intel.com + * Authors: Omair Mohammed Abdullah omair.m.abdullah@intel.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/firmware.h> +#include <linux/pm_runtime.h> +#include <linux/pm_qos.h> +#include <linux/acpi.h> +#include <asm/platform_sst_audio.h> +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/compress_driver.h> +#include <acpi/acbuffer.h> +#include <acpi/platform/acenv.h> +#include <acpi/platform/aclinux.h> +#include <acpi/actypes.h> +#include <acpi/acpi_bus.h> +#include "../sst-mfld-platform.h" +#include "../sst-dsp.h" +#include "sst.h" + +struct sst_machines { + char codec_id[32]; + char board[32]; + char machine[32]; + void (*machine_quirk)(void); + char firmware[32]; + struct sst_platform_info *pdata; + +}; + +/* LPE viewpoint addresses */ +#define SST_BYT_IRAM_PHY_START 0xff2c0000 +#define SST_BYT_IRAM_PHY_END 0xff2d4000 +#define SST_BYT_DRAM_PHY_START 0xff300000 +#define SST_BYT_DRAM_PHY_END 0xff320000 +#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */ +#define SST_BYT_IMR_VIRT_END 0xc01fffff +#define SST_BYT_SHIM_PHY_ADDR 0xff340000 +#define SST_BYT_MBOX_PHY_ADDR 0xff344000 +#define SST_BYT_DMA0_PHY_ADDR 0xff298000 +#define SST_BYT_DMA1_PHY_ADDR 0xff29c000 +#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000 +#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000 + +#define BYT_FW_MOD_TABLE_OFFSET 0x80000 +#define BYT_FW_MOD_TABLE_SIZE 0x100 +#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE) + +static const struct sst_info byt_fwparse_info = { + .use_elf = false, + .max_streams = 25, + .iram_start = SST_BYT_IRAM_PHY_START, + .iram_end = SST_BYT_IRAM_PHY_END, + .iram_use = true, + .dram_start = SST_BYT_DRAM_PHY_START, + .dram_end = SST_BYT_DRAM_PHY_END, + .dram_use = true, + .imr_start = SST_BYT_IMR_VIRT_START, + .imr_end = SST_BYT_IMR_VIRT_END, + .imr_use = true, + .mailbox_start = SST_BYT_MBOX_PHY_ADDR, + .num_probes = 0, + .lpe_viewpt_rqd = true, +}; + +static const struct sst_ipc_info byt_ipc_info = { + .ipc_offset = 0, + .mbox_recv_off = 0x400, +}; + +static const struct sst_lib_dnld_info byt_lib_dnld_info = { + .mod_base = SST_BYT_IMR_VIRT_START, + .mod_end = SST_BYT_IMR_VIRT_END, + .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET, + .mod_table_size = BYT_FW_MOD_TABLE_SIZE, + .mod_ddr_dnld = false, +}; + +static const struct sst_res_info byt_rvp_res_info = { + .shim_offset = 0x140000, + .shim_size = 0x000100, + .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR, + .ssp0_offset = 0xa0000, + .ssp0_size = 0x1000, + .dma0_offset = 0x98000, + .dma0_size = 0x4000, + .dma1_offset = 0x9c000, + .dma1_size = 0x4000, + .iram_offset = 0x0c0000, + .iram_size = 0x14000, + .dram_offset = 0x100000, + .dram_size = 0x28000, + .mbox_offset = 0x144000, + .mbox_size = 0x1000, + .acpi_lpe_res_index = 0, + .acpi_ddr_index = 2, + .acpi_ipc_irq_index = 5, +}; + +struct sst_platform_info byt_rvp_platform_data = { + .probe_data = &byt_fwparse_info, + .ipc_info = &byt_ipc_info, + .lib_info = &byt_lib_dnld_info, + .res_info = &byt_rvp_res_info, + .platform = "sst-mfld-platform", +}; + +#if IS_ENABLED(CONFIG_ACPI) +static int sst_platform_get_resources(struct intel_sst_drv *ctx) +{ + struct resource *rsrc; + struct platform_device *pdev = to_platform_device(ctx->dev); + + /* All ACPI resource request here */ + /* Get Shim addr */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, + ctx->pdata->res_info->acpi_lpe_res_index); + if (!rsrc) { + dev_err(ctx->dev, "Invalid SHIM base from IFWI"); + return -EIO; + } + dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start, + (unsigned int)resource_size(rsrc)); + + ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset; + ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1; + dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base); + ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, + ctx->pdata->res_info->iram_size); + if (!ctx->iram) { + dev_err(ctx->dev, "unable to map IRAM"); + return -EIO; + } + + ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset; + ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1; + dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base); + ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, + ctx->pdata->res_info->dram_size); + if (!ctx->dram) { + dev_err(ctx->dev, "unable to map DRAM"); + return -EIO; + } + + ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset; + dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add); + ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, + ctx->pdata->res_info->shim_size); + if (!ctx->shim) { + dev_err(ctx->dev, "unable to map SHIM"); + return -EIO; + } + + /* reassign physical address to LPE viewpoint address */ + ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr; + + /* Get mailbox addr */ + ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset; + dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add); + ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, + ctx->pdata->res_info->mbox_size); + if (!ctx->mailbox) { + dev_err(ctx->dev, "unable to map mailbox"); + return -EIO; + } + + /* reassign physical address to LPE viewpoint address */ + ctx->mailbox_add = ctx->info.mailbox_start; + + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, + ctx->pdata->res_info->acpi_ddr_index); + if (!rsrc) { + dev_err(ctx->dev, "Invalid DDR base from IFWI"); + return -EIO; + } + ctx->ddr_base = rsrc->start; + ctx->ddr_end = rsrc->end; + dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base); + ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, + resource_size(rsrc)); + if (!ctx->ddr) { + dev_err(ctx->dev, "unable to map DDR"); + return -EIO; + } + + /* Find the IRQ */ + ctx->irq_num = platform_get_irq(pdev, + ctx->pdata->res_info->acpi_ipc_irq_index); + return 0; +} + +static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + *(bool *)context = true; + return AE_OK; +} + +static struct sst_machines *sst_acpi_find_machine( + struct sst_machines *machines) +{ + struct sst_machines *mach; + bool found = false; + + for (mach = machines; mach->codec_id; mach++) + if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id, + sst_acpi_mach_match, + &found, NULL)) && found) + return mach; + + return NULL; +} + +int sst_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + struct intel_sst_drv *ctx; + const struct acpi_device_id *id; + struct sst_machines *mach; + struct platform_device *mdev; + struct platform_device *plat_dev; + unsigned int dev_id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + dev_dbg(dev, "for %s", id->id); + + mach = (struct sst_machines *)id->driver_data; + mach = sst_acpi_find_machine(mach); + if (mach == NULL) { + dev_err(dev, "No matching machine driver found\n"); + return -ENODEV; + } + + ret = kstrtouint(id->id, 16, &dev_id); + if (ret < 0) { + dev_err(dev, "Unique device id conversion error: %d\n", ret); + return ret; + } + + dev_dbg(dev, "ACPI device id: %x\n", dev_id); + + plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0); + if (plat_dev == NULL) { + dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform); + return -ENODEV; + } + + /* Create platform device for sst machine driver */ + mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0); + if (mdev == NULL) { + dev_err(dev, "Failed to create machine device: %s\n", mach->machine); + return -ENODEV; + } + + ret = sst_alloc_drv_context(&ctx, dev, dev_id); + if (ret < 0) + return ret; + + /* Fill sst platform data */ + ctx->pdata = mach->pdata; + strcpy(ctx->firmware_name, mach->firmware); + + ret = sst_platform_get_resources(ctx); + if (ret) + return ret; + + ret = sst_context_init(ctx); + if (ret < 0) + return ret; + + /* need to save shim registers in BYT */ + ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), + GFP_KERNEL); + if (!ctx->shim_regs64) { + return -ENOMEM; + goto do_sst_cleanup; + } + + sst_configure_runtime_pm(ctx); + platform_set_drvdata(pdev, ctx); + return ret; + +do_sst_cleanup: + sst_context_cleanup(ctx); + platform_set_drvdata(pdev, NULL); + dev_err(ctx->dev, "failed with %d\n", ret); + return ret; +} + +/** +* intel_sst_remove - remove function +* +* @pdev: platform device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +int sst_acpi_remove(struct platform_device *pdev) +{ + struct intel_sst_drv *ctx; + + ctx = platform_get_drvdata(pdev); + sst_context_cleanup(ctx); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#else +int sst_acpi_probe(struct platform_device *pdev) +{ + return -EINVAL; +} + +int sst_acpi_remove(struct platform_device *pdev) +{ + return -EINVAL; +} +#endif + +static struct sst_machines sst_acpi_bytcr[] = { + {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin", + &byt_rvp_platform_data }, + {}, +}; + +static const struct acpi_device_id sst_acpi_ids[] = { + { "80860F28", (unsigned long)&sst_acpi_bytcr}, + { }, +}; + +static struct platform_driver sst_acpi_driver = { + .driver = { + .name = "intel_sst_acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(sst_acpi_ids), + .pm = &intel_sst_pm, + }, + .probe = sst_acpi_probe, + .remove = sst_acpi_remove, +}; + +module_platform_driver(sst_acpi_driver); + +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver"); +MODULE_AUTHOR("Ramesh Babu K V"); +MODULE_AUTHOR("Omair Mohammed Abdullah"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c index 9a5df19..4b77208 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/sst/sst_pvt.c @@ -117,6 +117,7 @@ unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: val = sst_shim_read64(sst->shim, addr); break; } @@ -128,6 +129,7 @@ void write_shim_data(struct intel_sst_drv *sst, int addr, { switch (sst->dev_id) { case SST_MRFLD_PCI_ID: + case SST_BYT_ACPI_ID: sst_shim_write64(sst->shim, addr, (u64) data); break; }
On Tue, Nov 04, 2014 at 04:25:18PM +0530, Vinod Koul wrote:
@@ -323,7 +324,11 @@ EXPORT_SYMBOL_GPL(sst_context_init); void sst_context_cleanup(struct intel_sst_drv *ctx) { pm_runtime_get_noresume(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI)
- pm_runtime_disable(ctx->dev);
+#else pm_runtime_forbid(ctx->dev); +#endif sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); flush_scheduled_work();
No, we should be able to build a kernel which runs with or without ACPI - make this a runtime check if it's required (a comment might be in order too since this is a weird thing to do).
@@ -371,8 +376,19 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); pm_runtime_use_autosuspend(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI)
- /*
* For acpi devices, the actual physical device state is
* initially active. So change the state to active before
* enabling the pm
*/
- pm_runtime_set_active(ctx->dev);
- pm_runtime_enable(ctx->dev);
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+#else pm_runtime_allow(ctx->dev); pm_runtime_put_noidle(ctx->dev); +#endif
The general idiom for runtime PM is that we should default to bringing the device to power up anyway, that way the kernel continues to work if runtime PM has been disabled.
- /* save the shim registers because PMC doesn't save state */
- if (ctx->dev_id == SST_BYT_ACPI_ID)
sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
Is there any harm in doing this unconditionally?
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
void *context, void **ret)
+{
- *(bool *)context = true;
- return AE_OK;
+}
+static struct sst_machines *sst_acpi_find_machine(
- struct sst_machines *machines)
+{
- struct sst_machines *mach;
- bool found = false;
- for (mach = machines; mach->codec_id; mach++)
if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
sst_acpi_mach_match,
&found, NULL)) && found)
return mach;
acpi_get_devices() seems painful.
+#else +int sst_acpi_probe(struct platform_device *pdev) +{
- return -EINVAL;
+}
+int sst_acpi_remove(struct platform_device *pdev) +{
- return -EINVAL;
+} +#endif
Why not just not build this file if !CONFIG_ACPI?
+static struct sst_machines sst_acpi_bytcr[] = {
- {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
&byt_rvp_platform_data },
- {},
+};
Yay. Much win. Not that there's much that can be done at this point.
On Thu, Nov 06, 2014 at 12:43:49PM +0000, Mark Brown wrote:
On Tue, Nov 04, 2014 at 04:25:18PM +0530, Vinod Koul wrote:
@@ -323,7 +324,11 @@ EXPORT_SYMBOL_GPL(sst_context_init); void sst_context_cleanup(struct intel_sst_drv *ctx) { pm_runtime_get_noresume(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI)
- pm_runtime_disable(ctx->dev);
+#else pm_runtime_forbid(ctx->dev); +#endif sst_unregister(ctx->dev); sst_set_fw_state_locked(ctx, SST_SHUTDOWN); flush_scheduled_work();
No, we should be able to build a kernel which runs with or without ACPI
- make this a runtime check if it's required (a comment might be in
order too since this is a weird thing to do).
yes that is doable by adding a parameter and setting that in specfic probe, will add.
@@ -371,8 +376,19 @@ void sst_configure_runtime_pm(struct intel_sst_drv *ctx) { pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY); pm_runtime_use_autosuspend(ctx->dev); +#if IS_ENABLED(CONFIG_ACPI)
- /*
* For acpi devices, the actual physical device state is
* initially active. So change the state to active before
* enabling the pm
*/
- pm_runtime_set_active(ctx->dev);
- pm_runtime_enable(ctx->dev);
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+#else pm_runtime_allow(ctx->dev); pm_runtime_put_noidle(ctx->dev); +#endif
The general idiom for runtime PM is that we should default to bringing the device to power up anyway, that way the kernel continues to work if runtime PM has been disabled.
ok
- /* save the shim registers because PMC doesn't save state */
- if (ctx->dev_id == SST_BYT_ACPI_ID)
sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
Is there any harm in doing this unconditionally?
Shouldnt be, let me verify
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
void *context, void **ret)
+{
- *(bool *)context = true;
- return AE_OK;
+}
+static struct sst_machines *sst_acpi_find_machine(
- struct sst_machines *machines)
+{
- struct sst_machines *mach;
- bool found = false;
- for (mach = machines; mach->codec_id; mach++)
if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
sst_acpi_mach_match,
&found, NULL)) && found)
return mach;
acpi_get_devices() seems painful.
+#else +int sst_acpi_probe(struct platform_device *pdev) +{
- return -EINVAL;
+}
+int sst_acpi_remove(struct platform_device *pdev) +{
- return -EINVAL;
+} +#endif
Why not just not build this file if !CONFIG_ACPI?
Yes, anway this is only for ACPI systems.
+static struct sst_machines sst_acpi_bytcr[] = {
- {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
&byt_rvp_platform_data },
- {},
+};
Yay. Much win. Not that there's much that can be done at this point.
Yup, hopefully _DSD migration will help us here
On Thu, Nov 06, 2014 at 06:39:09PM +0530, Vinod Koul wrote:
On Thu, Nov 06, 2014 at 12:43:49PM +0000, Mark Brown wrote:
On Tue, Nov 04, 2014 at 04:25:18PM +0530, Vinod Koul wrote:
No, we should be able to build a kernel which runs with or without ACPI
- make this a runtime check if it's required (a comment might be in
order too since this is a weird thing to do).
yes that is doable by adding a parameter and setting that in specfic probe, will add.
There's a variable acpi_disabled you can check.
On Thu, Nov 06, 2014 at 01:44:26PM +0000, Mark Brown wrote:
On Thu, Nov 06, 2014 at 06:39:09PM +0530, Vinod Koul wrote:
On Thu, Nov 06, 2014 at 12:43:49PM +0000, Mark Brown wrote:
On Tue, Nov 04, 2014 at 04:25:18PM +0530, Vinod Koul wrote:
No, we should be able to build a kernel which runs with or without ACPI
- make this a runtime check if it's required (a comment might be in
order too since this is a weird thing to do).
yes that is doable by adding a parameter and setting that in specfic probe, will add.
There's a variable acpi_disabled you can check.
Thanks, thats a very good suggestion indeed :)
From: Subhransu S. Prusty subhransu.s.prusty@intel.com
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Kconfig | 12 ++ sound/soc/intel/Makefile | 2 + sound/soc/intel/bytcr_dpcm_rt5640.c | 258 +++++++++++++++++++++++++++++++++++ 3 files changed, 272 insertions(+), 0 deletions(-) create mode 100644 sound/soc/intel/bytcr_dpcm_rt5640.c
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index edff4a3..a8ccc3d 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -85,3 +85,15 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y if you have such a device If unsure select "N". + +config SND_SOC_INTEL_BYTCR_RT5640_MACH + tristate "ASoC Audio DSP Support for MID BYT Platform" + depends on X86 + select SND_SOC_RT5640 + select SND_SST_MFLD_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) MID Baytrail platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 9ab43be..fbde4b0 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -26,11 +26,13 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o +snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
# DSP driver obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c new file mode 100644 index 0000000..8a15bce --- /dev/null +++ b/sound/soc/intel/bytcr_dpcm_rt5640.c @@ -0,0 +1,258 @@ +/* + * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform + * + * Copyright (C) 2014 Intel Corp + * Author: Subhransu S. Prusty subhransu.s.prusty@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../codecs/rt5640.h" +#include "sst-atom-controls.h" + +static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route byt_audio_map[] = { + {"IN2P", NULL, "Headset Mic"}, + {"IN2N", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "MICBIAS1"}, + {"IN1P", NULL, "MICBIAS1"}, + {"LDO2", NULL, "Int Mic"}, + {"Headphone", NULL, "HPOL"}, + {"Headphone", NULL, "HPOR"}, + {"Ext Spk", NULL, "SPOLP"}, + {"Ext Spk", NULL, "SPOLN"}, + {"Ext Spk", NULL, "SPORP"}, + {"Ext Spk", NULL, "SPORN"}, + + {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + {"ssp2 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_kcontrol_new byt_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int byt_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + if (strncmp(codec_dai->name, "rt5640-aif1", 11)) + return 0; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec clock %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + snd_soc_dai_set_bclk_ratio(codec_dai, 50); + return 0; +} + +static const struct snd_soc_pcm_stream byt_dai_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; +static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int byt_aif1_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops byt_aif1_ops = { + .startup = byt_aif1_startup, +}; + +static struct snd_soc_ops byt_be_ssp2_ops = { + .hw_params = byt_aif1_hw_params, +}; + +static struct snd_soc_dai_link byt_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Baytrail Audio Port", + .stream_name = "Baytrail Audio", + .cpu_dai_name = "media-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + .ignore_suspend = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &byt_aif1_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Baytrail Compressed Port", + .stream_name = "Baytrail Compress", + .cpu_dai_name = "compress-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-mfld-platform", + }, + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-mfld-platform", + .no_pcm = 1, + .codec_dai_name = "rt5640-aif1", + .codec_name = "i2c-10EC5640:00", + .dai_fmt = SND_SOC_DAIFMT_I2S| SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = byt_codec_fixup, + .ignore_suspend = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &byt_be_ssp2_ops, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int snd_byt_prepare(struct device *dev) +{ + return snd_soc_suspend(dev); +} + +static void snd_byt_complete(struct device *dev) +{ + snd_soc_resume(dev); +} + +static int snd_byt_poweroff(struct device *dev) +{ + return snd_soc_poweroff(dev); +} +#else +#define snd_byt_prepare NULL +#define snd_byt_complete NULL +#define snd_byt_poweroff NULL +#endif + +/* SoC card */ +static struct snd_soc_card snd_soc_card_byt = { + .name = "baytrailcraudio", + .dai_link = byt_dailink, + .num_links = ARRAY_SIZE(byt_dailink), + .dapm_widgets = byt_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets), + .dapm_routes = byt_audio_map, + .num_dapm_routes = ARRAY_SIZE(byt_audio_map), + .controls = byt_mc_controls, + .num_controls = ARRAY_SIZE(byt_mc_controls), +}; + +static int snd_byt_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + + /* register the soc card */ + snd_soc_card_byt.dev = &pdev->dev; + + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt); + if (ret_val) { + dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, &snd_soc_card_byt); + return ret_val; +} + +static const struct dev_pm_ops snd_byt_mc_pm_ops = { + .prepare = snd_byt_prepare, + .complete = snd_byt_complete, + .poweroff = snd_byt_poweroff, +}; + +static struct platform_driver snd_byt_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "bytt100_rt5640", + .pm = &snd_byt_mc_pm_ops, + }, + .probe = snd_byt_mc_probe, +}; + +module_platform_driver(snd_byt_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); +MODULE_AUTHOR("Subhransu S. Prusty subhransu.s.prusty@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bytrt5640-audio");
On Tue, Nov 04, 2014 at 04:25:19PM +0530, Vinod Koul wrote:
+static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
- if (strncmp(codec_dai->name, "rt5640-aif1", 11))
return 0;
This looks wrong... fairly sure I queried this on an earlier version of the patch and was told it wasn't required.
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
params_rate(params) * 512,
SND_SOC_CLOCK_IN);
- if (ret < 0) {
dev_err(rtd->dev, "can't set codec clock %d\n", ret);
return ret;
- }
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
Missing blank line here and in several places throughout the file. I'd expect the PLL to be enabled before the sysclk is told to use it, error checking might kick in otherwise.
+#ifdef CONFIG_PM_SLEEP +static int snd_byt_prepare(struct device *dev) +{
- return snd_soc_suspend(dev);
+}
+static void snd_byt_complete(struct device *dev) +{
- snd_soc_resume(dev);
+}
+static int snd_byt_poweroff(struct device *dev) +{
- return snd_soc_poweroff(dev);
+} +#else +#define snd_byt_prepare NULL +#define snd_byt_complete NULL +#define snd_byt_poweroff NULL +#endif
Don't bother with the wrapper functions, they're not adding anything. Why are we using prepare() and complete() here - other machine drivers don't need to do that? Comments might be helpful...
On Thu, Nov 06, 2014 at 12:48:54PM +0000, Mark Brown wrote:
On Tue, Nov 04, 2014 at 04:25:19PM +0530, Vinod Koul wrote:
+static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
- if (strncmp(codec_dai->name, "rt5640-aif1", 11))
return 0;
This looks wrong... fairly sure I queried this on an earlier version of the patch and was told it wasn't required.
This was supposed to be removed, not sure why it creeped back in, will fix now
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
params_rate(params) * 512,
SND_SOC_CLOCK_IN);
- if (ret < 0) {
dev_err(rtd->dev, "can't set codec clock %d\n", ret);
return ret;
- }
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
Missing blank line here and in several places throughout the file. I'd expect the PLL to be enabled before the sysclk is told to use it, error checking might kick in otherwise.
ok
+#ifdef CONFIG_PM_SLEEP +static int snd_byt_prepare(struct device *dev) +{
- return snd_soc_suspend(dev);
+}
+static void snd_byt_complete(struct device *dev) +{
- snd_soc_resume(dev);
+}
+static int snd_byt_poweroff(struct device *dev) +{
- return snd_soc_poweroff(dev);
+} +#else +#define snd_byt_prepare NULL +#define snd_byt_complete NULL +#define snd_byt_poweroff NULL +#endif
Don't bother with the wrapper functions, they're not adding anything. Why are we using prepare() and complete() here - other machine drivers don't need to do that? Comments might be helpful...
due to I2C. We have seen that codec is resumed but I2C is still not ready causing i2c failures, so moving to complete and prepare helps. I will add this comment. Will remove wrappers.
Thanks
On Thu, Nov 06, 2014 at 06:41:42PM +0530, Vinod Koul wrote:
On Thu, Nov 06, 2014 at 12:48:54PM +0000, Mark Brown wrote:
Don't bother with the wrapper functions, they're not adding anything. Why are we using prepare() and complete() here - other machine drivers don't need to do that? Comments might be helpful...
due to I2C. We have seen that codec is resumed but I2C is still not ready causing i2c failures, so moving to complete and prepare helps. I will add this comment. Will remove wrappers.
That doesn't sound right - this would affect almost all systems. Deferred probe should ensure that the machine driver can't load until after all the component devices (including the CODEC driver) are instantiated and we should be suspending in the reverse order that we probe the devices.
On Thu, Nov 06, 2014 at 01:46:00PM +0000, Mark Brown wrote:
On Thu, Nov 06, 2014 at 06:41:42PM +0530, Vinod Koul wrote:
On Thu, Nov 06, 2014 at 12:48:54PM +0000, Mark Brown wrote:
Don't bother with the wrapper functions, they're not adding anything. Why are we using prepare() and complete() here - other machine drivers don't need to do that? Comments might be helpful...
due to I2C. We have seen that codec is resumed but I2C is still not ready causing i2c failures, so moving to complete and prepare helps. I will add this comment. Will remove wrappers.
That doesn't sound right - this would affect almost all systems. Deferred probe should ensure that the machine driver can't load until after all the component devices (including the CODEC driver) are instantiated and we should be suspending in the reverse order that we probe the devices.
Yes defer probe is the right solution here. But in machine driver loading how does it ensure that codec and platform are already loaded, should the sound card registration be treated as failure and deferred?
Thanks
On Mon, Nov 10, 2014 at 11:50:56AM +0530, Vinod Koul wrote:
On Thu, Nov 06, 2014 at 01:46:00PM +0000, Mark Brown wrote:
That doesn't sound right - this would affect almost all systems. Deferred probe should ensure that the machine driver can't load until after all the component devices (including the CODEC driver) are instantiated and we should be suspending in the reverse order that we probe the devices.
Yes defer probe is the right solution here. But in machine driver loading how does it ensure that codec and platform are already loaded, should the sound card registration be treated as failure and deferred?
Yes, the sound card registration will give you an -EPROBE_DEFER if one of the components isn't registered.
This patch adds machine driver support for Merrifield platform. Patch has dependency on Merrifield DSP driver.
Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/intel/Kconfig | 15 ++ sound/soc/intel/Makefile | 2 + sound/soc/intel/merr_dpcm_wm8958.c | 416 ++++++++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+), 0 deletions(-) create mode 100644 sound/soc/intel/merr_dpcm_wm8958.c
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a8ccc3d..06401d8 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -97,3 +97,18 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH used as alsa device in audio substem in Intel(R) MID devices Say Y if you have such a device If unsure select "N". + +config SND_SOC_INTEL_MRFLD_WM8958_MACH + tristate "SOC Machine Audio driver for Intel Merrifield MID platform" + depends on X86 + select SND_SOC_WM8994 + select MFD_CORE + select MFD_WM8994 + select REGULATOR_WM8994 + select SND_SST_MFLD_PLATFORM + select SND_SST_IPC_PCI + help + This adds support for ASoC machine driver for Intel(R) MID Merrifield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index fbde4b0..f23ff6a 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -27,12 +27,14 @@ snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o +snd-soc-sst-merr-dpcm-wm8958-objs := merr_dpcm_wm8958.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o +obj-$(CONFIG_SND_SOC_INTEL_MRFLD_WM8958_MACH) += snd-soc-sst-merr-dpcm-wm8958.o
# DSP driver obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/merr_dpcm_wm8958.c b/sound/soc/intel/merr_dpcm_wm8958.c new file mode 100644 index 0000000..4a53211 --- /dev/null +++ b/sound/soc/intel/merr_dpcm_wm8958.c @@ -0,0 +1,416 @@ +/* + * merr_dpcm_wm8958.c - ASoc DPCM Machine driver for Intel Merrfield MID platform + * + * Copyright (C) 2013-14 Intel Corp + * Author: Vinod Koul vinod.koul@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/async.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <linux/input.h> + +#include <linux/mfd/wm8994/core.h> +#include <linux/mfd/wm8994/registers.h> +#include <linux/mfd/wm8994/pdata.h> +#include "../codecs/wm8994.h" +#include "sst-atom-controls.h" + +/* Codec PLL output clk rate */ +#define CODEC_SYSCLK_RATE 24576000 +/* Input clock to codec at MCLK1 PIN */ +#define CODEC_IN_MCLK1_RATE 19200000 +/* Input clock to codec at MCLK2 PIN */ +#define CODEC_IN_MCLK2_RATE 32768 +/* define to select between MCLK1 and MCLK2 input to codec as its clock */ +#define CODEC_IN_MCLK1 1 +#define CODEC_IN_MCLK2 2 +#define CODEC_BE 1 + +struct mrfld_8958_mc_private { + struct snd_soc_jack jack; + int jack_retry; +}; + +/* + * Function to switch the input clock for codec, When audio is in + * progress input clock to codec will be through MCLK1 which is 19.2MHz + * while in off state input clock to codec will be through 32KHz through + * MCLK2 + * card : Sound card structure + * src : Input clock source to codec + */ +static int mrfld_8958_set_codec_clk(struct snd_soc_card *card, int src) +{ + int ret; + struct snd_soc_dai *aif1_dai = card->rtd[CODEC_BE].codec_dai; + + if (!aif1_dai) + return -ENODEV; + + switch (src) { + case CODEC_IN_MCLK1: + /* Turn ON the PLL to generate required sysclk rate + * from MCLK1 + */ + ret = snd_soc_dai_set_pll(aif1_dai, + WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + CODEC_IN_MCLK1_RATE, CODEC_SYSCLK_RATE); + if (ret < 0) { + dev_err(card->dev, "Failed to start FLL: %d\n", ret); + return ret; + } + /* Switch to MCLK1 input */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, + CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Failed to set codec sysclk configuration %d\n", + ret); + return ret; + } + break; + case CODEC_IN_MCLK2: + /* Switch to MCLK2 */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Failed to switch to MCLK2: %d\n", ret); + return ret; + } + /* Turn off PLL for MCLK1 */ + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); + if (ret < 0) { + dev_err(card->dev, "Failed to stop the FLL: %d", ret); + return ret; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int mrfld_wm8958_set_clk(struct snd_soc_dai *codec_dai) +{ + int ret = 0; + struct snd_soc_card *card = codec_dai->card; + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xf0, 0xf0, 4, 24); + if (ret < 0) { + dev_err(card->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK1); + + return ret; +} + +static int mrfld_8958_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return mrfld_wm8958_set_clk(codec_dai); +} + +static const struct snd_soc_pcm_stream mrfld_wm8958_dai_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int merr_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static int mrfld_8958_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + int ret = 0; + /* "wm8994-aif1" */ + struct snd_soc_dai *aif1_dai = card->rtd[CODEC_BE].codec_dai; + + if (!aif1_dai) + return -ENODEV; + + if (dapm->dev != aif1_dai->dev) + return 0; + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) + + ret = mrfld_wm8958_set_clk(aif1_dai); + break; + default: + break; + } + return ret; +} + +static int mrfld_8958_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + int ret = 0; + /* "wm8994-aif1" */ + struct snd_soc_dai *aif1_dai = card->rtd[CODEC_BE].codec_dai; + + if (!aif1_dai) + return -ENODEV; + + if (dapm->dev != aif1_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* + * We are in standby down so Switch to 32KHz MCLK2 input + * clock for codec + */ + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK2); + break; + default: + break; + } + if (&card->dapm == dapm) + card->dapm.bias_level = level; + return ret; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), +}; + +static const struct snd_soc_dapm_route map[] = { + { "Headphones", NULL, "HPOUT1L" }, + { "Headphones", NULL, "HPOUT1R" }, + + /* + * This machine uses 2 DMICs, other configs may use more so change + * below accordingly + */ + { "DMIC1DAT", NULL, "DMIC" }, + { "DMIC2DAT", NULL, "DMIC" }, + + /* + * MICBIAS2 is connected as Bias for AMIC so we link it + * here. Also AMIC wires up to IN1LP pin. + * DMIC is externally connected to 1.8V rail, so no link rqd. + */ + { "AMIC", NULL, "MICBIAS2" }, + { "IN1LP", NULL, "AMIC" }, + + /* dsp map link the SWM outs to codec AIF */ + { "AIF1 Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec_out0"}, + { "ssp2 Tx", NULL, "codec_out1"}, + { "codec_in0", NULL, "ssp2 Rx" }, + { "codec_in1", NULL, "ssp2 Rx" }, + { "ssp2 Rx", NULL, "AIF1 Capture"}, + +}; + +static int mrfld_8958_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *aif1_dai = card->rtd[CODEC_BE].codec_dai; + struct snd_soc_codec *codec = aif1_dai->codec; + + if (!aif1_dai) + return -ENODEV; + + ret = snd_soc_dai_set_tdm_slot(aif1_dai, 0xf0, 0xf0, 4, 24); + if (ret < 0) { + dev_err(card->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + if (!codec) { + dev_err(card->dev, "%s: we didnt find the codec pointer!\n", __func__); + return -ENODEV; + } + + ctx->jack_retry = 0; + ret = snd_soc_jack_new(codec, "Intel MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1, + &ctx->jack); + if (ret) { + dev_err(card->dev, "jack creation failed: %d\n", ret); + return ret; + } + + /* the WM8958 codec reports two keys BTN_0/1 for headset key + * presses. This mostly happens due to varying impendance + * measurement, so report both as media key for getting good + * performance + */ + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_1, KEY_MEDIA); + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + + wm8958_mic_detect(codec, &ctx->jack, NULL, NULL, NULL, NULL); + + return 0; +} + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; +static int mrfld_8958_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops mrfld_8958_ops = { + .startup = mrfld_8958_startup, +}; + +static struct snd_soc_ops mrfld_8958_be_ssp2_ops = { + .hw_params = mrfld_8958_hw_params, +}; + +struct snd_soc_dai_link mrfld_8958_msic_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Merrifield Audio Port", + .stream_name = "Merrifield Audio", + .cpu_dai_name = "Headset-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "sst-platform", + .init = mrfld_8958_init, + .ignore_suspend = 1, + .dynamic = 1, + .ops = &mrfld_8958_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Merrifield Compress Port", + .stream_name = "Merrifield Compress", + .platform_name = "sst-platform", + .cpu_dai_name = "Compress-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dynamic = 1, + .init = mrfld_8958_init, + }, + + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-port", + .platform_name = "sst-platform", + .no_pcm = 1, + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = merr_codec_fixup, + .ignore_suspend = 1, + .ops = &mrfld_8958_be_ssp2_ops, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mrfld = { + .name = "wm8958-audio", + .dai_link = mrfld_8958_msic_dailink, + .num_links = ARRAY_SIZE(mrfld_8958_msic_dailink), + .set_bias_level = mrfld_8958_set_bias_level, + .set_bias_level_post = mrfld_8958_set_bias_level_post, + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), + .fully_routed = true, +}; + +static int snd_mrfld_8958_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct mrfld_8958_mc_private *drv; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + /* register the soc card */ + snd_soc_card_mrfld.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_mrfld, drv); + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mrfld); + if (ret_val) { + dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_mrfld); + return ret_val; + +unalloc: + return ret_val; +} + +static struct platform_driver snd_mrfld_8958_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mrfld_wm8958", + }, + .probe = snd_mrfld_8958_mc_probe, +}; +module_platform_driver(snd_mrfld_8958_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Merrifield MID Machine driver"); +MODULE_AUTHOR("Vinod Koul vinod.koul@intel.com"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mrfld_wm8958");
On Tue, Nov 04, 2014 at 04:25:20PM +0530, Vinod Koul wrote:
+static int mrfld_wm8958_set_clk(struct snd_soc_dai *codec_dai) +{
- int ret = 0;
- struct snd_soc_card *card = codec_dai->card;
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xf0, 0xf0, 4, 24);
- if (ret < 0) {
dev_err(card->dev, "can't set codec pcm format %d\n", ret);
return ret;
- }
We already set this on init - why do we need to set it each time here too?
On Thu, Nov 06, 2014 at 12:51:19PM +0000, Mark Brown wrote:
On Tue, Nov 04, 2014 at 04:25:20PM +0530, Vinod Koul wrote:
+static int mrfld_wm8958_set_clk(struct snd_soc_dai *codec_dai) +{
- int ret = 0;
- struct snd_soc_card *card = codec_dai->card;
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xf0, 0xf0, 4, 24);
- if (ret < 0) {
dev_err(card->dev, "can't set codec pcm format %d\n", ret);
return ret;
- }
We already set this on init - why do we need to set it each time here too?
It is not required, will remove.
-- ~Vinod
participants (2)
-
Mark Brown
-
Vinod Koul