From 178ff7c6f3916aff3c3eaaec8636be3b41e93011 Mon Sep 17 00:00:00 2001 From: John Lin Date: Mon, 15 Feb 2016 10:40:17 +0800 Subject: ASoC: rt5645: Add dmi_system_id "Google Setzer" Add platform specific data for Setzer project. Signed-off-by: John Lin Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 7af5e73..dff706a 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3557,6 +3557,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), }, }, + { + .ident = "Google Setzer", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"), + }, + }, { } }; -- cgit v0.10.2 From 612047f0baefe2aeef1bc5ad8c7107a532b7d957 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 28 Mar 2016 14:29:22 +0100 Subject: ASoC: wm_adsp: Fix some subtle races on compressed stream Firstly, we should be locking the pwr_lock when we initialise the compressed buffer. Secondly, fixup a couple of places when we should be pulling pointers only under the pwr_lock as they may be affected by operations that take that lock. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d3b1cb1..4839d19 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2240,9 +2240,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + mutex_lock(&dsp->pwr_lock); + if (wm_adsp_fw[dsp->fw].num_caps != 0) ret = wm_adsp_buffer_init(dsp); + mutex_unlock(&dsp->pwr_lock); + break; case SND_SOC_DAPM_PRE_PMD: @@ -2814,12 +2818,15 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) { - struct wm_adsp_compr_buf *buf = dsp->buffer; - struct wm_adsp_compr *compr = dsp->compr; + struct wm_adsp_compr_buf *buf; + struct wm_adsp_compr *compr; int ret = 0; mutex_lock(&dsp->pwr_lock); + buf = dsp->buffer; + compr = dsp->compr; + if (!buf) { ret = -ENODEV; goto out; @@ -2879,14 +2886,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, struct snd_compr_tstamp *tstamp) { struct wm_adsp_compr *compr = stream->runtime->private_data; - struct wm_adsp_compr_buf *buf = compr->buf; struct wm_adsp *dsp = compr->dsp; + struct wm_adsp_compr_buf *buf; int ret = 0; adsp_dbg(dsp, "Pointer request\n"); mutex_lock(&dsp->pwr_lock); + buf = compr->buf; + if (!compr->buf) { ret = -ENXIO; goto out; -- cgit v0.10.2 From 33d740e07d1f565e44d35e7f7756a619b4f1e4ba Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 28 Mar 2016 14:29:21 +0100 Subject: ASoC: wm_adsp: Show avail in bytes to match other messages All other debug messages talk about data on the compressed stream in bytes except avail which is shown in words. To avoid confusion show avail in bytes as well. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 4839d19..953c427 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2809,7 +2809,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) avail += wm_adsp_buffer_size(buf); adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", - buf->read_index, write_index, avail); + buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); buf->avail = avail; -- cgit v0.10.2 From 9abe3dc77ea7ccad1c2112257bb352435dcee0ff Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 28 Mar 2016 14:29:23 +0100 Subject: ASoC: cs47l24: Fix a couple of small whitespace errors Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 576087b..383700a 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1157,6 +1157,7 @@ static struct snd_compr_ops cs47l24_compr_ops = { static struct snd_soc_platform_driver cs47l24_compr_platform = { .compr_ops = &cs47l24_compr_ops, }; + static int cs47l24_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -1225,9 +1226,9 @@ static int cs47l24_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register platform: %d\n", ret); return ret; } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); - if (ret < 0) { dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); snd_soc_unregister_platform(&pdev->dev); -- cgit v0.10.2 From c13202f7d7101a6f5542f3a31b9a6787ae7b746c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 28 Mar 2016 14:29:24 +0100 Subject: ASoC: cs47l24: Add support for audio trace firmware cs47l24 supports the audio trace firmware, this streams of audio to be captured from the CODEC over a compressed audio channel for analysis/debugging of audio processing firmwares. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 383700a..6b8b557 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { { "IN2L PGA", NULL, "IN2L" }, { "IN2R PGA", NULL, "IN2R" }, + { "Audio Trace DSP", NULL, "DSP2" }, + { "Audio Trace DSP", NULL, "SYSCLK" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), @@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .formats = CS47L24_FORMATS, }, }, + { + .name = "cs47l24-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .compress_new = snd_soc_new_compress, + }, + { + .name = "cs47l24-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, }; static int cs47l24_open(struct snd_compr_stream *stream) @@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream) if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) { n_adsp = 2; + } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) { + n_adsp = 1; } else { dev_err(arizona->dev, "No suitable compressed stream for DAI '%s'\n", @@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) { struct cs47l24_priv *priv = data; struct arizona *arizona = priv->core.arizona; - int ret; + int serviced = 0; + int i, ret; + + for (i = 1; i <= 2; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + } - ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]); - if (ret == -ENODEV) { + if (!serviced) { dev_err(arizona->dev, "Spurious compressed data IRQ\n"); return IRQ_NONE; } -- cgit v0.10.2 From f17131a93f43665a76ae1f6aebdbb3e41674137c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 29 Mar 2016 09:45:00 -0700 Subject: ASoC: intel: add function stub when ACPI is not enabled Add function stub for "sst_acpi_find_name_from_hid()" when CONFIG_ACPI is not enabled so that the driver will build successfully. This fixes the following build errors: (loadable module) ERROR: "sst_acpi_find_name_from_hid" [sound/soc/intel/boards/snd-soc-sst-bytcr-rt5640.ko] undefined! (or built-in) bytcr_rt5640.c:(.text+0x26fc52): undefined reference to `sst_acpi_find_name_from_hid' Reported-by: Borislav Petkov Signed-off-by: Randy Dunlap Signed-off-by: Mark Brown diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h index 4dcfb7e..8398cb2 100644 --- a/sound/soc/intel/common/sst-acpi.h +++ b/sound/soc/intel/common/sst-acpi.h @@ -12,10 +12,19 @@ * */ +#include +#include #include /* translation fron HID to I2C name, needed for DAI codec_name */ +#if IS_ENABLED(CONFIG_ACPI) const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); +#else +inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + return NULL; +} +#endif /* acpi match */ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); -- cgit v0.10.2 From 92eb4f62cbac0211e43ee4a6715ee2ea43167e88 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 11 Mar 2016 10:12:56 +0530 Subject: ASoC: Intel: Bxtn: Add Broxton DSP support Broxton DSP is mostly similar to Skylake one but with subtle differences like no Code Load DMA and uses HDA DMA for code loading, DSP D0 and D3 sequences are different. These changes are comprehended by adding different DSP power up and down handlers, and new loader ops and also adding prepare and trigger which HDA DSP DMA requires Signed-off-by: Jeeja KP Signed-off-by: Jayachandran B Signed-off-by: GuruprasadX Pawse Signed-off-by: Kranthi G Signed-off-by: Dharageswari R Signed-off-by: Ramesh Babu Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3e6c23..5a94f74 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -162,6 +162,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH config SND_SOC_INTEL_SKYLAKE tristate select SND_HDA_EXT_CORE + select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_HDA_I915 select SND_SOC_INTEL_SST diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 914b6da..c28f5d0 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \ - skl-sst.o + skl-sst.o bxt-sst.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c new file mode 100644 index 0000000..965ce40 --- /dev/null +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -0,0 +1,328 @@ +/* + * bxt-sst.c - DSP library functions for BXT platform + * + * Copyright (C) 2015-16 Intel Corp + * Author:Rafal Redzimski + * Jeeja KP + * + * 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 +#include +#include +#include + +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" + +#define BXT_BASEFW_TIMEOUT 3000 +#define BXT_INIT_TIMEOUT 500 +#define BXT_IPC_PURGE_FW 0x01004000 + +#define BXT_ROM_INIT 0x5 +#define BXT_ADSP_SRAM0_BASE 0x80000 + +/* Firmware status window */ +#define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE +#define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) + +#define BXT_ADSP_SRAM1_BASE 0xA0000 + +static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) +{ + return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); +} + +static int sst_bxt_prepare_fw(struct sst_dsp *ctx, + const void *fwdata, u32 fwsize) +{ + int stream_tag, ret, i; + u32 reg; + + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + if (stream_tag < 0) { + dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", + stream_tag); + return stream_tag; + } + + ctx->dsp_ops.stream_tag = stream_tag; + memcpy(ctx->dmab.area, fwdata, fwsize); + + /* Purge FW request */ + sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | + BXT_IPC_PURGE_FW | (stream_tag - 1)); + + ret = skl_dsp_enable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret); + ret = -EIO; + goto base_fw_load_failed; + } + + for (i = BXT_INIT_TIMEOUT; i > 0; --i) { + reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE); + + if (reg & SKL_ADSP_REG_HIPCIE_DONE) { + sst_dsp_shim_update_bits_forced(ctx, + SKL_ADSP_REG_HIPCIE, + SKL_ADSP_REG_HIPCIE_DONE, + SKL_ADSP_REG_HIPCIE_DONE); + break; + } + mdelay(1); + } + if (!i) { + dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg); + sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE, + SKL_ADSP_REG_HIPCIE_DONE, + SKL_ADSP_REG_HIPCIE_DONE); + } + + /* enable Interrupt */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + + for (i = BXT_INIT_TIMEOUT; i > 0; --i) { + if (SKL_FW_INIT == + (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) & + SKL_FW_STS_MASK)) { + + dev_info(ctx->dev, "ROM loaded, continue FW loading\n"); + break; + } + mdelay(1); + } + if (!i) { + dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg); + ret = -EIO; + goto base_fw_load_failed; + } + + return ret; + +base_fw_load_failed: + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + skl_dsp_disable_core(ctx); + return ret; +} + +static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) +{ + int ret; + + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, + BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); + + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + + return ret; +} + +static int bxt_load_base_firmware(struct sst_dsp *ctx) +{ + const struct firmware *fw = NULL; + struct skl_sst *skl = ctx->thread_context; + int ret; + + ret = request_firmware(&fw, ctx->fw_name, ctx->dev); + if (ret < 0) { + dev_err(ctx->dev, "Request firmware failed %d\n", ret); + goto sst_load_base_firmware_failed; + } + + ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + /* Retry Enabling core and ROM load. Retry seemed to help */ + if (ret < 0) { + ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size); + if (ret < 0) { + dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); + goto sst_load_base_firmware_failed; + } + } + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); + dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + skl_dsp_disable_core(ctx); + } else { + dev_dbg(ctx->dev, "Firmware download successful\n"); + ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n"); + skl_dsp_disable_core(ctx); + ret = -EIO; + } else { + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + ret = 0; + } + } + +sst_load_base_firmware_failed: + release_firmware(fw); + return ret; +} + +static int bxt_set_dsp_D0(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + int ret; + + skl->boot_complete = false; + + ret = skl_dsp_enable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret); + return ret; + } + + /* enable interrupt */ + skl_ipc_int_enable(ctx); + skl_ipc_op_int_enable(ctx); + + ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, + msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); + if (ret == 0) { + dev_err(ctx->dev, "ipc: error DSP boot timeout\n"); + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + return -EIO; + } + + skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING); + return 0; +} + +static int bxt_set_dsp_D3(struct sst_dsp *ctx) +{ + struct skl_ipc_dxstate_info dx; + struct skl_sst *skl = ctx->thread_context; + int ret = 0; + + if (!is_skl_dsp_running(ctx)) + return ret; + + dx.core_mask = SKL_DSP_CORE0_MASK; + dx.dx_mask = SKL_IPC_D3_MASK; + + ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, &dx); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret); + return ret; + } + + ret = skl_dsp_disable_core(ctx); + if (ret < 0) { + dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret); + ret = -EIO; + } + + skl_dsp_set_state_locked(ctx, SKL_DSP_RESET); + return 0; +} + +static struct skl_dsp_fw_ops bxt_fw_ops = { + .set_state_D0 = bxt_set_dsp_D0, + .set_state_D3 = bxt_set_dsp_D3, + .load_fw = bxt_load_base_firmware, + .get_fw_errcode = bxt_get_errorcode, +}; + +static struct sst_ops skl_ops = { + .irq_handler = skl_dsp_sst_interrupt, + .write = sst_shim32_write, + .read = sst_shim32_read, + .ram_read = sst_memcpy_fromio_32, + .ram_write = sst_memcpy_toio_32, + .free = skl_dsp_free, +}; + +static struct sst_dsp_device skl_dev = { + .thread = skl_dsp_irq_thread_handler, + .ops = &skl_ops, +}; + +int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp) +{ + struct skl_sst *skl; + struct sst_dsp *sst; + int ret; + + skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL); + if (skl == NULL) + return -ENOMEM; + + skl->dev = dev; + skl_dev.thread_context = skl; + + skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); + if (!skl->dsp) { + dev_err(skl->dev, "skl_dsp_ctx_init failed\n"); + return -ENODEV; + } + + sst = skl->dsp; + sst->fw_name = fw_name; + sst->dsp_ops = dsp_ops; + sst->fw_ops = bxt_fw_ops; + sst->addr.lpe = mmio_base; + sst->addr.shim = mmio_base; + + sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), + SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + + ret = skl_ipc_init(dev, skl); + if (ret) + return ret; + + skl->boot_complete = false; + init_waitqueue_head(&skl->boot_wait); + + ret = sst->fw_ops.load_fw(sst); + if (ret < 0) { + dev_err(dev, "Load base fw failed: %x", ret); + return ret; + } + + if (dsp) + *dsp = skl; + + return 0; +} +EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); + + +void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) +{ + skl_ipc_free(&ctx->ipc); + ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); + + if (ctx->dsp->addr.lpe) + iounmap(ctx->dsp->addr.lpe); + + ctx->dsp->ops->free(ctx->dsp); +} +EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Broxton IPC driver"); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 79c5089..e3d149c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -72,6 +72,105 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable) skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask); } +static int skl_dsp_setup_spib(struct device *dev, unsigned int size, + int stream_tag, int enable) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_stream *stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + struct hdac_ext_stream *estream; + + if (!stream) + return -EINVAL; + + estream = stream_to_hdac_ext_stream(stream); + /* enable/disable SPIB for this hdac stream */ + snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index); + + /* set the spib value */ + snd_hdac_ext_stream_set_spib(ebus, estream, size); + + return 0; +} + +static int skl_dsp_prepare(struct device *dev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *estream; + struct hdac_stream *stream; + struct snd_pcm_substream substream; + int ret; + + if (!bus) + return -ENODEV; + + memset(&substream, 0, sizeof(substream)); + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + + estream = snd_hdac_ext_stream_assign(ebus, &substream, + HDAC_EXT_STREAM_TYPE_HOST); + if (!estream) + return -ENODEV; + + stream = hdac_stream(estream); + + /* assign decouple host dma channel */ + ret = snd_hdac_dsp_prepare(stream, format, size, dmab); + if (ret < 0) + return ret; + + skl_dsp_setup_spib(dev, size, stream->stream_tag, true); + + return stream->stream_tag; +} + +static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_stream *stream; + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + if (!stream) + return -EINVAL; + + snd_hdac_dsp_trigger(stream, start); + + return 0; +} + +static int skl_dsp_cleanup(struct device *dev, + struct snd_dma_buffer *dmab, int stream_tag) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dev); + struct hdac_stream *stream; + struct hdac_ext_stream *estream; + struct hdac_bus *bus = ebus_to_hbus(ebus); + + if (!bus) + return -ENODEV; + + stream = snd_hdac_get_stream(bus, + SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + if (!stream) + return -EINVAL; + + estream = stream_to_hdac_ext_stream(stream); + skl_dsp_setup_spib(dev, 0, stream_tag, false); + snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST); + + snd_hdac_dsp_cleanup(stream, dmab); + + return 0; +} + static struct skl_dsp_loader_ops skl_get_loader_ops(void) { struct skl_dsp_loader_ops loader_ops; @@ -84,6 +183,21 @@ static struct skl_dsp_loader_ops skl_get_loader_ops(void) return loader_ops; }; +static struct skl_dsp_loader_ops bxt_get_loader_ops(void) +{ + struct skl_dsp_loader_ops loader_ops; + + memset(&loader_ops, 0, sizeof(loader_ops)); + + loader_ops.alloc_dma_buf = skl_alloc_dma_buf; + loader_ops.free_dma_buf = skl_free_dma_buf; + loader_ops.prepare = skl_dsp_prepare; + loader_ops.trigger = skl_dsp_trigger; + loader_ops.cleanup = skl_dsp_cleanup; + + return loader_ops; +}; + static const struct skl_dsp_ops dsp_ops[] = { { .id = 0x9d70, @@ -91,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = skl_sst_dsp_init, .cleanup = skl_sst_dsp_cleanup }, + { + .id = 0x5a98, + .loader_ops = bxt_get_loader_ops, + .init = bxt_sst_dsp_init, + .cleanup = bxt_sst_dsp_cleanup + }, }; static int skl_get_dsp_ops(int pci_id) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index b6e310d..ff31e66 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -124,10 +124,19 @@ struct skl_dsp_fw_ops { }; struct skl_dsp_loader_ops { + int stream_tag; + int (*alloc_dma_buf)(struct device *dev, struct snd_dma_buffer *dmab, size_t size); int (*free_dma_buf)(struct device *dev, struct snd_dma_buffer *dmab); + int (*prepare)(struct device *dev, unsigned int format, + unsigned int byte_size, + struct snd_dma_buffer *bufp); + int (*trigger)(struct device *dev, bool start, int stream_tag); + + int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab, + int stream_tag); }; struct skl_load_module_info { @@ -160,6 +169,10 @@ int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp); +int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); +void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ -- cgit v0.10.2 From 613c7c4003c8338a9a638485d95de2775948295b Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Tue, 5 Apr 2016 18:08:02 +0100 Subject: ASoC: dwc: Unmask I2S interrupts only for enabled channels There is no need to unmask all interrupts at I2S start. This can cause performance issues in slower platforms. Unmask only the interrupts for the used channels. Signed-off-by: Jose Abreu Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index bff258d..3effcd1 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -147,17 +147,18 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { + struct i2s_clk_config_data *config = &dev->config; u32 i, irq; i2s_write_reg(dev->i2s_base, IER, 1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - for (i = 0; i < 4; i++) { + for (i = 0; i < (config->chan_nr / 2); i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30); } i2s_write_reg(dev->i2s_base, ITER, 1); } else { - for (i = 0; i < 4; i++) { + for (i = 0; i < (config->chan_nr / 2); i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03); } -- cgit v0.10.2 From 9771b18a0b374b6e6ecfa84c8b59d5ef79e969b1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 6 Apr 2016 11:21:53 +0100 Subject: ASoC: wm_adsp: Factor out fetching of stream errors from the DSP Factor out the reading of the DSP error flag into its own function to support further improvements to the code. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 953c427..f70c609 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2816,6 +2816,23 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) return 0; } +static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) +{ + int ret; + + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); + if (ret < 0) { + adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); + return ret; + } + if (buf->error != 0) { + adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); + return -EIO; + } + + return 0; +} + int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) { struct wm_adsp_compr_buf *buf; @@ -2834,16 +2851,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) adsp_dbg(dsp, "Handling buffer IRQ\n"); - ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); - if (ret < 0) { - adsp_err(dsp, "Failed to check buffer error: %d\n", ret); - goto out; - } - if (buf->error != 0) { - adsp_err(dsp, "Buffer error occurred: %d\n", buf->error); - ret = -EIO; + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) goto out; - } ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), &buf->irq_count); -- cgit v0.10.2 From 5847609edb3c80be07e897e449a9bb579a0fe9d8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 6 Apr 2016 11:21:54 +0100 Subject: ASoC: wm_adsp: Improve DSP error handling If we encounter an error on the DSP side whilst user-space is waiting on the poll we should call snd_compr_fragment_elapsed, although data is not actually available we want to wake user-space such that the error can be propagated out quickly. Additionally some versions of the DSP firmware are not super consistent about actually generating an IRQ if they encounter an error, as such we will check the DSP error status every time we run out of available data as well, to ensure we catch it. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f70c609..3ac2e1f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2853,7 +2853,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) ret = wm_adsp_buffer_get_error(buf); if (ret < 0) - goto out; + goto out_notify; /* Wake poll to report error */ ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), &buf->irq_count); @@ -2868,6 +2868,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) goto out; } +out_notify: if (compr && compr->stream) snd_compr_fragment_elapsed(compr->stream); @@ -2928,6 +2929,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream, * DSP to inform us once a whole fragment is available. */ if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out; + ret = wm_adsp_buffer_reenable_irq(buf); if (ret < 0) { adsp_err(dsp, -- cgit v0.10.2 From 4a4436573a6669516f73bac25016683d396ed4c4 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 31 Mar 2016 16:35:58 +0300 Subject: ALSA: pcm: add IEC958 channel status helper for hw_params Add IEC958 channel status helper that gets the audio properties from snd_pcm_hw_params instead of snd_pcm_runtime. This is needed to produce the channel status bits already in audio stream configuration phase. Signed-off-by: Jyri Sarha Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/include/sound/pcm_iec958.h b/include/sound/pcm_iec958.h index 0eed397..36f023a 100644 --- a/include/sound/pcm_iec958.h +++ b/include/sound/pcm_iec958.h @@ -6,4 +6,6 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, size_t len); +int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params, + u8 *cs, size_t len); #endif diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c index 36b2d7a..e016871 100644 --- a/sound/core/pcm_iec958.c +++ b/sound/core/pcm_iec958.c @@ -9,30 +9,18 @@ #include #include #include +#include #include -/** - * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status - * @runtime: pcm runtime structure with ->rate filled in - * @cs: channel status buffer, at least four bytes - * @len: length of channel status buffer - * - * Create the consumer format channel status data in @cs of maximum size - * @len corresponding to the parameters of the PCM runtime @runtime. - * - * Drivers may wish to tweak the contents of the buffer after creation. - * - * Returns: length of buffer, or negative error code if something failed. - */ -int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, - size_t len) +static int create_iec958_consumer(uint rate, uint sample_width, + u8 *cs, size_t len) { unsigned int fs, ws; if (len < 4) return -EINVAL; - switch (runtime->rate) { + switch (rate) { case 32000: fs = IEC958_AES3_CON_FS_32000; break; @@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, } if (len > 4) { - switch (snd_pcm_format_width(runtime->format)) { + switch (sample_width) { case 16: ws = IEC958_AES4_CON_WORDLEN_20_16; break; @@ -92,4 +80,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, return len; } + +/** + * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status + * @runtime: pcm runtime structure with ->rate filled in + * @cs: channel status buffer, at least four bytes + * @len: length of channel status buffer + * + * Create the consumer format channel status data in @cs of maximum size + * @len corresponding to the parameters of the PCM runtime @runtime. + * + * Drivers may wish to tweak the contents of the buffer after creation. + * + * Returns: length of buffer, or negative error code if something failed. + */ +int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs, + size_t len) +{ + return create_iec958_consumer(runtime->rate, + snd_pcm_format_width(runtime->format), + cs, len); +} EXPORT_SYMBOL(snd_pcm_create_iec958_consumer); + +/** + * snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status + * @hw_params: the hw_params instance for extracting rate and sample format + * @cs: channel status buffer, at least four bytes + * @len: length of channel status buffer + * + * Create the consumer format channel status data in @cs of maximum size + * @len corresponding to the parameters of the PCM runtime @runtime. + * + * Drivers may wish to tweak the contents of the buffer after creation. + * + * Returns: length of buffer, or negative error code if something failed. + */ +int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params, + u8 *cs, size_t len) +{ + return create_iec958_consumer(params_rate(params), params_width(params), + cs, len); +} +EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params); -- cgit v0.10.2 From 4a462ce084d5beb92cfc68f53f88c035c82e6b59 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 31 Mar 2016 16:35:59 +0300 Subject: ALSA: pcm: Allow 32 bit sample format in IEC958 channel status helper Treat 32 bit sample width as if it was 24 bits when generating IEC958 channel status bits. On some platforms 24 sample width is problematic and to get full 24 bit precision a 32 bit format, using only the 24 most significant bits, may have to be used. Signed-off-by: Jyri Sarha Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c index e016871..5e6aed6 100644 --- a/sound/core/pcm_iec958.c +++ b/sound/core/pcm_iec958.c @@ -59,6 +59,7 @@ static int create_iec958_consumer(uint rate, uint sample_width, IEC958_AES4_CON_MAX_WORDLEN_24; break; case 24: + case 32: /* Assume 24-bit width for 32-bit samples. */ ws = IEC958_AES4_CON_WORDLEN_24_20 | IEC958_AES4_CON_MAX_WORDLEN_24; break; -- cgit v0.10.2 From 3fafd14d9422c46f5c2a142298384dc15dbf88b2 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Thu, 7 Apr 2016 17:53:57 +0100 Subject: ASoC: dwc: Use fifo depth to program FCR This patch makes Designware I2S driver use the fifo depth value to program the fifo configuration register instead of using hardcoded values. Signed-off-by: Jose Abreu Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 3effcd1..0db69b7 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -100,6 +100,7 @@ struct dw_i2s_dev { struct device *dev; u32 ccr; u32 xfer_resolution; + u32 fifo_th; /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; @@ -232,14 +233,16 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) if (stream == SNDRV_PCM_STREAM_PLAYBACK) { i2s_write_reg(dev->i2s_base, TCR(ch_reg), dev->xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); + i2s_write_reg(dev->i2s_base, TFCR(ch_reg), + dev->fifo_th - 1); irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); } else { i2s_write_reg(dev->i2s_base, RCR(ch_reg), dev->xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); + i2s_write_reg(dev->i2s_base, RFCR(ch_reg), + dev->fifo_th - 1); irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); @@ -499,6 +502,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, */ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2); + u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); u32 idx; if (dev->capability & DWC_I2S_RECORD && @@ -537,6 +541,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dev->capability |= DW_I2S_SLAVE; } + dev->fifo_th = fifo_depth / 2; return 0; } -- cgit v0.10.2 From 43b27d7286737d9af9ebeff0219e38560cb31748 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 8 Apr 2016 16:50:14 +0100 Subject: ASoC: arizona: Do not create OUT4R widget for CS47L24/WM1831 The CS47L24 and WM1831 codecs only use the OUT4L widget so we can skip creation of the OUT4R widget. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 92d22a0..d8a6823 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -221,6 +221,8 @@ int arizona_init_spk(struct snd_soc_codec *codec) switch (arizona->type) { case WM8997: + case CS47L24: + case WM1831: break; default: ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); -- cgit v0.10.2 From e92077c3f45395881ad8c690bb86a85ffe5198ba Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 12 Apr 2016 22:51:03 +0200 Subject: ASoC: fsl: imx-pcm-fiq: use correct format specifier Documentation/printk-formats.txt has size_t: use %zu or %zx runtime->dma_bytes is of type size_t. Signed-off-by: Heinrich Schuchardt Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index e63cd5e..dac6688 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -220,7 +220,7 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, ret = dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret, + pr_debug("%s: ret: %d %p %pad 0x%08zx\n", __func__, ret, runtime->dma_area, &runtime->dma_addr, runtime->dma_bytes); -- cgit v0.10.2 From 896491b304f956d3e87208a242db4fdfa952cdc5 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 13 Apr 2016 01:54:01 +0200 Subject: ASoC: au1x: use correct format specifier Documentation/printk-formats.txt has unsigned long: use %lu or %lx size_t: use %zu or %zx runtime->dma_bytes is of type size_t. runtime->min_align is of type unsigned long. Signed-off-by: Heinrich Schuchardt Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c index 5741c0a..b5d1caa 100644 --- a/sound/soc/au1x/dbdma2.c +++ b/sound/soc/au1x/dbdma2.c @@ -206,8 +206,8 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, stype = substream->stream; pcd = to_dmadata(substream); - DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d " - "runtime->min_align %d\n", + DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu " + "runtime->min_align %lu\n", (unsigned long)runtime->dma_area, (unsigned long)runtime->dma_addr, runtime->dma_bytes, runtime->min_align); -- cgit v0.10.2 From 22225835e23f7a768e767967004d2f3751c64be5 Mon Sep 17 00:00:00 2001 From: Petr Kulhavy Date: Mon, 18 Apr 2016 14:32:40 +0200 Subject: ASoC: davinci-mcbsp: add binding for McBSP Add devicetree binding for the TI DA850/OMAP-L138/AM18xx MultiChannel Buffered Serial Port (McBSP) The optional register range "dat" is not implemented at the moment. The current driver supports only DMA into RX/TX registers but no FIFO. Once the FIFO is implemented in the driver the "dat" range will be used. Signed-off-by: Petr Kulhavy Reviewed-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt b/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt new file mode 100644 index 0000000..55b53e1 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt @@ -0,0 +1,51 @@ +Texas Instruments DaVinci McBSP module +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This binding describes the "Multi-channel Buffered Serial Port" (McBSP) +audio interface found in some TI DaVinci processors like the OMAP-L138 or AM180x. + + +Required properties: +~~~~~~~~~~~~~~~~~~~~ +- compatible : + "ti,da850-mcbsp" : for DA850, AM180x and OPAM-L138 platforms + +- reg : physical base address and length of the controller memory mapped + region(s). +- reg-names : Should contain: + * "mpu" for the main registers (required). + * "dat" for the data FIFO (optional). + +- dmas: three element list of DMA controller phandles, DMA request line and + TC channel ordered triplets. +- dma-names: identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. The dma + identifiers must be "rx" and "tx". + +Optional properties: +~~~~~~~~~~~~~~~~~~~~ +- interrupts : Interrupt numbers for McBSP +- interrupt-names : Known interrupt names are "rx" and "tx" + +- pinctrl-0: Should specify pin control group used for this controller. +- pinctrl-names: Should contain only one value - "default", for more details + please refer to pinctrl-bindings.txt + +Example (AM1808): +~~~~~~~~~~~~~~~~~ + +mcbsp0: mcbsp@1d10000 { + compatible = "ti,da850-mcbsp"; + pinctrl-names = "default"; + pinctrl-0 = <&mcbsp0_pins>; + + reg = <0x00110000 0x1000>, + <0x00310000 0x1000>; + reg-names = "mpu", "dat"; + interrupts = <97 98>; + interrupts-names = "rx", "tx"; + dmas = <&edma0 3 1 + &edma0 2 1>; + dma-names = "tx", "rx"; + status = "okay"; +}; -- cgit v0.10.2 From 5f9a50c3e55ee887b7a0ccb68045b92579972b55 Mon Sep 17 00:00:00 2001 From: Petr Kulhavy Date: Mon, 18 Apr 2016 14:32:41 +0200 Subject: ASoC: Davinci: McBSP: add device tree support for McBSP This adds DT support for the TI DA8xx/OMAP-L1x/AM17xx/AM18xx McBSP driver. Signed-off-by: Petr Kulhavy Reviewed-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 50ca291..6b732d8 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -16,7 +16,11 @@ config SND_EDMA_SOC - DRA7xx family config SND_DAVINCI_SOC_I2S - tristate + tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support" + depends on SND_EDMA_SOC + help + Say Y or M here if you want to have support for McBSP IP found in + Texas Instruments DaVinci DA850 SoCs. config SND_DAVINCI_SOC_MCASP tristate "Multichannel Audio Serial Port (McASP) support" diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index ec98548..3849616 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -4,9 +4,15 @@ * Author: Vladimir Barinov, * Copyright: (C) 2007 MontaVista Software, Inc., * + * DT support (c) 2016 Petr Kulhavy, Barix AG + * based on davinci-mcasp.c DT support + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * + * TODO: + * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers */ #include @@ -650,13 +656,24 @@ static const struct snd_soc_component_driver davinci_i2s_component = { static int davinci_i2s_probe(struct platform_device *pdev) { + struct snd_dmaengine_dai_dma_data *dma_data; struct davinci_mcbsp_dev *dev; struct resource *mem, *res; void __iomem *io_base; int *dma; int ret; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!mem) { + dev_warn(&pdev->dev, + "\"mpu\" mem resource not found, using index 0\n"); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + } + io_base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(io_base)) return PTR_ERR(io_base); @@ -666,39 +683,43 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) - return -ENODEV; - clk_enable(dev->clk); - dev->base = io_base; - dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = - (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); + /* setup DMA, first TX, then RX */ + dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); - dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = - (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); - - /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENXIO; - goto err_release_clk; + if (res) { + dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; + *dma = res->start; + dma_data->filter_data = dma; + } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + dma_data->filter_data = "tx"; + } else { + dev_err(&pdev->dev, "Missing DMA tx resource\n"); + return -ENODEV; } - dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; - *dma = res->start; - dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma; + + dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "no DMA resource\n"); - ret = -ENXIO; - goto err_release_clk; + if (res) { + dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; + *dma = res->start; + dma_data->filter_data = dma; + } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + dma_data->filter_data = "rx"; + } else { + dev_err(&pdev->dev, "Missing DMA rx resource\n"); + return -ENODEV; } - dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; - *dma = res->start; - dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma; + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return -ENODEV; + clk_enable(dev->clk); dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); @@ -737,11 +758,18 @@ static int davinci_i2s_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id davinci_i2s_match[] = { + { .compatible = "ti,da850-mcbsp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_i2s_match); + static struct platform_driver davinci_mcbsp_driver = { .probe = davinci_i2s_probe, .remove = davinci_i2s_remove, .driver = { .name = "davinci-mcbsp", + .of_match_table = of_match_ptr(davinci_i2s_match), }, }; -- cgit v0.10.2 From 09184118a8abae030539469848d475adcc0e5839 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 31 Mar 2016 16:36:00 +0300 Subject: ASoC: hdmi-codec: Add hdmi-codec for external HDMI-encoders The hdmi-codec is a platform device driver to be registered from drivers of external HDMI encoders with I2S and/or spdif interface. The driver in turn registers an ASoC codec for the HDMI encoder's audio functionality. The structures and definitions in the API header are mostly redundant copies of similar structures in ASoC headers. This is on purpose to avoid direct dependencies to ASoC structures in video side driver. Signed-off-by: Jyri Sarha Acked-by: Arnaud Pouliquen Acked-by: PC Liao Signed-off-by: Mark Brown diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h new file mode 100644 index 0000000..fc3a481 --- /dev/null +++ b/include/sound/hdmi-codec.h @@ -0,0 +1,100 @@ +/* + * hdmi-codec.h - HDMI Codec driver API + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Jyri Sarha + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __HDMI_CODEC_H__ +#define __HDMI_CODEC_H__ + +#include +#include +#include +#include + +/* + * Protocol between ASoC cpu-dai and HDMI-encoder + */ +struct hdmi_codec_daifmt { + enum { + HDMI_I2S, + HDMI_RIGHT_J, + HDMI_LEFT_J, + HDMI_DSP_A, + HDMI_DSP_B, + HDMI_AC97, + HDMI_SPDIF, + } fmt; + int bit_clk_inv:1; + int frame_clk_inv:1; + int bit_clk_master:1; + int frame_clk_master:1; +}; + +/* + * HDMI audio parameters + */ +struct hdmi_codec_params { + struct hdmi_audio_infoframe cea; + struct snd_aes_iec958 iec; + int sample_rate; + int sample_width; + int channels; +}; + +struct hdmi_codec_ops { + /* + * Called when ASoC starts an audio stream setup. + * Optional + */ + int (*audio_startup)(struct device *dev); + + /* + * Configures HDMI-encoder for audio stream. + * Mandatory + */ + int (*hw_params)(struct device *dev, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); + + /* + * Shuts down the audio stream. + * Mandatory + */ + void (*audio_shutdown)(struct device *dev); + + /* + * Mute/unmute HDMI audio stream. + * Optional + */ + int (*digital_mute)(struct device *dev, bool enable); + + /* + * Provides EDID-Like-Data from connected HDMI device. + * Optional + */ + int (*get_eld)(struct device *dev, uint8_t *buf, size_t len); +}; + +/* HDMI codec initalization data */ +struct hdmi_codec_pdata { + const struct hdmi_codec_ops *ops; + uint i2s:1; + uint spdif:1; + int max_i2s_channels; +}; + +#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec" + +#endif /* __HDMI_CODEC_H__ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 649e92a..06d0e05 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -88,6 +88,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_NAU8825 if I2C + select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C select SND_SOC_PCM179X_I2C if I2C select SND_SOC_PCM179X_SPI if SPI_MASTER @@ -477,6 +478,11 @@ config SND_SOC_BT_SCO config SND_SOC_DMIC tristate +config SND_SOC_HDMI_CODEC + tristate + select SND_PCM_ELD + select SND_PCM_IEC958 + config SND_SOC_ES8328 tristate "Everest Semi ES8328 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 185a712..d7185dd 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-nau8825-objs := nau8825.o +snd-soc-hdmi-codec-objs := hdmi-codec.o snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm179x-codec-objs := pcm179x.o snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o @@ -290,6 +291,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o +obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c new file mode 100644 index 0000000..b46b8ed --- /dev/null +++ b/sound/soc/codecs/hdmi-codec.c @@ -0,0 +1,396 @@ +/* + * ALSA SoC codec for HDMI encoder drivers + * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Jyri Sarha + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* This is only to get MAX_ELD_BYTES */ + +struct hdmi_codec_priv { + struct hdmi_codec_pdata hcd; + struct snd_soc_dai_driver *daidrv; + struct hdmi_codec_daifmt daifmt[2]; + struct mutex current_stream_lock; + struct snd_pcm_substream *current_stream; + struct snd_pcm_hw_constraint_list ratec; + uint8_t eld[MAX_ELD_BYTES]; +}; + +static const struct snd_soc_dapm_widget hdmi_widgets[] = { + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route hdmi_routes[] = { + { "TX", NULL, "Playback" }, +}; + +enum { + DAI_ID_I2S = 0, + DAI_ID_SPDIF, +}; + +static int hdmi_codec_new_stream(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + mutex_lock(&hcp->current_stream_lock); + if (!hcp->current_stream) { + hcp->current_stream = substream; + } else if (hcp->current_stream != substream) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); + ret = -EINVAL; + } + mutex_unlock(&hcp->current_stream_lock); + + return ret; +} + +static int hdmi_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + dev_dbg(dai->dev, "%s()\n", __func__); + + ret = hdmi_codec_new_stream(substream, dai); + if (ret) + return ret; + + if (hcp->hcd.ops->audio_startup) { + ret = hcp->hcd.ops->audio_startup(dai->dev->parent); + if (ret) { + mutex_lock(&hcp->current_stream_lock); + hcp->current_stream = NULL; + mutex_unlock(&hcp->current_stream_lock); + return ret; + } + } + + if (hcp->hcd.ops->get_eld) { + ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld, + sizeof(hcp->eld)); + + if (!ret) { + ret = snd_pcm_hw_constraint_eld(substream->runtime, + hcp->eld); + if (ret) + return ret; + } + } + return 0; +} + +static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s()\n", __func__); + + WARN_ON(hcp->current_stream != substream); + + hcp->hcd.ops->audio_shutdown(dai->dev->parent); + + mutex_lock(&hcp->current_stream_lock); + hcp->current_stream = NULL; + mutex_unlock(&hcp->current_stream_lock); +} + +static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_params hp = { + .iec = { + .status = { 0 }, + .subcode = { 0 }, + .pad = 0, + .dig_subframe = { 0 }, + } + }; + int ret; + + dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, + params_width(params), params_rate(params), + params_channels(params)); + + if (params_width(params) > 24) + params->msbits = 24; + + ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status, + sizeof(hp.iec.status)); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + + ret = hdmi_codec_new_stream(substream, dai); + if (ret) + return ret; + + hdmi_audio_infoframe_init(&hp.cea); + hp.cea.channels = params_channels(params); + hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + + hp.sample_width = params_width(params); + hp.sample_rate = params_rate(params); + hp.channels = params_channels(params); + + return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id], + &hp); +} + +static int hdmi_codec_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_daifmt cf = { 0 }; + int ret = 0; + + dev_dbg(dai->dev, "%s()\n", __func__); + + if (dai->id == DAI_ID_SPDIF) { + cf.fmt = HDMI_SPDIF; + } else { + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + cf.bit_clk_master = 1; + cf.frame_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFM: + cf.frame_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + cf.bit_clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + cf.frame_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + cf.bit_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + cf.frame_clk_inv = 1; + cf.bit_clk_inv = 1; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cf.fmt = HDMI_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + cf.fmt = HDMI_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + cf.fmt = HDMI_DSP_B; + break; + case SND_SOC_DAIFMT_RIGHT_J: + cf.fmt = HDMI_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + cf.fmt = HDMI_LEFT_J; + break; + case SND_SOC_DAIFMT_AC97: + cf.fmt = HDMI_AC97; + break; + default: + dev_err(dai->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + } + + hcp->daifmt[dai->id] = cf; + + return ret; +} + +static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(dai->dev, "%s()\n", __func__); + + if (hcp->hcd.ops->digital_mute) + return hcp->hcd.ops->digital_mute(dai->dev->parent, mute); + + return 0; +} + +static const struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .set_fmt = hdmi_codec_set_fmt, + .digital_mute = hdmi_codec_digital_mute, +}; + + +#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) + +/* + * This list is only for formats allowed on the I2S bus. So there is + * some formats listed that are not supported by HDMI interface. For + * instance allowing the 32-bit formats enables 24-precision with CPU + * DAIs that do not support 24-bit formats. If the extra formats cause + * problems, we should add the video side driver an option to disable + * them. + */ +#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE) + +static struct snd_soc_dai_driver hdmi_i2s_dai = { + .name = "i2s-hifi", + .id = DAI_ID_I2S, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = HDMI_RATES, + .formats = I2S_FORMATS, + .sig_bits = 24, + }, + .ops = &hdmi_dai_ops, +}; + +static const struct snd_soc_dai_driver hdmi_spdif_dai = { + .name = "spdif-hifi", + .id = DAI_ID_SPDIF, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = HDMI_RATES, + .formats = SPDIF_FORMATS, + }, + .ops = &hdmi_dai_ops, +}; + +static struct snd_soc_codec_driver hdmi_codec = { + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .dapm_routes = hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_routes), +}; + +static int hdmi_codec_probe(struct platform_device *pdev) +{ + struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct hdmi_codec_priv *hcp; + int dai_count, i = 0; + int ret; + + dev_dbg(dev, "%s()\n", __func__); + + if (!hcd) { + dev_err(dev, "%s: No plalform data\n", __func__); + return -EINVAL; + } + + dai_count = hcd->i2s + hcd->spdif; + if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params || + !hcd->ops->audio_shutdown) { + dev_err(dev, "%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); + if (!hcp) + return -ENOMEM; + + hcp->hcd = *hcd; + mutex_init(&hcp->current_stream_lock); + + hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv), + GFP_KERNEL); + if (!hcp->daidrv) + return -ENOMEM; + + if (hcd->i2s) { + hcp->daidrv[i] = hdmi_i2s_dai; + hcp->daidrv[i].playback.channels_max = + hcd->max_i2s_channels; + i++; + } + + if (hcd->spdif) + hcp->daidrv[i] = hdmi_spdif_dai; + + ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv, + dai_count); + if (ret) { + dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n", + __func__, ret); + return ret; + } + + dev_set_drvdata(dev, hcp); + return 0; +} + +static int hdmi_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver hdmi_codec_driver = { + .driver = { + .name = HDMI_CODEC_DRV_NAME, + }, + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, +}; + +module_platform_driver(hdmi_codec_driver); + +MODULE_AUTHOR("Jyri Sarha "); +MODULE_DESCRIPTION("HDMI Audio Codec Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME); -- cgit v0.10.2 From 63a450aa4d08ccf4f53e9fa59144e746e2288319 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 19 Apr 2016 15:19:02 +0100 Subject: ASoC: da7219: Update PLL ranges and dividers to improve locking The expected MCLK frequency ranges and the associated dividers are updated to improve PLL locking in a corner scenario, with low MCLK frequency near an input divider change boundary. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 81c0708..3b1d65b 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1079,21 +1079,21 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, dev_err(codec->dev, "PLL input clock %d below valid range\n", da7219->mclk_rate); return -EINVAL; - } else if (da7219->mclk_rate <= 5000000) { - indiv_bits = DA7219_PLL_INDIV_2_5_MHZ; - indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL; - } else if (da7219->mclk_rate <= 10000000) { - indiv_bits = DA7219_PLL_INDIV_5_10_MHZ; - indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL; - } else if (da7219->mclk_rate <= 20000000) { - indiv_bits = DA7219_PLL_INDIV_10_20_MHZ; - indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7219->mclk_rate <= 40000000) { - indiv_bits = DA7219_PLL_INDIV_20_40_MHZ; - indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7219->mclk_rate <= 4500000) { + indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 9000000) { + indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7219->mclk_rate <= 18000000) { + indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7219->mclk_rate <= 36000000) { + indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7219->mclk_rate <= 54000000) { - indiv_bits = DA7219_PLL_INDIV_40_54_MHZ; - indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL; } else { dev_err(codec->dev, "PLL input clock %d above valid range\n", da7219->mclk_rate); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 5a787e7..ff2a2f0 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -194,11 +194,11 @@ /* DA7219_PLL_CTRL = 0x20 */ #define DA7219_PLL_INDIV_SHIFT 2 #define DA7219_PLL_INDIV_MASK (0x7 << 2) -#define DA7219_PLL_INDIV_2_5_MHZ (0x0 << 2) -#define DA7219_PLL_INDIV_5_10_MHZ (0x1 << 2) -#define DA7219_PLL_INDIV_10_20_MHZ (0x2 << 2) -#define DA7219_PLL_INDIV_20_40_MHZ (0x3 << 2) -#define DA7219_PLL_INDIV_40_54_MHZ (0x4 << 2) +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2) #define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 #define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) #define DA7219_PLL_MODE_SHIFT 6 @@ -761,11 +761,11 @@ #define DA7219_PLL_FREQ_OUT_98304 98304000 /* PLL Frequency Dividers */ -#define DA7219_PLL_INDIV_2_5_MHZ_VAL 1 -#define DA7219_PLL_INDIV_5_10_MHZ_VAL 2 -#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16 /* SRM */ #define DA7219_SRM_CHECK_RETRIES 8 -- cgit v0.10.2 From fb137ba64a6415ddf231495f6d1a82de1cd69ed0 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 19 Apr 2016 15:19:03 +0100 Subject: ASoC: da7219: Disallow unsupported 32KHz clock setting in set_dai_sysclk() The PLL function was updated to disallow 32KHz in commit 501f72e9c520 ("ASoC: da7219: Remove support for 32KHz PLL mode"), but set_dai_sysclk() was missed and still permits it. This patch resolves that discrepancy. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 3b1d65b..caea2ee 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1025,7 +1025,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) return 0; - if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) { + if ((freq < 2000000) || (freq > 54000000)) { dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", freq); return -EINVAL; -- cgit v0.10.2 From b6bf3289bc3c1d8df9f37c2f4f8450cc677fb286 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 19 Apr 2016 18:05:04 -0700 Subject: ASoC: ak4642: Remove CLK_IS_ROOT This flag is a no-op now (see commit 47b0eeb3dc8a "clk: Deprecate CLK_IS_ROOT", 2016-02-02) so remove it. Signed-off-by: Stephen Boyd Acked-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index cda27c2..1ee8506 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -608,9 +608,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev) of_property_read_string(np, "clock-output-names", &clk_name); - clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, - (parent_clk_name) ? 0 : CLK_IS_ROOT, - rate); + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate); if (!IS_ERR(clk)) of_clk_add_provider(np, of_clk_src_simple_get, clk); -- cgit v0.10.2 From 2f0ad49104cbb19db24442af736614659363d2ab Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Tue, 19 Apr 2016 13:12:35 +0800 Subject: ASoC: Change DAI link's be_id to a generic id The generic ID can be used by topology: - Toplogy can create FE links and set their ID, machine drivers will be notified and check this ID for machine-specific init. - Toplogy can use the ID to find existing BE & CC links and further configure them. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 02b4a21..ef25e86 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1002,7 +1002,7 @@ struct snd_soc_dai_link { */ const char *platform_name; struct device_node *platform_of_node; - int be_id; /* optional ID for machine driver BE identification */ + int id; /* optional ID for machine driver link identification */ const struct snd_soc_pcm_stream *params; unsigned int num_params; diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 3f8a1e1..7486a00 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -201,7 +201,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { /* SSP0 - Codec */ .name = "Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "snd-soc-dummy-dai", .platform_name = "snd-soc-dummy", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 032a2e7..88efb62 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -304,7 +304,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1c95ccc..35f591e 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index e609f08..6260df6 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 2a6f808..0618a7f 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = { /* back ends */ { .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 2e5347f..df9d254 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = { { /* SSP2 - Codec */ .name = "SSP2-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 2255857..863f1d5 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { /* SSP0 - Codec */ .name = "Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "snd-soc-dummy-dai", .platform_name = "snd-soc-dummy", .no_pcm = 1, diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 72176b7..9cc9240 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -456,7 +456,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -472,7 +472,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -489,7 +489,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 2, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -501,7 +501,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp1", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -512,7 +512,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp2", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -523,7 +523,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp3", - .be_id = 5, + .id = 5, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 5f1ca99..53380b2 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -505,7 +505,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -523,7 +523,7 @@ static struct snd_soc_dai_link skylake_dais[] = { { /* SSP1 - Codec */ .name = "SSP1-Codec", - .be_id = 1, + .id = 1, .cpu_dai_name = "SSP1 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -540,7 +540,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "dmic01", - .be_id = 2, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -552,7 +552,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp1", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -563,7 +563,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp2", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -574,7 +574,7 @@ static struct snd_soc_dai_link skylake_dais[] = { }, { .name = "iDisp3", - .be_id = 5, + .id = 5, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 2016397a..9e39fc1 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -375,7 +375,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { { /* SSP0 - Codec */ .name = "SSP0-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP0 Pin", .platform_name = "0000:00:1f.3", .no_pcm = 1, @@ -393,7 +393,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .id = 1, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -405,7 +405,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp1", - .be_id = 2, + .id = 2, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -416,7 +416,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp2", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -427,7 +427,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, { .name = "iDisp3", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", -- cgit v0.10.2 From 305e9020f09d28560373c0112682e6fd11e909f6 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Tue, 19 Apr 2016 13:12:25 +0800 Subject: ASoC: Export snd_soc_find_dai() This API can be used by topology to find an existing BE dai by name and further configure it. Topology will also check DAI ID to avoid wrong match. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 02b4a21..7687e2d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card, int snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv); +struct snd_soc_dai *snd_soc_find_dai( + const struct snd_soc_dai_link_component *dlc); + #include #ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d2e62b15..07663de 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -930,7 +930,7 @@ static struct snd_soc_component *soc_find_component( return NULL; } -static struct snd_soc_dai *snd_soc_find_dai( +struct snd_soc_dai *snd_soc_find_dai( const struct snd_soc_dai_link_component *dlc) { struct snd_soc_component *component; @@ -959,6 +959,7 @@ static struct snd_soc_dai *snd_soc_find_dai( return NULL; } +EXPORT_SYMBOL_GPL(snd_soc_find_dai); static bool soc_is_dai_link_bound(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) -- cgit v0.10.2 From 8e42db1eaab6c2558dbc2e6c1428730df0a295f4 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 21 Apr 2016 14:04:14 +0100 Subject: ASoC: arizona: Prefer lower FRATIO in pseudo-fractional mode When setting up an FLL in pseudo-fractional mode it is preferred to use a lower FRATIO if possible to give a higher reference clock frequency. This patch swaps the two loops in arizona_calc_fratio() so that lower FRATIOs are tried first. The decrementing loop is also changed to start from init_ratio because the original settings might already give a fractional value for N.K Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index d8a6823..0caecc6 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -2037,7 +2037,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll, init_ratio, Fref, refdiv); while (div <= ARIZONA_FLL_MAX_REFDIV) { - for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO; + /* start from init_ratio because this may already give a + * fractional N.K + */ + for (ratio = init_ratio; ratio > 0; ratio--) { + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); + return ratio; + } + } + + for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO; ratio++) { if ((ARIZONA_FLL_VCO_CORNER / 2) / (fll->vco_mult * ratio) < Fref) { @@ -2063,17 +2077,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll, } } - for (ratio = init_ratio - 1; ratio > 0; ratio--) { - if (target % (ratio * Fref)) { - cfg->refdiv = refdiv; - cfg->fratio = ratio - 1; - arizona_fll_dbg(fll, - "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", - Fref, refdiv, div, ratio); - return ratio; - } - } - div *= 2; Fref /= 2; refdiv++; -- cgit v0.10.2 From 09305da97c7808b900985526aa9198233f32fb37 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Thu, 21 Apr 2016 11:45:22 +0530 Subject: ASoC: Intel: Skylake: Use UUID in binary format To avoid complex string manipulations with UUID in canonical form, use UUID in binary format. Signed-off-by: Shreyas NC Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ff31e66..deabe73 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -118,7 +118,7 @@ struct skl_dsp_fw_ops { int (*set_state_D0)(struct sst_dsp *ctx); int (*set_state_D3)(struct sst_dsp *ctx); unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); - int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name); + int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); }; diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 348a734..bec4a7c 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "../common/sst-ipc.h" @@ -304,14 +305,16 @@ static int skl_transfer_module(struct sst_dsp *ctx, return ret; } -static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid) +static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid) { struct skl_module_table *module_entry = NULL; int ret = 0; char mod_name[64]; /* guid str = 32 chars + 4 hyphens */ + uuid_le *uuid_mod; - snprintf(mod_name, sizeof(mod_name), "%s%s%s", - "intel/dsp_fw_", guid, ".bin"); + uuid_mod = (uuid_le *)guid; + snprintf(mod_name, sizeof(mod_name), "%s%pUL%s", + "intel/dsp_fw_", uuid_mod, ".bin"); module_entry = skl_module_get_from_id(ctx, mod_id); if (module_entry == NULL) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 545b4e7..4f27d82 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1550,6 +1550,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, return -ENOMEM; w->priv = mconfig; + memcpy(&mconfig->guid, &dfw_config->uuid, 16); + mconfig->id.module_id = dfw_config->module_id; mconfig->id.instance_id = dfw_config->instance_id; mconfig->mcps = dfw_config->max_mcps; @@ -1579,10 +1581,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, mconfig->time_slot = dfw_config->time_slot; mconfig->formats_config.caps_size = dfw_config->caps.caps_size; - if (dfw_config->is_loadable) - memcpy(mconfig->guid, dfw_config->uuid, - ARRAY_SIZE(dfw_config->uuid)); - mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) * sizeof(*mconfig->m_in_pin), GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index de3c401..22c913d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -281,7 +281,7 @@ enum skl_module_state { }; struct skl_module_cfg { - char guid[SKL_UUID_STR_SZ]; + u8 guid[16]; struct skl_module_inst_id id; u8 domain; bool homogenous_inputs; diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h index 1db88a6..a32e5e9 100644 --- a/sound/soc/intel/skylake/skl-tplg-interface.h +++ b/sound/soc/intel/skylake/skl-tplg-interface.h @@ -181,7 +181,7 @@ struct skl_dfw_pipe { } __packed; struct skl_dfw_module { - char uuid[SKL_UUID_STR_SZ]; + u8 uuid[16]; u16 module_id; u16 instance_id; -- cgit v0.10.2 From 81151cfb6bfe69f1c5a52b795eb005226a322c9e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 20 Apr 2016 10:59:58 +0200 Subject: ASoC: hdmi-codec: Add ELD control ALSA doesn't know about all the different compressed audio formats, so there is no interface to let userspace enumerate the formats that are supported by the connected sink. Exporting the raw ELD bytes to userspace allows an application to select the appropriate audio format depending on the current capabilities of the connected HDMI sink device. Usually userspace then just pretends to ALSA that the data is in one of the raw 16-bit PCM audio formats and relies on the IEC controls to tell the sink how to interpret the data. Signed-off-by: Philipp Zabel Reviewed-by: Jyri Sarha Tested-by: Jyri Sarha Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index b46b8ed..c78333b 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -47,6 +47,42 @@ enum { DAI_ID_SPDIF, }; +static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(hcp->eld); + + return 0; +} + +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + mutex_lock(&hcp->eld_lock); + memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); + mutex_unlock(&hcp->eld_lock); + + return 0; +} + +static const struct snd_kcontrol_new hdmi_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, + }, +}; + static int hdmi_codec_new_stream(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -312,6 +348,8 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { }; static struct snd_soc_codec_driver hdmi_codec = { + .controls = hdmi_controls, + .num_controls = ARRAY_SIZE(hdmi_controls), .dapm_widgets = hdmi_widgets, .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), .dapm_routes = hdmi_routes, -- cgit v0.10.2 From db71336b9eec22c21cef65c90cea49130c464994 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 22 Apr 2016 10:40:11 +0200 Subject: ASoC: hdmi-codec: Add ELD control Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index c78333b..8e36e88 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -65,9 +65,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - mutex_lock(&hcp->eld_lock); memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); - mutex_unlock(&hcp->eld_lock); return 0; } -- cgit v0.10.2 From fba0d7066524ab7b8ccf60e7e95981d10ed008b0 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 22 Apr 2016 09:03:53 +0530 Subject: ASoC: Intel: Atom: fix boot warning Users have reported seeing this false warning on atom driver [ 5.647469] sst-mfld-platform sst-mfld-platform: Slot control: codec_out tx interleaver slot 0 doesn't have DAPM widget!!! [ 5.661612] sst-mfld-platform sst-mfld-platform: Slot control: codec_out tx interleaver slot 1 doesn't have DAPM widget!!! [ 5.661646] sst-mfld-platform sst-mfld-platform: Slot control: codec_out tx interleaver slot 2 doesn't have DAPM widget!!! [ 5.661681] sst-mfld-platform sst-mfld-platform: Slot control: codec_out tx interleaver slot 3 doesn't have DAPM widget!!! [ 5.661708] sst-mfld-platform sst-mfld-platform: Slot control: codec_in rx deinterleaver codec_in0_0 doesn't have DAPM widget!!! [ 5.661738] sst-mfld-platform sst-mfld-platform: Slot control: codec_in rx deinterleaver codec_in0_1 doesn't have DAPM widget!!! [ 5.661771] sst-mfld-platform sst-mfld-platform: Slot control: codec_in rx deinterleaver codec_in1_0 doesn't have DAPM widget!!! [ 5.661807] sst-mfld-platform sst-mfld-platform: Slot control: codec_in rx deinterleaver codec_in1_1 doesn't have DAPM widget!!! This is caused when check for control is not being associated with a dapm widget, but the check is wrong as the else case maybe triggered when widget is not powered up, so we should check if widget is associated before printing this message. Tested-by: Sandeep Tayal Tested-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index b97e6ad..98720a9 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol if (e->w && e->w->power) ret = sst_send_slot_map(drv); - else + else if (!e->w) dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n", kcontrol->id.name); return ret; -- cgit v0.10.2 From fbb88b5ca1dc84416fc1fec34773948b6780492c Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Fri, 22 Apr 2016 12:25:33 +0800 Subject: ASoC: Add kerneldoc comments for snd_soc_find_dai snd_soc_find_dai() has been exported and so add the kerneldoc comments for it. Signed-off-by: Mengdong Lin Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 07663de..16369ca 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -930,6 +930,17 @@ static struct snd_soc_component *soc_find_component( return NULL; } +/** + * snd_soc_find_dai - Find a registered DAI + * + * @dlc: name of the DAI and optional component info to match + * + * This function will search all regsitered components and their DAIs to + * find the DAI of the same name. The component's of_node and name + * should also match if being specified. + * + * Return: pointer of DAI, or NULL if not found. + */ struct snd_soc_dai *snd_soc_find_dai( const struct snd_soc_dai_link_component *dlc) { -- cgit v0.10.2 From ae48a35c408732413880d0ac0d6467baa5b3d68a Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Fri, 22 Apr 2016 14:16:26 +0100 Subject: ASoC: da7218: Update PLL ranges and dividers to improve locking The expected MCLK frequency ranges and the associated dividers are updated to improve PLL locking in a corner scenario, with low MCLK frequency near an input divider change boundary. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 93575f2..99ce23e 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -1868,27 +1868,27 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */ if (da7218->mclk_rate == 32768) { - indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; + indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; } else if (da7218->mclk_rate < 2000000) { dev_err(codec->dev, "PLL input clock %d below valid range\n", da7218->mclk_rate); return -EINVAL; - } else if (da7218->mclk_rate <= 5000000) { - indiv_bits = DA7218_PLL_INDIV_2_5_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; - } else if (da7218->mclk_rate <= 10000000) { - indiv_bits = DA7218_PLL_INDIV_5_10_MHZ; - indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL; - } else if (da7218->mclk_rate <= 20000000) { - indiv_bits = DA7218_PLL_INDIV_10_20_MHZ; - indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7218->mclk_rate <= 40000000) { - indiv_bits = DA7218_PLL_INDIV_20_40_MHZ; - indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7218->mclk_rate <= 4500000) { + indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7218->mclk_rate <= 9000000) { + indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7218->mclk_rate <= 18000000) { + indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7218->mclk_rate <= 36000000) { + indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7218->mclk_rate <= 54000000) { - indiv_bits = DA7218_PLL_INDIV_40_54_MHZ; - indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL; } else { dev_err(codec->dev, "PLL input clock %d above valid range\n", da7218->mclk_rate); diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h index c2c5904..477cd37 100644 --- a/sound/soc/codecs/da7218.h +++ b/sound/soc/codecs/da7218.h @@ -876,15 +876,11 @@ /* DA7218_PLL_CTRL = 0x91 */ #define DA7218_PLL_INDIV_SHIFT 0 #define DA7218_PLL_INDIV_MASK (0x7 << 0) -#define DA7218_PLL_INDIV_2_5_MHZ (0x0 << 0) -#define DA7218_PLL_INDIV_5_10_MHZ (0x1 << 0) -#define DA7218_PLL_INDIV_10_20_MHZ (0x2 << 0) -#define DA7218_PLL_INDIV_20_40_MHZ (0x3 << 0) -#define DA7218_PLL_INDIV_40_54_MHZ (0x4 << 0) -#define DA7218_PLL_INDIV_2_10_MHZ_VAL 2 -#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0) +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0) +#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0) +#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0) +#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0) #define DA7218_PLL_MCLK_SQR_EN_SHIFT 4 #define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4) #define DA7218_PLL_MODE_SHIFT 6 @@ -1336,6 +1332,13 @@ #define DA7218_PLL_FREQ_OUT_90316 90316800 #define DA7218_PLL_FREQ_OUT_98304 98304000 +/* PLL Frequency Dividers */ +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16 + /* ALC Calibration */ #define DA7218_ALC_CALIB_DELAY_MIN 2500 #define DA7218_ALC_CALIB_DELAY_MAX 5000 -- cgit v0.10.2 From a34b027dca5ea840fbc84121db66488375acfdea Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Mon, 25 Apr 2016 13:39:38 +0000 Subject: ASoC: bcm2835: add 24bit support This adds 24 bit support to the I2S driver of the BCM2835 Code ported from bcm2708-i2s driver in Raspberry Pi tree. Signed-off-by: Florian Meier Signed-off-by: Matthias Reichl Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 1c1f221..d2663e7 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -259,6 +259,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: data_length = 16; break; + case SNDRV_PCM_FORMAT_S24_LE: + data_length = 24; + break; case SNDRV_PCM_FORMAT_S32_LE: data_length = 32; break; @@ -279,7 +282,7 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* Setup the frame format */ format = BCM2835_I2S_CHEN; - if (data_length > 24) + if (data_length >= 24) format |= BCM2835_I2S_CHWEX; format |= BCM2835_I2S_CHWID((data_length-8)&0xf); @@ -570,6 +573,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .capture = { @@ -577,6 +581,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .ops = &bcm2835_i2s_dai_ops, -- cgit v0.10.2 From 60507fe191f524e82986fa737e5b27b4d3ad9289 Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Mon, 25 Apr 2016 13:39:39 +0000 Subject: ASoC: bcm2835: setup clock only if CPU is clock master We only need to enable the clock if we are a clock master. Code ported from bcm2708-i2s driver in Raspberry Pi tree. Original work by Zoltan Szenczi. Signed-off-by: Matthias Reichl Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index d2663e7..a0026e2 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -276,8 +276,15 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* otherwise calculate a fitting block ratio */ bclk_ratio = 2 * data_length; - /* set target clock rate*/ - clk_set_rate(dev->clk, sampling_rate * bclk_ratio); + /* Clock should only be set up here if CPU is clock master */ + switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + clk_set_rate(dev->clk, sampling_rate * bclk_ratio); + break; + default: + break; + } /* Setup the frame format */ format = BCM2835_I2S_CHEN; -- cgit v0.10.2 From de06f22f717b30641229036439b804ae79a7ad4d Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 25 Apr 2016 19:30:39 -0400 Subject: ASoC: cs42l56: Use IS_ENABLED() instead of checking for built-in or module The IS_ENABLED() macro checks if a Kconfig symbol has been enabled either built-in or as a module, use that macro instead of open coding the same. Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 7cd5f76..eec1ff8 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -56,7 +56,7 @@ struct cs42l56_private { u8 iface; u8 iface_fmt; u8 iface_inv; -#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) +#if IS_ENABLED(CONFIG_INPUT) struct input_dev *beep; struct work_struct beep_work; int beep_rate; -- cgit v0.10.2 From 2ab8e744a437d39619b323d7303fa2e6513274b2 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 26 Apr 2016 17:06:20 +0100 Subject: ASoC: arizona: No need to update_bits when writing AEC clock control The bits in the ARIZONA_CLOCK_CONTROL register only respond to writes of a '1', a write of '0' is ignored. So there's no need to use update_bits. We can do a simple write to set bits. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 0caecc6..0239639 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1124,7 +1124,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - unsigned int mask = 0x3 << w->shift; unsigned int val; switch (event) { @@ -1138,7 +1137,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w, return 0; } - snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val); + snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val); return 0; } -- cgit v0.10.2 From 80833ff0eea693d8e0c3305a869159a64141fdad Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 27 Apr 2016 09:06:49 +0200 Subject: ASoC: atmel_ssc_dai: read DSP mode A data on rising edges of bclk Signed-off-by: Peter Rosin Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 2768970..1267e1a 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -652,7 +652,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_START, SSC_START_RISING_RF) - | SSC_BF(RCMR_CKI, SSC_CKI_FALLING) + | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); @@ -692,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_RISING_RF) - | SSC_BF(RCMR_CKI, SSC_CKI_FALLING) + | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); -- cgit v0.10.2 From 66225e98b985047ef214632413cc404a6341c960 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 27 Apr 2016 14:58:27 +0100 Subject: ASoC: wm_adsp: free memory when unloaded or closed The patch adds a wm_adsp2_remove() function to ensure that memory is freed when the driver is unloaded or shut down. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d3b1cb1..5f8727a 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -944,6 +944,13 @@ static void wm_adsp_ctl_work(struct work_struct *work) kfree(ctl_work); } +static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) +{ + kfree(ctl->cache); + kfree(ctl->name); + kfree(ctl); +} + static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *alg_region, unsigned int offset, unsigned int len, @@ -2340,6 +2347,19 @@ int wm_adsp2_init(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp2_init); +void wm_adsp2_remove(struct wm_adsp *dsp) +{ + struct wm_coeff_ctl *ctl; + + while (!list_empty(&dsp->ctl_list)) { + ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, + list); + list_del(&ctl->list); + wm_adsp_free_ctl_blk(ctl); + } +} +EXPORT_SYMBOL_GPL(wm_adsp2_remove); + int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index b61cb57..feb61e2 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; int wm_adsp1_init(struct wm_adsp *dsp); int wm_adsp2_init(struct wm_adsp *dsp); +void wm_adsp2_remove(struct wm_adsp *dsp); int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec); int wm_adsp1_event(struct snd_soc_dapm_widget *w, -- cgit v0.10.2 From 401cf1466a59139ec1805e2171d43a32be92f89c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 27 Apr 2016 14:58:28 +0100 Subject: ASoC: arizona: call wm_adsp2_remove when codec driver is removed Ensure that the wm_adsp driver cleans up when the codec driver is removed. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 576087b..2931378 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1238,10 +1238,15 @@ static int cs47l24_probe(struct platform_device *pdev) static int cs47l24_remove(struct platform_device *pdev) { + struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + wm_adsp2_remove(&cs47l24->core.adsp[1]); + wm_adsp2_remove(&cs47l24->core.adsp[2]); + return 0; } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a8b3e3f..7a539e0 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -2093,10 +2093,14 @@ static int wm5102_probe(struct platform_device *pdev) static int wm5102_remove(struct platform_device *pdev) { + struct wm5102_priv *wm5102 = platform_get_drvdata(pdev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + wm_adsp2_remove(&wm5102->core.adsp[0]); + return 0; } diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 83ba70f..dd87af1 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2435,10 +2435,16 @@ static int wm5110_probe(struct platform_device *pdev) static int wm5110_remove(struct platform_device *pdev) { + struct wm5110_priv *wm5110 = platform_get_drvdata(pdev); + int i; + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_codec(&pdev->dev); pm_runtime_disable(&pdev->dev); + for (i = 0; i < WM5110_NUM_ADSP; i++) + wm_adsp2_remove(&wm5110->core.adsp[i]); + return 0; } -- cgit v0.10.2 From 56574d541f93cf8c9449f9ecadc83d97323cfcec Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 27 Apr 2016 14:58:29 +0100 Subject: ASoC: wm_adsp: factor out freeing of alg regions Add a function to delete and free the contents of the alg_regions list. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5f8727a..8cde7bb 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1571,6 +1571,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, return alg_region; } +static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) +{ + struct wm_adsp_alg_region *alg_region; + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } +} + static int wm_adsp1_setup_algs(struct wm_adsp *dsp) { struct wmfw_adsp1_id_hdr adsp1_id; @@ -2001,7 +2014,6 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; unsigned int val; @@ -2081,13 +2093,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct wm_adsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); - } + + wm_adsp_free_alg_regions(dsp); break; default: @@ -2229,7 +2236,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_adsp_alg_region *alg_region; struct wm_coeff_ctl *ctl; int ret; @@ -2276,13 +2282,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct wm_adsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); - } + wm_adsp_free_alg_regions(dsp); if (wm_adsp_fw[dsp->fw].num_caps != 0) wm_adsp_buffer_free(dsp); -- cgit v0.10.2 From 73fe01cfb3babff01748a9fbc95cc3ea2079cc7f Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Wed, 27 Apr 2016 15:26:51 +0200 Subject: ASoC: dmaengine_pcm: Add support for packed transfers dmaengine_pcm currently only supports setups where FIFO reads/writes correspond to exactly one sample, eg 16-bit sample data is transferred via 16-bit FIFO accesses, 32-bit data via 32-bit accesses. This patch adds support for setups with fixed width FIFOs where multiple samples are packed into a larger word. For example setups with a 32-bit wide FIFO register that expect 16-bit sample transfers to be done with the left+right sample data packed into a 32-bit word. Support for packed transfers is controlled via the SND_DMAENGINE_PCM_DAI_FLAG_PACK flag in snd_dmaengine_dai_dma_data.flags If this flag is set dmaengine_pcm doesn't put any restriction on the supported formats and sets the DMA transfer width to undefined. This means control over the constraints is now transferred to the DAI driver and it's responsible to provide proper configuration and check for possible corner cases that aren't handled by the ALSA core. Signed-off-by: Matthias Reichl Acked-by: Lars-Peter Clausen Tested-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index f86ef5e..67be244 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -51,6 +51,16 @@ struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, void *filter_data); struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream); +/* + * The DAI supports packed transfers, eg 2 16-bit samples in a 32-bit word. + * If this flag is set the dmaengine driver won't put any restriction on + * the supported sample formats and set the DMA transfer size to undefined. + * The DAI driver is responsible to disable any unsupported formats in it's + * configuration and catch corner cases that are not already handled in + * the ALSA core. + */ +#define SND_DMAENGINE_PCM_DAI_FLAG_PACK BIT(0) + /** * struct snd_dmaengine_dai_dma_data - DAI DMA configuration data * @addr: Address of the DAI data source or destination register. @@ -63,6 +73,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) * requesting the DMA channel. * @chan_name: Custom channel name to use when requesting DMA channel. * @fifo_size: FIFO size of the DAI controller in bytes + * @flags: PCM_DAI flags, only SND_DMAENGINE_PCM_DAI_FLAG_PACK for now */ struct snd_dmaengine_dai_dma_data { dma_addr_t addr; @@ -72,6 +83,7 @@ struct snd_dmaengine_dai_dma_data { void *filter_data; const char *chan_name; unsigned int fifo_size; + unsigned int flags; }; void snd_dmaengine_pcm_set_config_from_dai_data( diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 697c166..8eb58c7 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -106,8 +106,9 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); * direction of the substream. If the substream is a playback stream the dst * fields will be initialized, if it is a capture stream the src fields will be * initialized. The {dst,src}_addr_width field will only be initialized if the - * addr_width field of the DAI DMA data struct is not equal to - * DMA_SLAVE_BUSWIDTH_UNDEFINED. + * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of + * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If + * both conditions are met the latter takes priority. */ void snd_dmaengine_pcm_set_config_from_dai_data( const struct snd_pcm_substream *substream, @@ -117,11 +118,17 @@ void snd_dmaengine_pcm_set_config_from_dai_data( if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { slave_config->dst_addr = dma_data->addr; slave_config->dst_maxburst = dma_data->maxburst; + if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) + slave_config->dst_addr_width = + DMA_SLAVE_BUSWIDTH_UNDEFINED; if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) slave_config->dst_addr_width = dma_data->addr_width; } else { slave_config->src_addr = dma_data->addr; slave_config->src_maxburst = dma_data->maxburst; + if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) + slave_config->src_addr_width = + DMA_SLAVE_BUSWIDTH_UNDEFINED; if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) slave_config->src_addr_width = dma_data->addr_width; } diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 6fd1906..6cef397 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -163,31 +163,42 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea } /* - * Prepare formats mask for valid/allowed sample types. If the dma does - * not have support for the given physical word size, it needs to be - * masked out so user space can not use the format which produces - * corrupted audio. - * In case the dma driver does not implement the slave_caps the default - * assumption is that it supports 1, 2 and 4 bytes widths. + * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep + * hw.formats set to 0, meaning no restrictions are in place. + * In this case it's the responsibility of the DAI driver to + * provide the supported format information. */ - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - int bits = snd_pcm_format_physical_width(i); - - /* Enable only samples with DMA supported physical widths */ - switch (bits) { - case 8: - case 16: - case 24: - case 32: - case 64: - if (addr_widths & (1 << (bits / 8))) - hw.formats |= (1LL << i); - break; - default: - /* Unsupported types */ - break; + if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) + /* + * Prepare formats mask for valid/allowed sample types. If the + * dma does not have support for the given physical word size, + * it needs to be masked out so user space can not use the + * format which produces corrupted audio. + * In case the dma driver does not implement the slave_caps the + * default assumption is that it supports 1, 2 and 4 bytes + * widths. + */ + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + int bits = snd_pcm_format_physical_width(i); + + /* + * Enable only samples with DMA supported physical + * widths + */ + switch (bits) { + case 8: + case 16: + case 24: + case 32: + case 64: + if (addr_widths & (1 << (bits / 8))) + hw.formats |= (1LL << i); + break; + default: + /* Unsupported types */ + break; + } } - } return snd_soc_set_runtime_hwparams(substream, &hw); } -- cgit v0.10.2 From beff053c0ef6983897e3481169292e6435ef0a2d Mon Sep 17 00:00:00 2001 From: Matthias Reichl Date: Wed, 27 Apr 2016 15:26:52 +0200 Subject: ASoC: bcm2835: Add S16_LE support via packed DMA transfers The bcm2835-i2s driver already has support for the S16_LE format but that format hasn't been made available because dmaengine_pcm didn't support packed data transfers. bcm2835-i2s needs 16-bit left+right channel data to be packed into a 32-bit word, the FIFO register is 32-bit only and doesn't support 16-bit access. Now that dmaengine_pcm supports packed transfers the format can be made available by setting the SND_DMAENGINE_PCM_DAI_FLAG_PACK flag. No further configuration is necessary: - snd_dmaengine_dai_dma_data.addr_width is already set to DMA_SLAVE_BUSWIDTH_4_BYTES to force 32-bit DMA transfers - dmaengine_pcm will pick up the S16_LE format from the DAI configuration and make it available since it's no longer masked out due to the PACK flag. - there are no further corner cases to catch in hw_params, since the channel count is fixed at 2 we always have two 16-bit stereo samples that can be transferred via 32-bit DMA Signed-off-by: Matthias Reichl Tested-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index a0026e2..6ba2049 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -690,6 +690,15 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; + /* + * Set the PACK flag to enable S16_LE support (2 S16_LE values + * packed into 32-bit transfers). + */ + dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags = + SND_DMAENGINE_PCM_DAI_FLAG_PACK; + dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = + SND_DMAENGINE_PCM_DAI_FLAG_PACK; + /* BCLK ratio - use default */ dev->bclk_ratio = 0; -- cgit v0.10.2 From 9fc7c862e78ba8bec142935673227f2463aa05a5 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:29 +0530 Subject: ALSA: hda - add helper to get channels from cap bits This helper is copied from legacy hda driver. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Acked-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/sound/hda/local.h b/sound/hda/local.h index d692f41..0d5bb15 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -16,6 +16,16 @@ static inline int get_wcaps_type(unsigned int wcaps) return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; } +static inline unsigned int get_wcaps_channels(u32 wcaps) +{ + unsigned int chans; + + chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13; + chans = (chans + 1) * 2; + + return chans; +} + extern const struct attribute_group *hdac_dev_attr_groups[]; int hda_widget_sysfs_init(struct hdac_device *codec); void hda_widget_sysfs_exit(struct hdac_device *codec); -- cgit v0.10.2 From b7756edeb7d03b675e10b4862dccc8deb4b0ca17 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:28 +0530 Subject: ASoC: hdac_hdmi: parse eld for channel map capability This patch parses ELD speaker allocation data block to find sink's chmap capability. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 26f9459..64ffe93 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -60,11 +60,17 @@ struct hdac_hdmi_cvt { struct hdac_hdmi_cvt_params params; }; +/* Currently only spk_alloc, more to be added */ +struct hdac_hdmi_parsed_eld { + u8 spk_alloc; +}; + struct hdac_hdmi_eld { bool monitor_present; bool eld_valid; int eld_size; char eld_buffer[ELD_MAX_SIZE]; + struct hdac_hdmi_parsed_eld info; }; struct hdac_hdmi_pin { @@ -1008,6 +1014,12 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid) return hdac_hdmi_query_cvt_params(&edev->hdac, cvt); } +static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev, + struct hdac_hdmi_pin *pin) +{ + pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER]; +} + static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) { struct hdac_ext_device *edev = pin->edev; @@ -1065,6 +1077,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll) snd_jack_report(pcm->jack, SND_JACK_AVOUT); } + hdac_hdmi_parse_eld(edev, pin); print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET, pin->eld.eld_buffer, pin->eld.eld_size); -- cgit v0.10.2 From bcced704788312360c0413d13b11611ae00a91c8 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:30 +0530 Subject: ASoC: hdac_hdmi: Add multichannel support To support multichannel hdac hdmi driver registers with HDA channel map framework. Channel count and channel slot verbs are programmed by using the chmap helpers/ops. The channel allocation is then programmed in the audio infoframe as per CEA spec. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 64ffe93..034593b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "../../hda/local.h" #include "hdac_hdmi.h" @@ -82,6 +83,10 @@ struct hdac_hdmi_pin { struct hdac_ext_device *edev; int repoll_count; struct delayed_work work; + struct mutex lock; + bool chmap_set; + unsigned char chmap[8]; /* ALSA API channel-map */ + int channels; /* current number of channels */ }; struct hdac_hdmi_pcm { @@ -106,6 +111,7 @@ struct hdac_hdmi_priv { int num_pin; int num_cvt; struct mutex pin_mutex; + struct hdac_chmap chmap; }; static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) @@ -284,26 +290,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, int i; const u8 *eld_buf; u8 conn_type; - int channels = 2; + int channels, ca; list_for_each_entry(pin, &hdmi->pin_list, head) { if (pin->nid == pin_nid) break; } + ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc, + pin->channels, pin->chmap_set, true, pin->chmap); + + channels = snd_hdac_get_active_channels(ca); + hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels); + + snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, + pin->channels, pin->chmap, pin->chmap_set); + eld_buf = pin->eld.eld_buffer; conn_type = drm_eld_get_conn_type(eld_buf); - /* setup channel count */ - snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, channels - 1); - switch (conn_type) { case DRM_ELD_CONN_TYPE_HDMI: hdmi_audio_infoframe_init(&frame); - /* Default stereo for now */ frame.channels = channels; + frame.channel_allocation = ca; ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); if (ret < 0) @@ -317,7 +328,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac, dp_ai.len = 0x1b; dp_ai.ver = 0x11 << 2; dp_ai.CC02_CT47 = channels - 1; - dp_ai.CA = 0; + dp_ai.CA = ca; dip = (u8 *)&dp_ai; break; @@ -376,17 +387,23 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream, struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai); struct hdac_hdmi_priv *hdmi = hdac->private_data; struct hdac_hdmi_dai_pin_map *dai_map; + struct hdac_hdmi_pin *pin; struct hdac_ext_dma_params *dd; int ret; dai_map = &hdmi->dai_map[dai->id]; + pin = dai_map->pin; dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream); dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n", dd->stream_tag, dd->format); + mutex_lock(&pin->lock); + pin->channels = substream->runtime->channels; + ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid, dai_map->pin->nid); + mutex_unlock(&pin->lock); if (ret < 0) return ret; @@ -646,6 +663,10 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + mutex_lock(&dai_map->pin->lock); + dai_map->pin->channels = 0; + mutex_unlock(&dai_map->pin->lock); + dai_map->pin = NULL; } } @@ -653,10 +674,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, static int hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt) { + unsigned int chans; + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; int err; - /* Only stereo supported as of now */ - cvt->params.channels_min = cvt->params.channels_max = 2; + chans = get_wcaps(hdac, cvt->nid); + chans = get_wcaps_channels(chans); + + cvt->params.channels_min = 2; + + cvt->params.channels_max = chans; + if (chans > hdmi->chmap.channels_max) + hdmi->chmap.channels_max = chans; err = snd_hdac_query_supported_pcm(hdac, cvt->nid, &cvt->params.rates, @@ -1136,6 +1166,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid) hdmi->num_pin++; pin->edev = edev; + mutex_init(&pin->lock); INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld); return 0; @@ -1506,6 +1537,7 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) return -ENOMEM; edev->private_data = hdmi_priv; + snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); dev_set_drvdata(&codec->dev, edev); -- cgit v0.10.2 From 1a10612fc3f5eb5cfa89af3f6b8181d69f79a371 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:31 +0530 Subject: ASoC: skl_rt286: Fix to support hdmi channel map support HDMI registers channel map controls per PCM. As PCMs are not registered during dai_link init callback, store the pcm ids and codec DAIs during this init callback. Register for late probe and call the jack_init API which also registers channel map in the late probe callback handler. The patch following the machine driver changes adds the channel map control in the hdac_hdmi codec driver. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 2016397a..06de802 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -30,6 +30,16 @@ static struct snd_soc_jack skylake_headset; +struct skl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct skl_rt286_private { + struct list_head hdmi_pcm_list; +}; + enum { SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_CP, @@ -142,9 +152,20 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id); + pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static unsigned int rates[] = { @@ -438,6 +459,21 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { }, }; +static int skylake_card_late_probe(struct snd_soc_card *card) +{ + struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hdmi_pcm *pcm; + int err; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); + if (err < 0) + return err; + } + + return 0; +} + /* skylake audio machine driver for SPT + RT286S */ static struct snd_soc_card skylake_rt286 = { .name = "skylake-rt286", @@ -451,11 +487,21 @@ static struct snd_soc_card skylake_rt286 = { .dapm_routes = skylake_rt286_map, .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map), .fully_routed = true, + .late_probe = skylake_card_late_probe, }; static int skylake_audio_probe(struct platform_device *pdev) { + struct skl_rt286_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + skylake_rt286.dev = &pdev->dev; + snd_soc_card_set_drvdata(&skylake_rt286, ctx); return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286); } -- cgit v0.10.2 From 0d425b4f900e4dc65bd186387dae32dbbb186e77 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:32 +0530 Subject: ASoC: Intel: boards: Update skl_nau88l25_max98357a driver to support chmap HDMI registers channel map controls per PCM. As PCMs are not registered during dai_link init callback, store the pcm ids and codec DAIs during this init callback. Register for late probe and call the jack_init API which also registers channel map in the late probe callback handler. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 72176b7..8ccc97c 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -30,6 +30,16 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +struct skl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct skl_nau8825_private { + struct list_head hdmi_pcm_list; +}; + enum { SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_CP, @@ -192,23 +202,56 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI1_PB; + pcm->codec_dai = dai; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); + pcm->device = SKL_DPCM_AUDIO_HDMI2_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI3_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) @@ -534,6 +577,21 @@ static struct snd_soc_dai_link skylake_dais[] = { }, }; +static int skylake_card_late_probe(struct snd_soc_card *card) +{ + struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hdmi_pcm *pcm; + int err; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); + if (err < 0) + return err; + } + + return 0; +} + /* skylake audio machine driver for SPT + NAU88L25 */ static struct snd_soc_card skylake_audio_card = { .name = "sklnau8825max", @@ -547,11 +605,21 @@ static struct snd_soc_card skylake_audio_card = { .dapm_routes = skylake_map, .num_dapm_routes = ARRAY_SIZE(skylake_map), .fully_routed = true, + .late_probe = skylake_card_late_probe, }; static int skylake_audio_probe(struct platform_device *pdev) { + struct skl_nau8825_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + skylake_audio_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&skylake_audio_card, ctx); return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); } -- cgit v0.10.2 From 46ed1a27fb44febb2c362fc30fcb51e8eed06e3a Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:33 +0530 Subject: ASoC: Intel: boards: Update skl_nau88l25_ssm4567 driver to support chmap HDMI registers channel map controls per PCM. As PCMs are not registered during dai_link init callback, store the pcm ids and codec DAIs during this init callback. Register for late probe and call the jack_init API which also registers channel map in the late probe callback handler. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 5f1ca99..bde85bf 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -34,6 +34,15 @@ static struct snd_soc_jack skylake_headset; static struct snd_soc_card skylake_audio_card; +struct skl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct skl_nau88125_private { + struct list_head hdmi_pcm_list; +}; enum { SKL_DPCM_AUDIO_PB = 0, SKL_DPCM_AUDIO_CP, @@ -222,24 +231,57 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd) static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI1_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB); + return 0; } static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI2_PB; + pcm->codec_dai = dai; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB); + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) { + struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai = rtd->codec_dai; + struct skl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; - return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB); + pcm->device = SKL_DPCM_AUDIO_HDMI3_PB; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; } static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd) @@ -585,6 +627,21 @@ static struct snd_soc_dai_link skylake_dais[] = { }, }; +static int skylake_card_late_probe(struct snd_soc_card *card) +{ + struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hdmi_pcm *pcm; + int err; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device); + if (err < 0) + return err; + } + + return 0; +} + /* skylake audio machine driver for SPT + NAU88L25 */ static struct snd_soc_card skylake_audio_card = { .name = "sklnau8825adi", @@ -600,11 +657,21 @@ static struct snd_soc_card skylake_audio_card = { .codec_conf = ssm4567_codec_conf, .num_configs = ARRAY_SIZE(ssm4567_codec_conf), .fully_routed = true, + .late_probe = skylake_card_late_probe, }; static int skylake_audio_probe(struct platform_device *pdev) { + struct skl_nau88125_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + skylake_audio_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&skylake_audio_card, ctx); return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); } -- cgit v0.10.2 From 2889099eb8cd0811dc2986643d46c0b62b90eeb4 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:34 +0530 Subject: ASoC: hdac_hdmi: Register chmap controls and ops With this patch, chmap controls are created and user space can set the channel map. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 034593b..0ed3975 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -114,6 +114,19 @@ struct hdac_hdmi_priv { struct hdac_chmap chmap; }; +static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, + int pcm_idx) +{ + struct hdac_hdmi_pcm *pcm; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pcm_id == pcm_idx) + return pcm; + } + + return NULL; +} + static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev) { struct hdac_device *hdac = dev_to_hdac_dev(dev); @@ -664,6 +677,8 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); mutex_lock(&dai_map->pin->lock); + dai_map->pin->chmap_set = false; + memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap)); dai_map->pin->channels = 0; mutex_unlock(&dai_map->pin->lock); @@ -1386,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = { .pin_eld_notify = hdac_hdmi_eld_notify_cb, }; +static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, + int device) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (rtd->pcm && (rtd->pcm->device == device)) + return rtd->pcm; + } + + return NULL; +} + int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) { char jack_name[NAME_SIZE]; @@ -1395,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) snd_soc_component_get_dapm(&codec->component); struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pcm *pcm; + struct snd_pcm *snd_pcm; + int err; /* * this is a new PCM device, create new pcm and @@ -1406,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device) pcm->pcm_id = device; pcm->cvt = hdmi->dai_map[dai->id].cvt; + snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); + if (snd_pcm) { + err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); + if (err < 0) { + dev_err(&edev->hdac.dev, + "chmap control add failed with err: %d for pcm: %d\n", + err, device); + kfree(pcm); + return err; + } + } + list_add_tail(&pcm->head, &hdmi->pcm_list); sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device); @@ -1524,6 +1566,60 @@ static struct snd_soc_codec_driver hdmi_hda_codec = { .idle_bias_off = true, }; +static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + /* chmap is already set to 0 in caller */ + if (!pin) + return; + + memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap)); +} + +static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + mutex_lock(&pin->lock); + pin->chmap_set = true; + memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap)); + if (prepared) + hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid); + mutex_unlock(&pin->lock); +} + +static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + return pin ? true:false; +} + +static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +{ + struct hdac_ext_device *edev = to_ehdac_device(hdac); + struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_pin *pin = pcm->pin; + + if (!pin && !pin->eld.eld_valid) + return 0; + + return pin->eld.info.spk_alloc; +} + static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) { struct hdac_device *codec = &edev->hdac; @@ -1538,6 +1634,10 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) edev->private_data = hdmi_priv; snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap); + hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; + hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; + hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; + hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; dev_set_drvdata(&codec->dev, edev); -- cgit v0.10.2 From 7e12dc87ac59963cf1765fb8272412db19004987 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:35 +0530 Subject: ASoC: Intel: Skylake: Add multichannel support for HDMI Channel max is changed to 8 from stereo to support multichannel capability for HDMI devices. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dab0900..8de9212 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -51,7 +51,7 @@ static struct snd_pcm_hardware azx_pcm_hw = { .rate_min = 8000, .rate_max = 48000, .channels_min = 1, - .channels_max = HDA_QUAD, + .channels_max = 8, .buffer_bytes_max = AZX_MAX_BUF_SIZE, .period_bytes_min = 128, .period_bytes_max = AZX_MAX_BUF_SIZE / 2, @@ -682,7 +682,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "HDMI1 Playback", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | @@ -697,7 +697,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "HDMI2 Playback", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | @@ -712,7 +712,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "HDMI3 Playback", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | @@ -765,7 +765,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "iDisp1 Tx", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE, @@ -777,7 +777,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "iDisp2 Tx", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | @@ -790,7 +790,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "iDisp3 Tx", .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, + .channels_max = 8, .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000| SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | -- cgit v0.10.2 From ea5a137d0fe263854ae6267a0fa208c544d83452 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 14 Apr 2016 10:07:36 +0530 Subject: ASoC: Intel: Skylake: Update channel map based on runtime params Default channel map is set for 2 channels. Fix the channel map based on runtime params to support multichannel. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 545b4e7..8fceb7a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -154,13 +154,32 @@ static void skl_dump_mconfig(struct skl_sst *ctx, dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg); } +static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs) +{ + int slot_map = 0xFFFFFFFF; + int start_slot = 0; + int i; + + for (i = 0; i < chs; i++) { + /* + * For 2 channels with starting slot as 0, slot map will + * look like 0xFFFFFF10. + */ + slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i))); + start_slot++; + } + fmt->ch_map = slot_map; +} + static void skl_tplg_update_params(struct skl_module_fmt *fmt, struct skl_pipe_params *params, int fixup) { if (fixup & SKL_RATE_FIXUP_MASK) fmt->s_freq = params->s_freq; - if (fixup & SKL_CH_FIXUP_MASK) + if (fixup & SKL_CH_FIXUP_MASK) { fmt->channels = params->ch; + skl_tplg_update_chmap(fmt, fmt->channels); + } if (fixup & SKL_FMT_FIXUP_MASK) { fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt); -- cgit v0.10.2 From 3cc6185bcccff32df41faa97d592a99d258db185 Mon Sep 17 00:00:00 2001 From: Caleb Crome Date: Mon, 25 Apr 2016 11:36:18 -0700 Subject: ASoC: fsl_ssi: add CCSR_SSI_SOR to volatile register list The CCSR_SSI_SOR is a register that clears the TX and/or the RX fifo on the i.MX SSI port. The fsl_ssi_trigger writes this register in order to clear the fifo at trigger time. However, since the CCSR_SSI_SOR register is not in the volatile list, the caching mechanism prevented the register write in the trigger function. This caused the fifo to not be cleared (because the value was unchanged from the last time the register was written), and thus causes the channels in both TDM or simple I2S mode to slip and be in the wrong time slots on SSI restart. This has gone unnoticed for so long because with simple stereo mode, the consequence is that left and right are swapped, which isn't that noticeable. However, it's catestrophic in some systems that require the channels to be in the right slots. Signed-off-by: Caleb Crome Suggested-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ed8de10..08dcbbf 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) case CCSR_SSI_SACDAT: case CCSR_SSI_SATAG: case CCSR_SSI_SACCST: + case CCSR_SSI_SOR: return true; default: return false; -- cgit v0.10.2 From 823ecdd684e28d4e71686fc8787b6d31b1223382 Mon Sep 17 00:00:00 2001 From: Jim Lodes Date: Mon, 25 Apr 2016 11:08:10 -0500 Subject: ASoC: davinci-mcasp: Fix overwriting of ahclkx The mcasp davinci_mcasp_set_dai_fmt function was overriding ahclkx input/output status that had already been set by the davinci_mcasp_set_sysclk function. This commit removes clearing of the ahclkx input/output status from davinci_mcasp_set_dai_fmt. Signed-off-by: Jim Lodes Signed-off-by: J.D. Schroeder Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index e132498..a1197ad 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, - ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR); + ACLKX | AFSX | ACLKR | AHCLKR | AFSR); mcasp->bclk_master = 0; break; default: -- cgit v0.10.2 From 95536d8c29985167e745ff0d8c7cd7dcf4318e6b Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Thu, 28 Apr 2016 18:45:25 +0530 Subject: ASoC: Intel: Skylake: Fix the NULL pointer exception in dsp_clean up If request firmware fails at init, the code loader DMA allocation can be NULL, so check for boot complete before freeing up these resources Signed-off-by: Dharageswari R Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 2962ef2..13c1985 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp) skl_ipc_int_disable(dsp); free_irq(dsp->irq, dsp); - dsp->cl_dev.ops.cl_cleanup_controller(dsp); - skl_cldma_int_disable(dsp); skl_ipc_op_int_disable(dsp); skl_ipc_int_disable(dsp); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index bec4a7c..13ec8d5 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -454,6 +454,10 @@ void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) skl_clear_module_table(ctx->dsp); skl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); + if (ctx->boot_complete) { + ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); + skl_cldma_int_disable(ctx->dsp); + } } EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup); -- cgit v0.10.2 From 76222d6dd2e64c895735ab271ecc8b0df568981d Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Thu, 28 Apr 2016 18:45:26 +0530 Subject: ASoC: Intel: Skylake: Fix memory leak during init instance param_data variable is allocated during set module format of init instance is not getting freed and hence can cause a memory leak. So free it up. Signed-off-by: Mousumi Jana Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index e3d149c..226db84 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -864,7 +864,7 @@ int skl_init_module(struct skl_sst *ctx, return ret; } mconfig->m_state = SKL_MODULE_INIT_DONE; - + kfree(param_data); return ret; } -- cgit v0.10.2 From 1a13b1fafffd41c12a7068c4aa74f5a1d2210a07 Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Thu, 28 Apr 2016 18:45:27 +0530 Subject: ASoC: Intel: Skylake: Prevent sending Set DMA Control IPC if the widget is "On" If widget of a playback or capture DAI is already On, then no need not send the Set DMA Control IPC message to firmware. Signed-off-by: Dharageswari R Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dab0900..4fcf5f8 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -213,7 +213,7 @@ static int skl_be_prepare(struct snd_pcm_substream *substream, struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; - if ((dai->playback_active > 1) || (dai->capture_active > 1)) + if (dai->playback_widget->power || dai->capture_widget->power) return 0; mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream); -- cgit v0.10.2 From 9a655db0201ef523683d700cb3f4508c08bc9d8c Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 28 Apr 2016 18:45:28 +0530 Subject: ASoC: Intel: Skylake: Suspend PCMs when marked as active suspend For 'ignore_suspend' cases we need to keep DSP and pipes On, but can suspend the stream and pause the DMA as we are not rendering data during the suspended time. For this we can check the dai widget ignore_suspend flag in trigger suspend/resume, and start and stop the host DMA and host copier pipelines. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4fcf5f8..b0e7797 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -402,23 +402,33 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl_module_cfg *mconfig; struct hdac_ext_bus *ebus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_soc_dapm_widget *w; int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (!mconfig) return -EIO; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - skl_pcm_prepare(substream, dai); - /* - * enable DMA Resume enable bit for the stream, set the dpib - * & lpib position to resune before starting the DMA - */ - snd_hdac_ext_stream_drsm_enable(ebus, true, - hdac_stream(stream)->index); - snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib); - snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + if (!w->ignore_suspend) { + skl_pcm_prepare(substream, dai); + /* + * enable DMA Resume enable bit for the stream, set the + * dpib & lpib position to resume before starting the + * DMA + */ + snd_hdac_ext_stream_drsm_enable(ebus, true, + hdac_stream(stream)->index); + snd_hdac_ext_stream_set_dpibr(ebus, stream, + stream->dpib); + snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + } case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -448,7 +458,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, return ret; ret = skl_decoupled_trigger(substream, cmd); - if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) { + if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) { /* save the dpib and lpib positions */ stream->dpib = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE + -- cgit v0.10.2 From 551f4bc86807637098786c78afb78418ada4aa1f Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 28 Apr 2016 18:45:29 +0530 Subject: ASoC: Intel: Boards: remove ignore_suspend for WoV streams On WoV we can suspend the DMA and keep the DSP pipelines only On, so remove the ignore_suspend for WoV streams but keep them for WoV endpoints. This helps in achieving better power by suspending DMAs Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 72176b7..ca8063d 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -391,7 +391,6 @@ static struct snd_soc_dai_link skylake_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, .ops = &skylaye_refcap_ops, diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 5f1ca99..a0e3a3f 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -440,7 +440,6 @@ static struct snd_soc_dai_link skylake_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, .ops = &skylaye_refcap_ops, diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c index 2016397a..ef5b17f 100644 --- a/sound/soc/intel/boards/skl_rt286.c +++ b/sound/soc/intel/boards/skl_rt286.c @@ -317,7 +317,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = { .platform_name = "0000:00:1f.3", .init = NULL, .dpcm_capture = 1, - .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, }, -- cgit v0.10.2 From 9ee78757d5dae51decc881b293a39a605c9a6df2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 2 May 2016 13:57:36 +0100 Subject: ASoC: wm_adsp: Add support for TLV based binary controls This patch adds support for the arbitrary length TLV based binary controls. This allows users to properly access controls that are more than 512 bytes in length. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 3ac2e1f..f835277 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -160,6 +160,8 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +#define ADSP_MAX_STD_CTRL_SIZE 512 + struct wm_adsp_buf { struct list_head list; void *buf; @@ -435,6 +437,7 @@ struct wm_coeff_ctl { size_t len; unsigned int set:1; struct snd_kcontrol *kcontrol; + struct soc_bytes_ext bytes_ext; unsigned int flags; }; @@ -711,10 +714,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) be16_to_cpu(scratch[3])); } +static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) +{ + return container_of(ext, struct wm_coeff_ctl, bytes_ext); +} + static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = ctl->len; @@ -763,7 +773,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, static int wm_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); char *p = ucontrol->value.bytes.data; int ret = 0; @@ -780,6 +792,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, + const unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); + + if (copy_from_user(ctl->cache, bytes, size)) { + ret = -EFAULT; + } else { + ctl->set = 1; + if (ctl->enabled) + ret = wm_coeff_write_control(ctl, ctl->cache, size); + } + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, void *buf, size_t len) { @@ -822,7 +857,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, static int wm_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); char *p = ucontrol->value.bytes.data; int ret = 0; @@ -845,12 +882,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, return ret; } +static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, + unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + int ret = 0; + + mutex_lock(&ctl->dsp->pwr_lock); + + if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { + if (ctl->enabled) + ret = wm_coeff_read_control(ctl, ctl->cache, size); + else + ret = -EPERM; + } else { + if (!ctl->flags && ctl->enabled) + ret = wm_coeff_read_control(ctl, ctl->cache, size); + } + + if (!ret && copy_to_user(bytes, ctl->cache, size)) + ret = -EFAULT; + + mutex_unlock(&ctl->dsp->pwr_lock); + + return ret; +} + struct wmfw_ctl_work { struct wm_adsp *dsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; +static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) +{ + unsigned int out, rd, wr, vol; + + if (len > ADSP_MAX_STD_CTRL_SIZE) { + rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; + wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } else { + rd = SNDRV_CTL_ELEM_ACCESS_READ; + wr = SNDRV_CTL_ELEM_ACCESS_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = 0; + } + + if (in) { + if (in & WMFW_CTL_FLAG_READABLE) + out |= rd; + if (in & WMFW_CTL_FLAG_WRITEABLE) + out |= wr; + if (in & WMFW_CTL_FLAG_VOLATILE) + out |= vol; + } else { + out |= rd | wr | vol; + } + + return out; +} + static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; @@ -868,19 +965,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) kcontrol->info = wm_coeff_info; kcontrol->get = wm_coeff_get; kcontrol->put = wm_coeff_put; - kcontrol->private_value = (unsigned long)ctl; + kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kcontrol->tlv.c = snd_soc_bytes_tlv_callback; + kcontrol->private_value = (unsigned long)&ctl->bytes_ext; - if (ctl->flags) { - if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - if (ctl->flags & WMFW_CTL_FLAG_READABLE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; - } else { - kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; - kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; - } + ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + + kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); if (ret < 0) @@ -1032,11 +1125,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl->flags = flags; ctl->offset = offset; - if (len > 512) { - adsp_warn(dsp, "Truncating control %s from %d\n", - ctl->name, len); - len = 512; - } ctl->len = len; ctl->cache = kzalloc(ctl->len, GFP_KERNEL); if (!ctl->cache) { -- cgit v0.10.2 From 8f658815da156a9239b98b34e5ba1d3db71a2f6e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 May 2016 10:42:58 +0300 Subject: ASoC: hdac_hdmi: Potential NULL deref in hdac_hdmi_get_spk_alloc() We intended || here instead of &&. The original code potentially leads to a NULL dereference. Fixes: 2889099eb8cd ('ASoC: hdac_hdmi: Register chmap controls and ops') Signed-off-by: Dan Carpenter Reviewd-by: Takashi Sakamoto Acked-by: Vinod Koul Tested-by: Sachin Mokashi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 0ed3975..f1170e0 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1614,7 +1614,7 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_pin *pin = pcm->pin; - if (!pin && !pin->eld.eld_valid) + if (!pin || !pin->eld.eld_valid) return 0; return pin->eld.info.spk_alloc; -- cgit v0.10.2 From edd713509ae46ffcf178e3b1431af1ca202be8ba Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 17:11:55 +0100 Subject: ASoC: wm_adsp: Move compr_attach/attached functions Move wm_adsp_compr_attach and wm_adsp_compr_attached functions so they will stay logically grouped with similar functions after some additional changes. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 630ebcd..42fc469 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2452,6 +2452,25 @@ void wm_adsp2_remove(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp2_remove); +static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) +{ + return compr->buf != NULL; +} + +static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) +{ + /* + * Note this will be more complex once each DSP can support multiple + * streams + */ + if (!compr->dsp->buffer) + return -EINVAL; + + compr->buf = compr->dsp->buffer; + + return 0; +} + int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr; @@ -2810,25 +2829,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp) return 0; } -static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) -{ - return compr->buf != NULL; -} - -static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) -{ - /* - * Note this will be more complex once each DSP can support multiple - * streams - */ - if (!compr->dsp->buffer) - return -EINVAL; - - compr->buf = compr->dsp->buffer; - - return 0; -} - int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) { struct wm_adsp_compr *compr = stream->runtime->private_data; -- cgit v0.10.2 From 721be3be2f75c69cf0f2d7826007a6eefee7dac3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 17:11:56 +0100 Subject: ASoC: wm_adsp: Detach compressed stream on free If someone powers down the DSP core (through routing changes say) whilst a compressed record is in progress we can end up using a freed pointer to the buffer object. When a compressed audio stream is triggered we attach it to a buffer on a physical DSP. This patch adds a detach of the buffer from the stream when the stream is freed or when the DSP is powered down which avoids the situation where we use a buffer when it is no longer valid. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 42fc469..a07bd7c 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -273,8 +273,11 @@ struct wm_adsp_buffer { __be32 words_written[2]; /* total words written (64 bit) */ }; +struct wm_adsp_compr; + struct wm_adsp_compr_buf { struct wm_adsp *dsp; + struct wm_adsp_compr *compr; struct wm_adsp_buffer_region *regions; u32 host_buf_ptr; @@ -2467,10 +2470,26 @@ static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) return -EINVAL; compr->buf = compr->dsp->buffer; + compr->buf->compr = compr; return 0; } +static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) +{ + if (!compr) + return; + + /* Wake the poll so it can see buffer is no longer attached */ + if (compr->stream) + snd_compr_fragment_elapsed(compr->stream); + + if (wm_adsp_compr_attached(compr)) { + compr->buf->compr = NULL; + compr->buf = NULL; + } +} + int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) { struct wm_adsp_compr *compr; @@ -2524,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) mutex_lock(&dsp->pwr_lock); + wm_adsp_compr_detach(compr); dsp->compr = NULL; kfree(compr->raw_buf); @@ -2820,6 +2840,8 @@ err_buffer: static int wm_adsp_buffer_free(struct wm_adsp *dsp) { if (dsp->buffer) { + wm_adsp_compr_detach(dsp->buffer->compr); + kfree(dsp->buffer->regions); kfree(dsp->buffer); -- cgit v0.10.2 From a6e806c49e3265494ac6fe6ec88ed5c010652e0d Mon Sep 17 00:00:00 2001 From: John Keeping Date: Wed, 4 May 2016 17:21:56 +0100 Subject: ASoC: rockchip: Revert "ASoC: rockchip: i2s: remove unused variables" This reverts commit 5938448b99275cba95167c3f9d39ca9225fdad38. It turns out that the commit that made these variables unused is wrong so we're about to revert it. Bring back the variables in prepration. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 2f8e204..34743ec 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -34,6 +34,13 @@ struct rk_i2s_dev { struct regmap *regmap; +/* + * Used to indicate the tx/rx status. + * I2S controller hopes to start the tx and rx together, + * also to stop them when they are both try to stop. +*/ + bool tx_start; + bool rx_start; bool is_master_mode; }; @@ -77,7 +84,11 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_TXS_START, I2S_XFER_TXS_START); + + i2s->tx_start = true; } else { + i2s->tx_start = false; + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); @@ -115,7 +126,11 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_RXS_START, I2S_XFER_RXS_START); + + i2s->rx_start = true; } else { + i2s->rx_start = false; + regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); -- cgit v0.10.2 From 7e885d211f023dfd201fad8246bbf3c3bd126c61 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Wed, 4 May 2016 17:21:57 +0100 Subject: ASoC: rockchip: Revert "ASoC: rockchip: i2s: separate capture and playback" This reverts commit eba65d179c1149cf79e68608d452631f33d7f017. This broke audio on Veyron Jerry Chromebooks and I now cannot reproduce the problem I was trying to fix even with this commit reverted, so it seems that this was completely the wrong thing to do. Reported-by: Enric Balletbo Serra Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 34743ec..574c6af 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -82,8 +82,8 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START, - I2S_XFER_TXS_START); + I2S_XFER_TXS_START | I2S_XFER_RXS_START, + I2S_XFER_TXS_START | I2S_XFER_RXS_START); i2s->tx_start = true; } else { @@ -92,23 +92,27 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE); - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_TXS_START, - I2S_XFER_TXS_STOP); - - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_TXC, - I2S_CLR_TXC); + if (!i2s->rx_start) { + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_STOP | + I2S_XFER_RXS_STOP); - regmap_read(i2s->regmap, I2S_CLR, &val); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_TXC | I2S_CLR_RXC, + I2S_CLR_TXC | I2S_CLR_RXC); - /* Should wait for clear operation to finish */ - while (val & I2S_CLR_TXC) { regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; + + /* Should wait for clear operation to finish */ + while (val) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } @@ -124,8 +128,8 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE); regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_RXS_START, - I2S_XFER_RXS_START); + I2S_XFER_TXS_START | I2S_XFER_RXS_START, + I2S_XFER_TXS_START | I2S_XFER_RXS_START); i2s->rx_start = true; } else { @@ -134,23 +138,27 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE); - regmap_update_bits(i2s->regmap, I2S_XFER, - I2S_XFER_RXS_START, - I2S_XFER_RXS_STOP); - - regmap_update_bits(i2s->regmap, I2S_CLR, - I2S_CLR_RXC, - I2S_CLR_RXC); + if (!i2s->tx_start) { + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START | + I2S_XFER_RXS_START, + I2S_XFER_TXS_STOP | + I2S_XFER_RXS_STOP); - regmap_read(i2s->regmap, I2S_CLR, &val); + regmap_update_bits(i2s->regmap, I2S_CLR, + I2S_CLR_TXC | I2S_CLR_RXC, + I2S_CLR_TXC | I2S_CLR_RXC); - /* Should wait for clear operation to finish */ - while (val & I2S_CLR_RXC) { regmap_read(i2s->regmap, I2S_CLR, &val); - retry--; - if (!retry) { - dev_warn(i2s->dev, "fail to clear\n"); - break; + + /* Should wait for clear operation to finish */ + while (val) { + regmap_read(i2s->regmap, I2S_CLR, &val); + retry--; + if (!retry) { + dev_warn(i2s->dev, "fail to clear\n"); + break; + } } } } -- cgit v0.10.2 From 381437dd0bd590902320b97e6512792b075becd4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 5 May 2016 11:13:31 +0800 Subject: ASoC: rt5645: polling jd status in all conditions We only polling jd status when rt5645->pdata.jd_invert is true. However, it should be done at all time since there will be no interrupt for jd if we press a headset button and remove the headset at the same time. Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index dff706a..3c6594d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work) if (btn_type == 0)/* button release */ report = rt5645->jack_type; else { - if (rt5645->pdata.jd_invert) { - mod_timer(&rt5645->btn_check_timer, - msecs_to_jiffies(100)); - } + mod_timer(&rt5645->btn_check_timer, + msecs_to_jiffies(100)); } break; @@ -3816,9 +3814,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, if (rt5645->pdata.jd_invert) { regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); - setup_timer(&rt5645->btn_check_timer, - rt5645_btn_check_callback, (unsigned long)rt5645); } + setup_timer(&rt5645->btn_check_timer, + rt5645_btn_check_callback, (unsigned long)rt5645); INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); -- cgit v0.10.2 From 5181365f5312d67dcdc9e4bc22516c48a83c8754 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Thu, 5 May 2016 11:53:06 +0100 Subject: ASoC: da7219: Add initial ACPI id for device This adds "DLGS7219" ACPI id for the codec. Signed-off-by: Adam Thomson Tested-by: Sathyanarayana Nujella Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index caea2ee..17e2119 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1426,6 +1426,12 @@ static const struct of_device_id da7219_of_match[] = { }; MODULE_DEVICE_TABLE(of, da7219_of_match); +static const struct acpi_device_id da7219_acpi_match[] = { + { .id = "DLGS7219", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, da7219_acpi_match); + static enum da7219_micbias_voltage da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val) { @@ -1955,6 +1961,7 @@ static struct i2c_driver da7219_i2c_driver = { .driver = { .name = "da7219", .of_match_table = of_match_ptr(da7219_of_match), + .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, .probe = da7219_i2c_probe, .remove = da7219_i2c_remove, -- cgit v0.10.2 From 1593af62b694b3638edf577e3b763fa1a4ca3d76 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 May 2016 19:33:58 -0300 Subject: ASoC: fsl_sai: Introduce a compatible string for MX6UL MX6UL may need to configure the General Purpose Register 1 (GPR1), so it is better to add a new compatible string to differentiate. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 044e5d7..777b941 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -7,8 +7,8 @@ codec/DSP interfaces. Required properties: - - compatible : Compatible list, contains "fsl,vf610-sai" or - "fsl,imx6sx-sai". + - compatible : Compatible list, contains "fsl,vf610-sai", + "fsl,imx6sx-sai" or "fsl,imx6ul-sai" - reg : Offset and length of the register set for the device. diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 0754df7..d8b673f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -797,7 +797,8 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->pdev = pdev; - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") || + of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) sai->sai_on_imx = true; sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); @@ -898,6 +899,7 @@ static int fsl_sai_probe(struct platform_device *pdev) static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,imx6sx-sai", }, + { .compatible = "fsl,imx6ul-sai", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); -- cgit v0.10.2 From 4d2458507d0b465c62ae80f3e81b8c008ec96b05 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 May 2016 19:33:59 -0300 Subject: ASoC: fsl_sai: Allow setting the SAI MCLK direction On mx6ul the General Purpose Register 1 (GPR1) contains the following bits for configuring the direction of the SAI MCLKs: SAI1_MCLK_DIR, SAI2_MCLK_DIR, SAI3_MCLK_DIR Introduce the "fsl,sai-mclk-direction-output" optional property to allow configuring the SAI_MCLK outputs. Tested on a imx6ul-evk board. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 777b941..740b467 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -48,6 +48,11 @@ Required properties: receive data by following their own bit clocks and frame sync clocks separately. +Optional properties (for mx6ul): + + - fsl,sai-mclk-direction-output: This is a boolean property. If present, + indicates that SAI will output the SAI MCLK clock. + Note: - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the default synchronous mode (sync Rx with Tx) will be used, which means both diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index 238c8db..6835382 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -447,5 +447,11 @@ #define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18) #define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17) +#define IMX6UL_GPR1_SAI1_MCLK_DIR (0x1 << 19) +#define IMX6UL_GPR1_SAI2_MCLK_DIR (0x1 << 20) +#define IMX6UL_GPR1_SAI3_MCLK_DIR (0x1 << 21) +#define IMX6UL_GPR1_SAI_MCLK_MASK (0x7 << 19) +#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \ + IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR) #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */ diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index d8b673f..2147994 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "fsl_sai.h" #include "imx-pcm.h" @@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; + struct regmap *gpr; struct resource *res; void __iomem *base; char tmp[8]; int irq, ret, i; + int index; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -878,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev) fsl_sai_dai.symmetric_samplebits = 0; } + if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && + of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) { + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); + if (IS_ERR(gpr)) { + dev_err(&pdev->dev, "cannot find iomuxc registers\n"); + return PTR_ERR(gpr); + } + + index = of_alias_get_id(np, "sai"); + if (index < 0) + return index; + + regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index), + MCLK_DIR(index)); + } + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; -- cgit v0.10.2 From c286b3f9600b2ddc573208792d947e1a251c6b15 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 5 May 2016 11:19:19 +0530 Subject: ASoC: Intel: Skylake: Fix memory leak in nhlt init During skl_nhlt_init(), acpi obj pointer is allocated and never freed and remap address is not unmapped. To fix this we should release the ACPI obj and also unmap the nhlt address during cleanup of driver. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 14d1916e..7d73648 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" -void *skl_nhlt_init(struct device *dev) +struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { acpi_handle handle; union acpi_object *obj; struct nhlt_resource_desc *nhlt_ptr = NULL; + struct nhlt_acpi_table *nhlt_table = NULL; if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { dev_err(dev, "Requested NHLT device not found\n"); @@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev) obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); if (obj && obj->type == ACPI_TYPE_BUFFER) { nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - - return memremap(nhlt_ptr->min_addr, nhlt_ptr->length, + nhlt_table = (struct nhlt_acpi_table *) + memremap(nhlt_ptr->min_addr, nhlt_ptr->length, MEMREMAP_WB); + ACPI_FREE(obj); + return nhlt_table; } dev_err(dev, "device specific method to extract NHLT blob failed\n"); return NULL; } -void skl_nhlt_free(void *addr) +void skl_nhlt_free(struct nhlt_acpi_table *nhlt) { - memunmap(addr); + memunmap((void *) nhlt); } static struct nhlt_specific_cfg *skl_get_specific_cfg( @@ -120,7 +123,7 @@ struct nhlt_specific_cfg struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct device *dev = bus->dev; struct nhlt_specific_cfg *sp_config; - struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; + struct nhlt_acpi_table *nhlt = skl->nhlt; u16 bps = (s_fmt == 16) ? 16 : 32; u8 j; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 3982f55..83e985c 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -643,7 +643,7 @@ static int skl_probe(struct pci_dev *pci, err = skl_machine_device_register(skl, (void *)pci_id->driver_data); if (err < 0) - goto out_free; + goto out_nhlt_free; err = skl_init_dsp(skl); if (err < 0) { @@ -693,6 +693,8 @@ out_dsp_free: skl_free_dsp(skl); out_mach_free: skl_machine_device_unregister(skl); +out_nhlt_free: + skl_nhlt_free(skl->nhlt); out_free: skl->init_failed = 1; skl_free(ebus); @@ -743,6 +745,7 @@ static void skl_remove(struct pci_dev *pci) skl_free_dsp(skl); skl_machine_device_unregister(skl); skl_dmic_device_unregister(skl); + skl_nhlt_free(skl->nhlt); skl_free(ebus); dev_set_drvdata(&pci->dev, NULL); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 39e16fa..4b4b387 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -66,7 +66,7 @@ struct skl { struct platform_device *dmic_dev; struct platform_device *i2s_dev; - void *nhlt; /* nhlt ptr */ + struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ struct skl_dsp_resource resource; @@ -103,8 +103,8 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -void *skl_nhlt_init(struct device *dev); -void skl_nhlt_free(void *addr); +struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); +void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn); -- cgit v0.10.2 From b58cea7355875d6ae7aacb66c105f5c99f489909 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 6 May 2016 18:13:17 +0100 Subject: ASoC: da7129: Add missing include of acpi.h Reported-by: Stephen Rothwell Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 17e2119..5c93899 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -11,6 +11,7 @@ * option) any later version. */ +#include #include #include #include -- cgit v0.10.2 From aeea2fdd9b623fcd6991ac3617ef6a3b646c2899 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 7 May 2016 20:04:52 -0300 Subject: MAINTAINERS: Add myself as reviewer of FSL/NXP SoC sound drivers I would like to help reviewing FSL/NXP SoC sound drivers. Signed-off-by: Fabio Estevam Acked-by: Timur Tabi Signed-off-by: Mark Brown diff --git a/MAINTAINERS b/MAINTAINERS index 03e00c7..734bd0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS M: Timur Tabi M: Nicolin Chen M: Xiubo Li +R: Fabio Estevam L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Maintained -- cgit v0.10.2 From 19357366633cfc53532b587180af3655f0e453f3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 May 2016 13:39:14 +0300 Subject: ASoC: davinci-mcasp: Do not allow multiple streams in one direction Make sure that the user can not start multiple streams with the same direction. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index e132498..020d866 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1230,11 +1230,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, int i, dir; int tdm_slots = mcasp->tdm_slots; - if (mcasp->tdm_mask[substream->stream]) - tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); + /* Do not allow more then one stream per direction */ + if (mcasp->substreams[substream->stream]) + return -EBUSY; mcasp->substreams[substream->stream] = substream; + if (mcasp->tdm_mask[substream->stream]) + tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) return 0; -- cgit v0.10.2 From 20d4b10730183a02851580f072bd9b0122873dc5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 May 2016 13:42:29 +0300 Subject: ASoC: davinci-mcasp: Use defines for clkdiv IDs Instead of hardwired IDs add defines for the available dividers. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 020d866..adf1c39 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -547,14 +547,14 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, pm_runtime_get_sync(mcasp->dev); switch (div_id) { - case 0: /* MCLK divider */ + case MCASP_CLKDIV_AUXCLK: /* MCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(div - 1), AHCLKXDIV_MASK); mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRDIV(div - 1), AHCLKRDIV_MASK); break; - case 1: /* BCLK divider */ + case MCASP_CLKDIV_BCLK: /* BCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXDIV(div - 1), ACLKXDIV_MASK); mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, @@ -563,7 +563,8 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, mcasp->bclk_div = div; break; - case 2: /* + case MCASP_CLKDIV_BCLK_FS_RATIO: + /* * BCLK/LRCLK ratio descries how many bit-clock cycles * fit into one frame. The clock ratio is given for a * full period of data (for I2S format both left and diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index a3be108..1e8787f 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -306,4 +306,9 @@ #define NUMEVT(x) (((x) & 0xFF) << 8) #define NUMDMA_MASK (0xFF) +/* clock divider IDs */ +#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */ +#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */ +#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */ + #endif /* DAVINCI_MCASP_H */ -- cgit v0.10.2 From 226e73e23b6b7f7d6df47562a7555ddb121163cf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 May 2016 13:42:30 +0300 Subject: ASoC: davinci-mcasp: Change __davinci_mcasp_set_clkdiv() first parameter Change the first parameter to struct davinci_mcasp* from struct snd_soc_dai* The function internally does not use or need the DAI information. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index adf1c39..99061c4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -540,11 +540,9 @@ out: return ret; } -static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, +static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, int div, bool explicit) { - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - pm_runtime_get_sync(mcasp->dev); switch (div_id) { case MCASP_CLKDIV_AUXCLK: /* MCLK divider */ @@ -592,7 +590,9 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { - return __davinci_mcasp_set_clkdiv(dai, div_id, div, 1); + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1); } static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, @@ -1056,7 +1056,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", ppm); - __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0); + __davinci_mcasp_set_clkdiv(mcasp, 1, div, 0); } ret = mcasp_common_hw_param(mcasp, substream->stream, -- cgit v0.10.2 From 3e9bee11d83190b852d428b3e35a942c6e2293cd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 May 2016 13:42:31 +0300 Subject: ASoC: davinci-mcasp: Restructure the davinci_mcasp_calc_clk_div() Change the return value to error_pmm instead of the BCLK div and handle the divider configuration to McASP within the function when the set flag is true. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 99061c4..58fe112 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1000,9 +1000,9 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, } static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, - unsigned int bclk_freq, - int *error_ppm) + unsigned int bclk_freq, bool set) { + int error_ppm; int div = mcasp->sysclk_freq / bclk_freq; int rem = mcasp->sysclk_freq % bclk_freq; @@ -1014,13 +1014,18 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, rem = rem - bclk_freq; } } - if (error_ppm) - *error_ppm = - (div*1000000 + (int)div64_long(1000000LL*rem, - (int)bclk_freq)) - /div - 1000000; + error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem, + (int)bclk_freq)) / div - 1000000; - return div; + if (set) { + if (error_ppm) + dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", + error_ppm); + + __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); + } + + return error_ppm; } static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, @@ -1045,18 +1050,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int slots = mcasp->tdm_slots; int rate = params_rate(params); int sbits = params_width(params); - int ppm, div; if (mcasp->slot_width) sbits = mcasp->slot_width; - div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, - &ppm); - if (ppm) - dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", - ppm); - - __davinci_mcasp_set_clkdiv(mcasp, 1, div, 0); + davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true); } ret = mcasp_common_hw_param(mcasp, substream->stream, @@ -1167,7 +1165,8 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, davinci_mcasp_dai_rates[i]; int ppm; - davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, + false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { if (range.empty) { range.min = davinci_mcasp_dai_rates[i]; @@ -1206,8 +1205,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, if (rd->mcasp->slot_width) sbits = rd->mcasp->slot_width; - davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate, - &ppm); + ppm = davinci_mcasp_calc_clk_div(rd->mcasp, + sbits * slots * rate, + false); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set(&nfmt, i); count++; -- cgit v0.10.2 From ddecd1492de476488a92493510fb86c6ffe9acbd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 9 May 2016 13:42:32 +0300 Subject: ASoC: davinci-mcasp: Calculate AUXCLK divider when setting up master clocks If the McASP is used as clock master and the reference clock is AUXCLK we can have additional level of divider. The BCLK divider is limited to maximum 32, if the desired bclk can not be reached with this, the AUXCLK divider also needs to be used. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 58fe112..f390bb4 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1003,13 +1003,31 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, unsigned int bclk_freq, bool set) { int error_ppm; - int div = mcasp->sysclk_freq / bclk_freq; - int rem = mcasp->sysclk_freq % bclk_freq; + unsigned int sysclk_freq = mcasp->sysclk_freq; + u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG); + int div = sysclk_freq / bclk_freq; + int rem = sysclk_freq % bclk_freq; + int aux_div = 1; + + if (div > (ACLKXDIV_MASK + 1)) { + if (reg & AHCLKXE) { + aux_div = div / (ACLKXDIV_MASK + 1); + if (div % (ACLKXDIV_MASK + 1)) + aux_div++; + + sysclk_freq /= aux_div; + div = sysclk_freq / bclk_freq; + rem = sysclk_freq % bclk_freq; + } else if (set) { + dev_warn(mcasp->dev, "Too fast reference clock (%u)\n", + sysclk_freq); + } + } if (rem != 0) { if (div == 0 || - ((mcasp->sysclk_freq / div) - bclk_freq) > - (bclk_freq - (mcasp->sysclk_freq / (div+1)))) { + ((sysclk_freq / div) - bclk_freq) > + (bclk_freq - (sysclk_freq / (div+1)))) { div++; rem = rem - bclk_freq; } @@ -1023,6 +1041,9 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, error_ppm); __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); + if (reg & AHCLKXE) + __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK, + aux_div, 0); } return error_ppm; -- cgit v0.10.2 From 420c470d6b5c2924a3182edf5b002870ff770331 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:29 +0100 Subject: ASoC: es8328: Move clock setup to hw_params This ensures that the clock is setup after its frequency has been set; the existing code in set_dai_fmt may be called before the clock rate has been set resulting in an incorrect configuration. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index afa6c5d..3ca89ae 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -445,9 +445,10 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int clk_rate; + int clk_rate = clk_get_rate(es8328->clk); int i; int reg; + int val; u8 ratio; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -455,16 +456,24 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, else reg = ES8328_ADCCONTROL5; - clk_rate = clk_get_rate(es8328->clk); - - if ((clk_rate != ES8328_SYSCLK_RATE_1X) && - (clk_rate != ES8328_SYSCLK_RATE_2X)) { + switch (clk_rate) { + case ES8328_SYSCLK_RATE_1X: + val = 0; + break; + case ES8328_SYSCLK_RATE_2X: + val = ES8328_MASTERMODE_MCLKDIV2; + break; + default: dev_err(codec->dev, "%s: clock is running at %d Hz, not %d or %d Hz\n", __func__, clk_rate, ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); return -EINVAL; } + ret = snd_soc_update_bits(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MCLKDIV2, val); + if (ret < 0) + return ret; /* find master mode MCLK to sampling frequency ratio */ ratio = mclk_ratios[0].rate; @@ -484,8 +493,6 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int clk_rate; u8 mode = ES8328_DACCONTROL1_DACWL_16; /* set master/slave audio interface */ @@ -515,14 +522,8 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, snd_soc_write(codec, ES8328_ADCCONTROL4, mode); /* Master serial port mode, with BCLK generated automatically */ - clk_rate = clk_get_rate(es8328->clk); - if (clk_rate == ES8328_SYSCLK_RATE_1X) - snd_soc_write(codec, ES8328_MASTERMODE, - ES8328_MASTERMODE_MSC); - else - snd_soc_write(codec, ES8328_MASTERMODE, - ES8328_MASTERMODE_MCLKDIV2 | - ES8328_MASTERMODE_MSC); + snd_soc_update_bits(codec, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC); return 0; } -- cgit v0.10.2 From 57e41f3fb32a359753a3b2679c2502b2750bf6af Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:30 +0100 Subject: ASoC: es8328: Fix ADC format setup The ADCCONTROL4 and DACCONTROL1 registers are similar but not identical, with the DACCONTROL1 having each field starting one bit higher than ADCCONTROL4. Instead of introducing a magic shift, add new constants for the values in ADCCONTROL4 and use a second variable to setup the ADC. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 3ca89ae..63e8262 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -493,7 +493,8 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u8 mode = ES8328_DACCONTROL1_DACWL_16; + u8 dac_mode = ES8328_DACCONTROL1_DACWL_16; + u8 adc_mode = ES8328_ADCCONTROL4_ADCWL_16; /* set master/slave audio interface */ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) @@ -502,13 +503,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S; break; case SND_SOC_DAIFMT_RIGHT_J: - mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST; break; case SND_SOC_DAIFMT_LEFT_J: - mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST; break; default: return -EINVAL; @@ -518,8 +522,8 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) return -EINVAL; - snd_soc_write(codec, ES8328_DACCONTROL1, mode); - snd_soc_write(codec, ES8328_ADCCONTROL4, mode); + snd_soc_write(codec, ES8328_DACCONTROL1, dac_mode); + snd_soc_write(codec, ES8328_ADCCONTROL4, adc_mode); /* Master serial port mode, with BCLK generated automatically */ snd_soc_update_bits(codec, ES8328_MASTERMODE, diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 156c748..5a4af01 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -84,7 +84,22 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL1 0x09 #define ES8328_ADCCONTROL2 0x0a #define ES8328_ADCCONTROL3 0x0b + #define ES8328_ADCCONTROL4 0x0c +#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0) +#define ES8328_ADCCONTROL4_ADCWL_24 (0 << 2) +#define ES8328_ADCCONTROL4_ADCWL_20 (1 << 2) +#define ES8328_ADCCONTROL4_ADCWL_18 (2 << 2) +#define ES8328_ADCCONTROL4_ADCWL_16 (3 << 2) +#define ES8328_ADCCONTROL4_ADCWL_32 (4 << 2) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5) + #define ES8328_ADCCONTROL5 0x0d #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) -- cgit v0.10.2 From 2da1ab667a506cc6a7dea88b70e6df3d281458f8 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:31 +0100 Subject: ASoC: es8328: Fix mask for VMIDSEL This is always used along with ES8328_CONTROL1_ENREF so there is no change in the generated code as a result of this fix. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 5a4af01..8bc79ff 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) -#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0) +#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0) #define ES8328_CONTROL1_ENREF (1 << 2) #define ES8328_CONTROL1_SEQEN (1 << 3) #define ES8328_CONTROL1_SAMEFS (1 << 4) -- cgit v0.10.2 From f2ed04a4317e5c8074d98a5c1da175596811a2d8 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:32 +0100 Subject: ASoC: es8328: Use single R/W for regmap The chip only supports single reads and writes. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 63e8262..d580300 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -713,6 +713,7 @@ const struct regmap_config es8328_regmap_config = { .val_bits = 8, .max_register = ES8328_REG_MAX, .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, }; EXPORT_SYMBOL_GPL(es8328_regmap_config); -- cgit v0.10.2 From 8865c95e43257e6676bc0f6b042ecce17eff74fe Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:34 +0100 Subject: ASoC: es8328: Move sample size setup to hw_params This is a refactor in preparation for supporting more sample sizes which has no functional change. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index d580300..c5a36e6 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -482,9 +482,16 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, ratio = mclk_ratios[i].ratio; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_update_bits(codec, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACWL_MASK, + ES8328_DACCONTROL1_DACWL_16); + es8328->playback_fs = params_rate(params); es8328_set_deemph(codec); - } + } else + snd_soc_update_bits(codec, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCWL_MASK, + ES8328_ADCCONTROL4_ADCWL_16); return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); } @@ -493,8 +500,8 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u8 dac_mode = ES8328_DACCONTROL1_DACWL_16; - u8 adc_mode = ES8328_ADCCONTROL4_ADCWL_16; + u8 dac_mode = 0; + u8 adc_mode = 0; /* set master/slave audio interface */ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) @@ -522,8 +529,10 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) return -EINVAL; - snd_soc_write(codec, ES8328_DACCONTROL1, dac_mode); - snd_soc_write(codec, ES8328_ADCCONTROL4, adc_mode); + snd_soc_update_bits(codec, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode); + snd_soc_update_bits(codec, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); /* Master serial port mode, with BCLK generated automatically */ snd_soc_update_bits(codec, ES8328_MASTERMODE, diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 8bc79ff..9c33d8b 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -86,6 +86,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL3 0x0b #define ES8328_ADCCONTROL4 0x0c +#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0) #define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0) #define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) #define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) @@ -95,6 +96,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL4_ADCWL_18 (2 << 2) #define ES8328_ADCCONTROL4_ADCWL_16 (3 << 2) #define ES8328_ADCCONTROL4_ADCWL_32 (4 << 2) +#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2) #define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) #define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) #define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5) @@ -124,6 +126,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL14 0x16 #define ES8328_DACCONTROL1 0x17 +#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1) #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) @@ -133,6 +136,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_DACCONTROL1_DACWL_18 (2 << 3) #define ES8328_DACCONTROL1_DACWL_16 (3 << 3) #define ES8328_DACCONTROL1_DACWL_32 (4 << 3) +#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) -- cgit v0.10.2 From 779e86a31402c3f33f20bb02e99a5b75595bdf7f Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:35 +0100 Subject: ASoC: es8328: Support more sample formats The values are the same for the DAC and ADC so remove the specific values and use values with shifts. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index c5a36e6..a66c21c 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -60,7 +60,11 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = { #define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_11025) -#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) struct es8328_priv { struct regmap *regmap; @@ -449,6 +453,7 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, int i; int reg; int val; + int wl; u8 ratio; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -470,10 +475,28 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); return -EINVAL; } - ret = snd_soc_update_bits(codec, ES8328_MASTERMODE, + snd_soc_update_bits(codec, ES8328_MASTERMODE, ES8328_MASTERMODE_MCLKDIV2, val); - if (ret < 0) - return ret; + + switch (params_width(params)) { + case 16: + wl = 3; + break; + case 18: + wl = 2; + break; + case 20: + wl = 1; + break; + case 24: + wl = 0; + break; + case 32: + wl = 4; + break; + default: + return -EINVAL; + } /* find master mode MCLK to sampling frequency ratio */ ratio = mclk_ratios[0].rate; @@ -484,14 +507,14 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { snd_soc_update_bits(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_MASK, - ES8328_DACCONTROL1_DACWL_16); + wl << ES8328_DACCONTROL1_DACWL_SHIFT); es8328->playback_fs = params_rate(params); es8328_set_deemph(codec); } else snd_soc_update_bits(codec, ES8328_ADCCONTROL4, ES8328_ADCCONTROL4_ADCWL_MASK, - ES8328_ADCCONTROL4_ADCWL_16); + wl << ES8328_ADCCONTROL4_ADCWL_SHIFT); return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); } diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 9c33d8b..1a736e7 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -91,11 +91,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) #define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) #define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0) -#define ES8328_ADCCONTROL4_ADCWL_24 (0 << 2) -#define ES8328_ADCCONTROL4_ADCWL_20 (1 << 2) -#define ES8328_ADCCONTROL4_ADCWL_18 (2 << 2) -#define ES8328_ADCCONTROL4_ADCWL_16 (3 << 2) -#define ES8328_ADCCONTROL4_ADCWL_32 (4 << 2) +#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2 #define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2) #define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) #define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) @@ -131,11 +127,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap); #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) -#define ES8328_DACCONTROL1_DACWL_24 (0 << 3) -#define ES8328_DACCONTROL1_DACWL_20 (1 << 3) -#define ES8328_DACCONTROL1_DACWL_18 (2 << 3) -#define ES8328_DACCONTROL1_DACWL_16 (3 << 3) -#define ES8328_DACCONTROL1_DACWL_32 (4 << 3) +#define ES8328_DACCONTROL1_DACWL_SHIFT 3 #define ES8328_DACCONTROL1_DACWL_MASK (7 << 3) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) -- cgit v0.10.2 From 45749c918129e409c44777f051dc0a5afb689459 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:36 +0100 Subject: ASoC: es8328: Support more sample rates Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index a66c21c..b8ca214 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -26,18 +26,30 @@ #include #include "es8328.h" -#define ES8328_SYSCLK_RATE_1X 11289600 -#define ES8328_SYSCLK_RATE_2X 22579200 +static const unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; -/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */ -static struct { - int rate; - u8 ratio; -} mclk_ratios[] = { - { 8000, 9 }, - {11025, 7 }, - {22050, 4 }, - {44100, 2 }, +static const int ratios_12288[] = { + 10, 7, 6, 4, 3, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_11289[] = { + 8018, 11025, 22050, 44100, 88200, +}; + +static const int ratios_11289[] = { + 9, 7, 4, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, }; /* regulator supplies for sgtl5000, VDDD is an optional external supply */ @@ -57,9 +69,14 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = { "HPVDD", }; -#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \ +#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_22050 | \ - SNDRV_PCM_RATE_11025) + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_8000) #define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S18_3LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ @@ -71,6 +88,9 @@ struct es8328_priv { struct clk *clk; int playback_fs; bool deemph; + int mclkdiv2; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; + const int *mclk_ratios; struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; }; @@ -443,40 +463,55 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute) mute ? ES8328_DACCONTROL3_DACMUTE : 0); } +static int es8328_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + + if (es8328->sysclk_constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + es8328->sysclk_constraints); + + return 0; +} + static int es8328_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); - int clk_rate = clk_get_rate(es8328->clk); int i; int reg; - int val; int wl; - u8 ratio; + int ratio; + + if (!es8328->sysclk_constraints) { + dev_err(codec->dev, "No MCLK configured\n"); + return -EINVAL; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = ES8328_DACCONTROL2; else reg = ES8328_ADCCONTROL5; - switch (clk_rate) { - case ES8328_SYSCLK_RATE_1X: - val = 0; - break; - case ES8328_SYSCLK_RATE_2X: - val = ES8328_MASTERMODE_MCLKDIV2; - break; - default: - dev_err(codec->dev, - "%s: clock is running at %d Hz, not %d or %d Hz\n", - __func__, clk_rate, - ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X); + for (i = 0; i < es8328->sysclk_constraints->count; i++) + if (es8328->sysclk_constraints->list[i] == params_rate(params)) + break; + + if (i == es8328->sysclk_constraints->count) { + dev_err(codec->dev, "LRCLK %d unsupported with current clock\n", + params_rate(params)); return -EINVAL; } + + ratio = es8328->mclk_ratios[i]; snd_soc_update_bits(codec, ES8328_MASTERMODE, - ES8328_MASTERMODE_MCLKDIV2, val); + ES8328_MASTERMODE_MCLKDIV2, + es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0); switch (params_width(params)) { case 16: @@ -498,12 +533,6 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - /* find master mode MCLK to sampling frequency ratio */ - ratio = mclk_ratios[0].rate; - for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++) - if (params_rate(params) <= mclk_ratios[i].rate) - ratio = mclk_ratios[i].ratio; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { snd_soc_update_bits(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_MASK, @@ -519,6 +548,40 @@ static int es8328_hw_params(struct snd_pcm_substream *substream, return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio); } +static int es8328_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec); + int mclkdiv2 = 0; + + switch (freq) { + case 0: + es8328->sysclk_constraints = NULL; + es8328->mclk_ratios = NULL; + break; + case 22579200: + mclkdiv2 = 1; + /* fallthru */ + case 11289600: + es8328->sysclk_constraints = &constraints_11289; + es8328->mclk_ratios = ratios_11289; + break; + case 24576000: + mclkdiv2 = 1; + /* fallthru */ + case 12288000: + es8328->sysclk_constraints = &constraints_12288; + es8328->mclk_ratios = ratios_12288; + break; + default: + return -EINVAL; + } + + es8328->mclkdiv2 = mclkdiv2; + return 0; +} + static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -616,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec, } static const struct snd_soc_dai_ops es8328_dai_ops = { + .startup = es8328_startup, .hw_params = es8328_hw_params, .digital_mute = es8328_mute, + .set_sysclk = es8328_set_sysclk, .set_fmt = es8328_set_dai_fmt, }; -- cgit v0.10.2 From ca0d8797397c5daa6260a6c67b845d79f65140f5 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Mon, 9 May 2016 12:24:37 +0100 Subject: ASoC: es8328: Set symmetric rates Although the ES8328 does support different rates for capture and playback, only very limited combinations are supported (8kHz and 48kHz or 8.0182kHz and 44.1kHz) with most rates required to be symmetric. Instead of adding a lot of complexity for little gain, let's enforce symmetric rates. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index b8ca214..2086d71 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -703,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = { .formats = ES8328_FORMATS, }, .ops = &es8328_dai_ops, + .symmetric_rates = 1, }; static int es8328_suspend(struct snd_soc_codec *codec) -- cgit v0.10.2 From fcc494af3cfaefc9f8a51c3c7e7f208a0553b28f Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 10 May 2016 22:02:05 +0530 Subject: ASoC: Intel: Skylake: Add more SSP DAIs The Broxton-P platform has 6 SSPs so we need to add ssp2 thru ssp5 to DAI list for the driver. Signed-off-by: Pardha Saradhi K Signed-off-by: Ramesh Babu Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b0e7797..4494db6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -770,6 +770,78 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { + .name = "SSP2 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp2 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP3 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp3 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP4 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp4 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp4 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = "SSP5 Pin", + .ops = &skl_be_ssp_dai_ops, + .playback = { + .stream_name = "ssp5 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp5 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, .playback = { -- cgit v0.10.2 From 76016322ec5670052fdabb08c586d6b16bd5062f Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Tue, 10 May 2016 22:02:06 +0530 Subject: ASoC: Intel: Add Broxton-P machine driver This patch adds the Broxton-P machine driver for Intel Broxton-P reference boards. This machine uses the RT298 codec Signed-off-by: Ramesh Babu Signed-off-by: Senthilnathan Veppur Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 399afa1..91c15ab 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y if you have such a device If unsure select "N". +config SND_SOC_INTEL_BXT_RT298_MACH + tristate "ASoC Audio driver for Broxton with RT298 I2S mode" + depends on X86 && ACPI && I2C + select SND_SOC_INTEL_SST + select SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT298 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton platforms + with RT286 I2S audio codec. + Say Y if you have such a device + If unsure select "N". + config SND_SOC_INTEL_BYT_RT5640_MACH tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" depends on X86_INTEL_LPSS && I2C diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 3310c0f..a850677 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -2,6 +2,7 @@ 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-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -14,6 +15,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.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_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.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-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c new file mode 100644 index 0000000..1b845ff --- /dev/null +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -0,0 +1,353 @@ +/* + * Intel Broxton-P I2S Machine Driver + * + * Copyright (C) 2014-2016, Intel Corporation. All rights reserved. + * + * Modified from: + * Intel Skylake I2S Machine driver + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include "../../codecs/hdac_hdmi.h" +#include "../../codecs/rt298.h" + +static struct snd_soc_jack broxton_headset; +/* Headset jack detection DAPM pins */ + +enum { + BXT_DPCM_AUDIO_PB = 0, + BXT_DPCM_AUDIO_CP, + BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_HDMI1_PB, + BXT_DPCM_AUDIO_HDMI2_PB, + BXT_DPCM_AUDIO_HDMI3_PB, +}; + +static struct snd_soc_jack_pin broxton_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new broxton_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack detect */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMic", NULL, "SoC DMIC"}, + + {"HDMI1", NULL, "hif5 Output"}, + {"HDMI2", NULL, "hif6 Output"}, + {"HDMI3", NULL, "hif7 Output"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "codec0_out"}, + + { "codec0_in", NULL, "ssp5 Rx" }, + { "ssp5 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "Capture" }, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + +}; + +static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret = 0; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &broxton_headset, + broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins)); + + if (ret) + return ret; + + rt298_mic_detect(codec, &broxton_headset); + return 0; +} + +static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->codec_dai; + + return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id); +} + +static int broxton_ssp5_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); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP5 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int broxton_rt298_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; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, + 19200000, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + return ret; +} + +static struct snd_soc_ops broxton_rt298_ops = { + .hw_params = broxton_rt298_hw_params, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Front End DAI links */ + [BXT_DPCM_AUDIO_PB] + { + .name = "Bxt Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + [BXT_DPCM_AUDIO_CP] + { + .name = "Bxt Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + [BXT_DPCM_AUDIO_REF_CP] + { + .name = "Bxt Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI1_PB] + { + .name = "Bxt HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI2_PB] + { + .name = "Bxt HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [BXT_DPCM_AUDIO_HDMI3_PB] + { + .name = "Bxt HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + /* Back End DAI links */ + { + /* SSP5 - Codec */ + .name = "SSP5-Codec", + .be_id = 0, + .cpu_dai_name = "SSP5 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt298-aif1", + .init = broxton_rt298_codec_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp5_fixup, + .ops = &broxton_rt298_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .be_id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .be_id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .be_id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .be_id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:0e.0", + .init = broxton_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-rt298", + .owner = THIS_MODULE, + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = broxton_controls, + .num_controls = ARRAY_SIZE(broxton_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298); +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .driver = { + .name = "bxt_alc298s_i2s", + }, +}; +module_platform_driver(broxton_audio) + +/* Module information */ +MODULE_AUTHOR("Ramesh Babu "); +MODULE_AUTHOR("Senthilnathan Veppur "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_alc298s_i2s"); -- cgit v0.10.2 From a0d5caeaebfd00853efa0080afc850e10be7b39a Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 10 May 2016 16:11:04 +0100 Subject: ASoC: da7213: Add DAI DAPM event to control DAI clocks Currently, when Codec is I2S master DAI clocks are continuously generated even if all audio streams have stopped. To improve efficiency, control of the DAI clocks for master mode have been moved to a DAPM widget event so they're only enabled as required. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 7278f93..701bd62 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -726,6 +726,36 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = { /* + * DAPM Events + */ + +static int da7213_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable DAI clks for master mode */ + if (da7213->master) + snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, + DA7213_DAI_CLK_EN_MASK); + return 0; + case SND_SOC_DAPM_POST_PMD: + /* Disable DAI clks if in master mode */ + if (da7213->master) + snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* * DAPM widgets */ @@ -736,7 +766,8 @@ static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = { /* Use a supply here as this controls both input & output DAIs */ SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT, - DA7213_NO_INVERT, NULL, 0), + DA7213_NO_INVERT, da7213_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), /* * Input @@ -1143,11 +1174,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* Set master/slave mode */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE; da7213->master = true; break; case SND_SOC_DAIFMT_CBS_CFS: - dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE; da7213->master = false; break; default: diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 030fd69..5de5c29 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -178,8 +178,6 @@ #define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) #define DA7213_DAI_CLK_POL_INV (0x1 << 2) #define DA7213_DAI_WCLK_POL_INV (0x1 << 3) -#define DA7213_DAI_CLK_EN_SLAVE_MODE (0x0 << 7) -#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7) #define DA7213_DAI_CLK_EN_MASK (0x1 << 7) /* DA7213_DAI_CTRL = 0x29 */ -- cgit v0.10.2 From d575b0b0f01a805508c5cf48b540f004e9b5de07 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 10 May 2016 16:11:05 +0100 Subject: ASoC: da7213: Add checking of SRM lock status before enabling DAI When the codec is DAI clk slave, and the SRM feature of the PLL is being used, the enabling of the DAI should occur only after the PLL has locked to the incoming WCLK. This update adds checking to the the DAI widget event, so it waits for SRM to lock. There is also a timeout if that lock doesn't occur within a given time. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 701bd62..680d111 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -734,6 +734,9 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -742,6 +745,26 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, DA7213_DAI_CLK_EN_MASK, DA7213_DAI_CLK_EN_MASK); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); + if (!(pll_ctrl & DA7213_PLL_SRM_EN)) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_read(codec, DA7213_PLL_STATUS); + if (pll_status & DA7219_PLL_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7213_SRM_CHECK_RETRIES) & (!srm_lock)); + + if (!srm_lock) + dev_warn(codec->dev, "SRM failed to lock\n"); + return 0; case SND_SOC_DAPM_POST_PMD: /* Disable DAI clks if in master mode */ diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 5de5c29..af75340 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -142,6 +142,9 @@ * Bit fields */ +/* DA7213_PLL_STATUS = 0x03 */ +#define DA7219_PLL_SRM_LOCK (0x1 << 1) + /* DA7213_SR = 0x22 */ #define DA7213_SR_8000 (0x1 << 0) #define DA7213_SR_11025 (0x2 << 0) @@ -502,6 +505,7 @@ #define DA7213_PLL_INDIV_10_20_MHZ_VAL 4 #define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 #define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 +#define DA7213_SRM_CHECK_RETRIES 8 enum da7213_clk_src { DA7213_CLKSRC_MCLK = 0, -- cgit v0.10.2 From 7e28fd469624fc41ec326a31abbc63a7afdd10f5 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 10 May 2016 16:11:06 +0100 Subject: ASoC: da7213: Default PC counter to free-running when DAI disabled Currently PC counter is always synchronised to DAI which means that when the DAI is disabled, features such as ALC calibration cannot be executed successfully. This patch makes sure that when the DAI is disabled, PC counter is set to free-running. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 680d111..657b7eb 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -746,6 +746,10 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, DA7213_DAI_CLK_EN_MASK, DA7213_DAI_CLK_EN_MASK); + /* PC synchronised to DAI */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, 0); + /* Slave mode, if SRM not enabled no need for status checks */ pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL); if (!(pll_ctrl & DA7213_PLL_SRM_EN)) @@ -767,6 +771,11 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w, return 0; case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + /* Disable DAI clks if in master mode */ if (da7213->master) snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE, @@ -1599,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec) /* Default to using SRM for slave mode */ da7213->srm_en = true; + /* Default PC counter to free-running */ + snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + /* Enable all Gain Ramps */ snd_soc_update_bits(codec, DA7213_AUX_L_CTRL, DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index af75340..26b87e3c 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -413,6 +413,9 @@ #define DA7213_DMIC_CLK_RATE_SHIFT 2 #define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2) +/* DA7213_PC_COUNT = 0x94 */ +#define DA7213_PC_FREERUN_MASK (0x1 << 0) + /* DA7213_DIG_CTRL = 0x99 */ #define DA7213_DAC_L_INV_SHIFT 3 #define DA7213_DAC_R_INV_SHIFT 7 -- cgit v0.10.2 From 1e62c52ddc2d23a02ac2308cc1bb6ff18f0cf3cd Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 10 May 2016 16:11:07 +0100 Subject: ASoC: da7213: Update PLL ranges to improve locking at frequency boundary This update changes the dividers used for ranges of input MCLK frequencies, to improve PLL locking for a corner case when at edge of MCLK frequency input divider range. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 657b7eb..a233fe7 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1344,26 +1344,26 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, /* Workout input divider based on MCLK rate */ if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) { /* 32KHz PLL Mode */ - indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; - indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; freq_ref = 3750000; pll_ctrl |= DA7213_PLL_32K_MODE; } else { /* 5 - 54MHz MCLK */ if (da7213->mclk_rate < 5000000) { goto pll_err; - } else if (da7213->mclk_rate <= 10000000) { - indiv_bits = DA7213_PLL_INDIV_5_10_MHZ; - indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL; - } else if (da7213->mclk_rate <= 20000000) { - indiv_bits = DA7213_PLL_INDIV_10_20_MHZ; - indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL; - } else if (da7213->mclk_rate <= 40000000) { - indiv_bits = DA7213_PLL_INDIV_20_40_MHZ; - indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL; + } else if (da7213->mclk_rate <= 9000000) { + indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ; + indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL; + } else if (da7213->mclk_rate <= 18000000) { + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7213->mclk_rate <= 36000000) { + indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL; } else if (da7213->mclk_rate <= 54000000) { - indiv_bits = DA7213_PLL_INDIV_40_54_MHZ; - indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL; + indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL; } else { goto pll_err; } diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 26b87e3c..fbb7a35 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -163,10 +163,10 @@ #define DA7213_VMID_EN (0x1 << 7) /* DA7213_PLL_CTRL = 0x27 */ -#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2) -#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2) -#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2) -#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2) +#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2) +#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2) +#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2) +#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2) #define DA7213_PLL_INDIV_MASK (0x3 << 2) #define DA7213_PLL_MCLK_SQR_EN (0x1 << 4) #define DA7213_PLL_32K_MODE (0x1 << 5) @@ -499,16 +499,16 @@ #define DA7213_ALC_AVG_ITERATIONS 5 /* PLL related */ -#define DA7213_SYSCLK_MCLK 0 -#define DA7213_SYSCLK_PLL 1 -#define DA7213_PLL_FREQ_OUT_90316800 90316800 -#define DA7213_PLL_FREQ_OUT_98304000 98304000 -#define DA7213_PLL_FREQ_OUT_94310400 94310400 -#define DA7213_PLL_INDIV_5_10_MHZ_VAL 2 -#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4 -#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8 -#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16 -#define DA7213_SRM_CHECK_RETRIES 8 +#define DA7213_SYSCLK_MCLK 0 +#define DA7213_SYSCLK_PLL 1 +#define DA7213_PLL_FREQ_OUT_90316800 90316800 +#define DA7213_PLL_FREQ_OUT_98304000 98304000 +#define DA7213_PLL_FREQ_OUT_94310400 94310400 +#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2 +#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16 +#define DA7213_SRM_CHECK_RETRIES 8 enum da7213_clk_src { DA7213_CLKSRC_MCLK = 0, -- cgit v0.10.2 From abc189eadf6c12e60f95030e9c84083175526eaf Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 10 May 2016 16:11:08 +0100 Subject: ASoC: da7213: Allow PLL disable/bypass when using 32KHz sysclk Current checking for PLL 32KHz mode fails in driver code when bypassing the PLL. This is due to an incorrect check of PLL source type when 32KHz clock is provided. Removal of this check resolves the issue. Signed-off-by: Adam Thomson Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index a233fe7..e5527bc 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1342,7 +1342,7 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, pll_ctrl = 0; /* Workout input divider based on MCLK rate */ - if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) { + if (da7213->mclk_rate == 32768) { /* 32KHz PLL Mode */ indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; -- cgit v0.10.2 From 396cbebeeb9734aee8efe39431d3b96655bf1e94 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Tue, 10 May 2016 09:08:57 +0300 Subject: ASoC: Intel: Fix printk formatting Format number after 0x in hex. Cc: Jie Yang Cc: Liam Girdwood Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Signed-off-by: Joonas Lahtinen Signed-off-by: Mark Brown diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 1aa819c..994256b 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -445,7 +445,7 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream, pages = snd_sgbuf_aligned_pages(size); - dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n", + dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n", dma_area, size, pages); for (i = 0; i < pages; i++) { -- cgit v0.10.2 From bfb7802a06ac1855096a3f248822e8f943e6574d Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 11 May 2016 11:07:02 +1000 Subject: ASoC: Intel: fix up for DAI link's be_id change Signed-off-by: Stephen Rothwell Acked-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 1b845ff..f478751 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -254,7 +254,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { { /* SSP5 - Codec */ .name = "SSP5-Codec", - .be_id = 0, + .id = 0, .cpu_dai_name = "SSP5 Pin", .platform_name = "0000:00:0e.0", .no_pcm = 1, @@ -271,7 +271,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { }, { .name = "dmic01", - .be_id = 1, + .id = 1, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", @@ -282,7 +282,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { }, { .name = "iDisp1", - .be_id = 3, + .id = 3, .cpu_dai_name = "iDisp1 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi1", @@ -293,7 +293,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { }, { .name = "iDisp2", - .be_id = 4, + .id = 4, .cpu_dai_name = "iDisp2 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi2", @@ -304,7 +304,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { }, { .name = "iDisp3", - .be_id = 5, + .id = 5, .cpu_dai_name = "iDisp3 Pin", .codec_name = "ehdaudio0D2", .codec_dai_name = "intel-hdmi-hifi3", -- cgit v0.10.2 From 32902177f7f6ae70e1d5e71d935aa1bfcae7f01c Mon Sep 17 00:00:00 2001 From: John Keeping Date: Thu, 12 May 2016 13:55:53 +0100 Subject: ASoC: dapm: deprecate MICBIAS widget type Commit 086d7f804e26 ("ASoC: Convert WM8962 MICBIAS to a supply widget", 2011-09-23) says: A supply widget is generally clearer than a MICBIAS widget and a mic bias is just a type of supply so use a supply widget for the MICBIAS. This also avoids confusion with the routing when connected to multiple inputs. but this has never been documented as a policy. Add some comments to make it clear. Signed-off-by: John Keeping Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 9706946..3101d53 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -100,6 +100,7 @@ struct device; { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +/* DEPRECATED: use SND_SOC_DAPM_SUPPLY */ #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ { .id = snd_soc_dapm_micbias, .name = wname, \ SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ @@ -473,7 +474,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_out_drv, /* output driver */ snd_soc_dapm_adc, /* analog to digital converter */ snd_soc_dapm_dac, /* digital to analog converter */ - snd_soc_dapm_micbias, /* microphone bias (power) */ + snd_soc_dapm_micbias, /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */ snd_soc_dapm_mic, /* microphone */ snd_soc_dapm_hp, /* headphones */ snd_soc_dapm_spk, /* speaker */ -- cgit v0.10.2 From bb7cb54b388d8d0fbb3af27f14b121ee9c92e867 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 May 2016 09:38:49 +0530 Subject: ASoC: rt298: fix null deref on acpi driver data ACPI driver data can be NULL so we need to check that before dereference the driver data. Signed-off-by: Senthilnathan Veppur Signed-off-by: Vinod Koul Acked-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index f0e6c06..52aacb1 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1184,7 +1184,7 @@ static int rt298_i2c_probe(struct i2c_client *i2c, /* enable jack combo mode on supported devices */ acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); - if (acpiid) { + if (acpiid && acpiid->driver_data) { rt298->pdata = *(struct rt298_platform_data *) acpiid->driver_data; } -- cgit v0.10.2 From b9c17f13ba484d8492278c67cd95b7207def776f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 May 2016 09:38:50 +0530 Subject: ASoC: rt298: Add DMI match for Broxton-P reference platform Broxton-P reference platform also uses combo jack for audio connector so we need to set codec pdata to use this based on DMI match for this board. Signed-off-by: Ramesh Babu Signed-off-by: Senthilnathan Veppur Signed-off-by: Vinod Koul Acked-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 52aacb1..a1aaffc 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); +static const struct dmi_system_id force_combo_jack_table[] = { + { + .ident = "Intel Broxton P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P") + } + }, + { } +}; + static int rt298_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1189,6 +1201,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c, acpiid->driver_data; } + if (dmi_check_system(force_combo_jack_table)) { + rt298->pdata.cbj_en = true; + rt298->pdata.gpio2_en = false; + } + /* VREF Charging */ regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); -- cgit v0.10.2 From 4446085d21e75dd6c0c45577f12db0bd7c7bf35f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 May 2016 08:58:53 +0530 Subject: ALSA: hdac: add link pm and ref counting The HDA links can be switched off when not is use, similarly command DMA can be stopped as well. This calls for a reference counting mechanism on the link by it's users to manage the link power. The DMA can be turned off when all links are off For this we add two APIs snd_hdac_ext_bus_link_get snd_hdac_ext_bus_link_put They help users to turn up/down link and manage the DMA as well Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Acked-by: Takashi Iwai Signed-off-by: Mark Brown diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 07fa592..b9593b2 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -14,6 +14,8 @@ * @gtscap: gts capabilities pointer * @drsmcap: dma resume capabilities pointer * @hlink_list: link list of HDA links + * @lock: lock for link mgmt + * @cmd_dma_state: state of cmd DMAs: CORB and RIRB */ struct hdac_ext_bus { struct hdac_bus bus; @@ -27,6 +29,9 @@ struct hdac_ext_bus { void __iomem *drsmcap; struct list_head hlink_list; + + struct mutex lock; + bool cmd_dma_state; }; int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev, @@ -142,6 +147,9 @@ struct hdac_ext_link { void __iomem *ml_addr; /* link output stream reg pointer */ u32 lcaps; /* link capablities */ u16 lsdiid; /* link sdi identifier */ + + int ref_count; + struct list_head list; }; @@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, int stream); +int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, + struct hdac_ext_link *link); +int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, + struct hdac_ext_link *link); + /* update register macro */ #define snd_hdac_updatel(addr, reg, mask, val) \ writel(((readl(addr + reg) & ~(mask)) | (val)), \ diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 2433f7c..3b7ae24 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, INIT_LIST_HEAD(&ebus->hlink_list); ebus->idx = idx++; + mutex_init(&ebus->lock); + ebus->cmd_dma_state = true; + return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c index 548cc1e..860f8ca 100644 --- a/sound/hda/ext/hdac_ext_controller.c +++ b/sound/hda/ext/hdac_ext_controller.c @@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus) hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP); hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID); + /* since link in On, update the ref */ + hlink->ref_count = 1; + list_add_tail(&hlink->list, &ebus->hlink_list); } @@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all); + +int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus, + struct hdac_ext_link *link) +{ + int ret = 0; + + mutex_lock(&ebus->lock); + + /* + * if we move from 0 to 1, count will be 1 so power up this link + * as well, also check the dma status and trigger that + */ + if (++link->ref_count == 1) { + if (!ebus->cmd_dma_state) { + snd_hdac_bus_init_cmd_io(&ebus->bus); + ebus->cmd_dma_state = true; + } + + ret = snd_hdac_ext_bus_link_power_up(link); + } + + mutex_unlock(&ebus->lock); + return ret; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get); + +int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus, + struct hdac_ext_link *link) +{ + int ret = 0; + struct hdac_ext_link *hlink; + bool link_up = false; + + mutex_lock(&ebus->lock); + + /* + * if we move from 1 to 0, count will be 0 + * so power down this link as well + */ + if (--link->ref_count == 0) { + ret = snd_hdac_ext_bus_link_power_down(link); + + /* + * now check if all links are off, if so turn off + * cmd dma as well + */ + list_for_each_entry(hlink, &ebus->hlink_list, list) { + if (hlink->ref_count) { + link_up = true; + break; + } + } + + if (!link_up) { + snd_hdac_bus_stop_cmd_io(&ebus->bus); + ebus->cmd_dma_state = false; + } + } + + mutex_unlock(&ebus->lock); + return ret; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put); -- cgit v0.10.2 From cce6c149eba3aabf678ffea91ac1e4103b9c185e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 May 2016 08:58:54 +0530 Subject: ASoC: Intel: Skylake: add link management Use shiny new link APIs to manage the links. Also remove old link configuration logic from driver. We need to keep link and cmd dma to off during active suspend to allow system to enter low power state and turn it on if the link and cmd dma was on before active suspend in active resume. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4494db6..1548023 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -533,7 +533,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - snd_hdac_ext_bus_link_power_up(link); snd_hdac_ext_link_stream_reset(link_dev); snd_hdac_ext_link_stream_setup(link_dev, format_val); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 83e985c..06d8c26 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -229,7 +229,12 @@ static int skl_suspend(struct device *dev) * running, we need to save the state for these and continue */ if (skl->supend_active) { + /* turn off the links and stop the CORB/RIRB DMA if it is On */ snd_hdac_ext_bus_link_power_down_all(ebus); + + if (ebus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(&ebus->bus); + enable_irq_wake(bus->irq); pci_save_state(pci); pci_disable_device(pci); @@ -255,6 +260,7 @@ static int skl_resume(struct device *dev) struct hdac_ext_bus *ebus = pci_get_drvdata(pci); struct skl *skl = ebus_to_skl(ebus); struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_link *hlink = NULL; int ret; /* Turned OFF in HDMI codec driver after codec reconfiguration */ @@ -276,8 +282,29 @@ static int skl_resume(struct device *dev) ret = pci_enable_device(pci); snd_hdac_ext_bus_link_power_up_all(ebus); disable_irq_wake(bus->irq); + /* + * turn On the links which are On before active suspend + * and start the CORB/RIRB DMA if On before + * active suspend. + */ + list_for_each_entry(hlink, &ebus->hlink_list, list) { + if (hlink->ref_count) + snd_hdac_ext_bus_link_power_up(hlink); + } + + if (ebus->cmd_dma_state) + snd_hdac_bus_init_cmd_io(&ebus->bus); } else { ret = _skl_resume(ebus); + + /* turn off the links which are off before suspend */ + list_for_each_entry(hlink, &ebus->hlink_list, list) { + if (!hlink->ref_count) + snd_hdac_ext_bus_link_power_down(hlink); + } + + if (!ebus->cmd_dma_state) + snd_hdac_bus_stop_cmd_io(&ebus->bus); } return ret; @@ -613,6 +640,7 @@ static int skl_probe(struct pci_dev *pci, struct skl *skl; struct hdac_ext_bus *ebus = NULL; struct hdac_bus *bus = NULL; + struct hdac_ext_link *hlink = NULL; int err; /* we use ext core ops, so provide NULL for ops here */ @@ -679,6 +707,12 @@ static int skl_probe(struct pci_dev *pci, } } + /* + * we are done probling so decrement link counts + */ + list_for_each_entry(hlink, &ebus->hlink_list, list) + snd_hdac_ext_bus_link_put(ebus, hlink); + /*configure PM */ pm_runtime_put_noidle(bus->dev); pm_runtime_allow(bus->dev); -- cgit v0.10.2 From b2047e996cd88d36eb0f4e84fe6aedab831a4b31 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 12 May 2016 08:58:55 +0530 Subject: ASoC: hdac_hdmi: add link management Manage the hda idisp link using shiny new link APIs. We need to keep link On while we probe and also hold the reference in runtime resume and drop in suspend Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index aaa038f..13002f3 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1378,10 +1378,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec) struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(&codec->component); struct hdac_hdmi_pin *pin; + struct hdac_ext_link *hlink = NULL; int ret; edev->scodec = codec; + /* + * hold the ref while we probe, also no need to drop the ref on + * exit, we call pm_runtime_suspend() so that will do for us + */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + ret = create_fill_widget_route_map(dapm); if (ret < 0) return ret; @@ -1480,9 +1488,14 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) struct hdac_device *codec = &edev->hdac; struct hdac_hdmi_priv *hdmi_priv; struct snd_soc_dai_driver *hdmi_dais = NULL; + struct hdac_ext_link *hlink = NULL; int num_dais = 0; int ret = 0; + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev)); + snd_hdac_ext_bus_link_get(edev->ebus, hlink); + hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL); if (hdmi_priv == NULL) return -ENOMEM; @@ -1516,8 +1529,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) } /* ASoC specific initialization */ - return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, - hdmi_dais, num_dais); + ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec, + hdmi_dais, num_dais); + + snd_hdac_ext_bus_link_put(edev->ebus, hlink); + + return ret; } static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev) @@ -1556,6 +1573,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + unsigned long timeout; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1579,6 +1599,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) return err; } + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_put(ebus, hlink); + return 0; } @@ -1587,6 +1610,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev) struct hdac_ext_device *edev = to_hda_ext_device(dev); struct hdac_device *hdac = &edev->hdac; struct hdac_bus *bus = hdac->bus; + struct hdac_ext_bus *ebus = hbus_to_ebus(bus); + struct hdac_ext_link *hlink = NULL; int err; dev_dbg(dev, "Enter: %s\n", __func__); @@ -1595,6 +1620,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev) if (!bus) return 0; + hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev)); + snd_hdac_ext_bus_link_get(ebus, hlink); + err = snd_hdac_display_power(bus, true); if (err < 0) { dev_err(bus->dev, "Cannot turn on display power on i915\n"); -- cgit v0.10.2 From 97d3ddd71fbf663a5da52897757333341a8b254f Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 13 May 2016 09:14:12 +0000 Subject: ASoC: pcm5102a: Add support for PCM5102A codec Some definitions to support the PCM5102A codec by Texas Instruments. Signed-off-by: Florian Meier Changes to original patch by Florian Meier: * rebased (Makefile and Kconfig * fixed checkpath errors (spaces, newlines) * added dt-binding documentation Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/pcm5102a.txt b/Documentation/devicetree/bindings/sound/pcm5102a.txt new file mode 100644 index 0000000..c63ab0b6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/pcm5102a.txt @@ -0,0 +1,13 @@ +PCM5102a audio CODECs + +These devices does not use I2C or SPI. + +Required properties: + + - compatible : set as "ti,pcm5102a" + +Examples: + + pcm5102a: pcm5102a { + compatible = "ti,pcm5102a"; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 649e92a..f736953 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -94,6 +94,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM3008 select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER + select SND_SOC_PCM5102A select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_RT286 if I2C @@ -575,6 +576,9 @@ config SND_SOC_PCM3168A_SPI select SND_SOC_PCM3168A select REGMAP_SPI +config SND_SOC_PCM5102A + tristate + config SND_SOC_PCM512x tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 185a712..4532a74 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -89,6 +89,7 @@ snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o +snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o @@ -298,6 +299,7 @@ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o +obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c new file mode 100644 index 0000000..ed51567 --- /dev/null +++ b/sound/soc/codecs/pcm5102a.c @@ -0,0 +1,69 @@ +/* + * Driver for the PCM5102A codec + * + * Author: Florian Meier + * Copyright 2013 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 +#include +#include + +#include + +static struct snd_soc_dai_driver pcm5102a_dai = { + .name = "pcm5102a-hifi", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; + +static int pcm5102a_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, + &pcm5102a_dai, 1); +} + +static int pcm5102a_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id pcm5102a_of_match[] = { + { .compatible = "ti,pcm5102a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm5102a_of_match); + +static struct platform_driver pcm5102a_codec_driver = { + .probe = pcm5102a_probe, + .remove = pcm5102a_remove, + .driver = { + .name = "pcm5102a-codec", + .owner = THIS_MODULE, + .of_match_table = pcm5102a_of_match, + }, +}; + +module_platform_driver(pcm5102a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); +MODULE_AUTHOR("Florian Meier "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 48a260eec301fd7a112d1737ca2755d91558a349 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:55 +0200 Subject: ASoC: fsl_ssi: Real hardware channels max number is 32 The max number of slots in TDM mode is 32: - Frame Rate Divider Control is a 5bit value - Time slot mask registers control 32 slots. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ed8de10..8d5f3c19 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1158,14 +1158,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { .playback = { .stream_name = "CPU-Playback", .channels_min = 1, - .channels_max = 2, + .channels_max = 32, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 32, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, -- cgit v0.10.2 From e09745f2e6a1f692fc63db01850aacf025475aad Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:56 +0200 Subject: ASoC: fsl_ssi: The IPG/5 limitation concerns the bitclk, not the sysclk. im6sl reference manual 47.7.4: " Bit clock - Used to serially clock the data bits in and out of the SSI port. This clock is either generated internally (from SSI's sys clock) or taken from external clock source (through the Tx/Rx clock ports). [...] Care should be taken to ensure that the bit clock frequency (either internally generated by dividing the SSI's sys clock or sourced from external device through Tx/Rx clock ports) is never greater than 1/5 of the ipg_clk (from CCM) frequency. " Since, in master mode, the sysclk is a multiple of bitclk, we can easily reach a high sysclk value, whereas keeping a reasonable bitclk. ex: 8ch x 16bit x 48kHz = 6144000, requires a 24576000 sysclk (PM=1) yet ipg_clk/5 = 66Mhz/5 = 13.2 Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8d5f3c19..86229c8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -670,6 +670,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, if (IS_ERR(ssi_private->baudclk)) return -EINVAL; + /* + * Hardware limitation: The bclk rate must be + * never greater than 1/5 IPG clock rate + */ + if (freq * 5 > clk_get_rate(ssi_private->clk)) { + dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n"); + return -EINVAL; + } + baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); /* It should be already enough to divide clock by setting pm alone */ @@ -686,13 +695,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, else clkrate = clk_round_rate(ssi_private->baudclk, tmprate); - /* - * Hardware limitation: The bclk rate must be - * never greater than 1/5 IPG clock rate - */ - if (clkrate * 5 > clk_get_rate(ssi_private->clk)) - continue; - clkrate /= factor; afreq = clkrate / (i + 1); -- cgit v0.10.2 From 0096b693962d3abde4f41b13cf03c765f33e9d8d Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:57 +0200 Subject: ASoC: fsl_ssi: Save a dev reference for dev_err() purpose. Most of functions only receive the ssi_private reference and don't have a knowledge of 'dev' pointer, even for debug purpose. Signed-off-by: Arnaud Mouiche Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 86229c8..149df3c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -261,6 +261,7 @@ struct fsl_ssi_private { struct fsl_ssi_dbg dbg_stats; const struct fsl_ssi_soc_data *soc; + struct device *dev; }; /* @@ -1404,6 +1405,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->soc = of_id->data; + ssi_private->dev = &pdev->dev; sprop = of_get_property(np, "fsl,mode", NULL); if (sprop) { -- cgit v0.10.2 From d9f2a202877c15818d98268f47d6b4bcfcb84437 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:58 +0200 Subject: ASoC: fsl_ssi: Fix samples being dropped at Playback startup If the capture is already running while playback is started, it is highly probable (>80% in a 8 channels scenario) that samples are lost between the DMA and TX fifo. The reason is that SIER.TDMAE is set before STCR.TFEN0, leaving a time window where the FIFO doesn't receive the samples written by the DMA. This particular case happened only if capture is already enabled as SCR.SSIEN is already set at the playback startup instant. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 149df3c..47ebb83 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -475,9 +475,9 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, * (online configuration) */ if (enable) { - regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); + regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); } else { u32 sier; u32 srcr; -- cgit v0.10.2 From 61fcf10a0ee44763e0347b297a377137f8950772 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:59 +0200 Subject: ASoC: fsl_ssi: Fix channel slipping in Playback at startup Previously, SCR.SSIEN and SCR.TE were enabled at once if no capture stream was also running. This may not give a chance for the DMA to write the first sample in TX FIFO before the streaming starts on the PCM bus, inserting void samples first. Those void samples are then responsible for slipping the channels. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 47ebb83..8944af5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -507,8 +507,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, config_done: /* Enabling of subunits is done after configuration */ - if (enable) + if (enable) { + if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) { + /* + * Be sure the Tx FIFO is filled when TE is set. + * Otherwise, there are some chances to start the + * playback with some void samples inserted first, + * generating a channel slip. + * + * First, SSIEN must be set, to let the FIFO be filled. + * + * Notes: + * - Limit this fix to the DMA case until FIQ cases can + * be tested. + * - Limit the length of the busy loop to not lock the + * system too long, even if 1-2 loops are sufficient + * in general. + */ + int i; + int max_loop = 100; + regmap_update_bits(regs, CCSR_SSI_SCR, + CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN); + for (i = 0; i < max_loop; i++) { + u32 sfcsr; + regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr); + if (CCSR_SSI_SFCSR_TFCNT0(sfcsr)) + break; + } + if (i == max_loop) { + dev_err(ssi_private->dev, + "Timeout waiting TX FIFO filling\n"); + } + } regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); + } } -- cgit v0.10.2 From 027db2e122db81b055a2b569d72f2f1d29c4d007 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:14:00 +0200 Subject: ASoC: fsl_ssi: Fix channel slipping on capture (or playback) restart in full duplex. Happened when the Playback (or Capture) is running continuously and Capture (or Playback) is restarted (xrun, manual stop/start...) Since the RX (or TX) FIFO are only reset when the whole SSI is disabled, pending samples from previous capture (or playback) session may still be present. They must be erased to not introduce channel slipping. FIFO Clear register fields are documented in IMX51, IMX35 reference manual. They are not documented in IMX50 or IMX6 RM, despite they are working as expected on IMX6SL and IMX6solo. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8944af5..d2dd47d 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -401,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, } /* + * Clear RX or TX FIFO to remove samples from the previous + * stream session which may be still present in the FIFO and + * may introduce bad samples and/or channel slipping. + * + * Note: The SOR is not documented in recent IMX datasheet, but + * is described in IMX51 reference manual at section 56.3.3.15. + */ +static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, + bool is_rx) +{ + if (is_rx) { + regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, + CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR); + } else { + regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, + CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR); + } +} + +/* * Calculate the bits that have to be disabled for the current stream that is * getting disabled. This keeps the bits enabled that are necessary for the * second stream to work if 'stream_active' is true. @@ -475,6 +495,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, * (online configuration) */ if (enable) { + fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE); + regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); -- cgit v0.10.2