diff options
Diffstat (limited to 'sound/soc')
181 files changed, 6409 insertions, 2346 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index c0abad2067e1..d22758165496 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -36,6 +36,9 @@ config SND_SOC_COMPRESS config SND_SOC_TOPOLOGY bool +config SND_SOC_ACPI + tristate + # All the supported SoCs source "sound/soc/adi/Kconfig" source "sound/soc/amd/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 39c27a58158d..5327f4d6c668 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o @@ -14,6 +15,12 @@ ifneq ($(CONFIG_SND_SOC_AC97_BUS),) snd-soc-core-objs += soc-ac97.o endif +ifneq ($(CONFIG_SND_SOC_ACPI),) +snd-soc-acpi-objs := soc-acpi.o +endif + +obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o + obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 78187eb24f56..d5838402f667 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP tristate "AMD Audio Coprocessor support" help This option enables ACP DMA support on AMD platform. + +config SND_SOC_AMD_CZ_RT5645_MACH + tristate "AMD CZ support for RT5645" + select SND_SOC_RT5645 + depends on SND_SOC_AMD_ACP && I2C + help + This option enables machine driver for rt5645. diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 1a66ec0366b2..f07fd2e2870a 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -1,3 +1,5 @@ -snd-soc-acp-pcm-objs := acp-pcm-dma.o +acp_audio_dma-objs := acp-pcm-dma.o +snd-soc-acp-rt5645-mach-objs := acp-rt5645.o -obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o +obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o +obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 08b1399d1da2..9f521a55d610 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -20,7 +20,7 @@ #include <linux/pm_runtime.h> #include <sound/soc.h> - +#include <drm/amd_asic_type.h> #include "acp.h" #define PLAYBACK_MIN_NUM_PERIODS 2 @@ -35,6 +35,13 @@ #define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define MIN_BUFFER MAX_BUFFER +#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192 +#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE +#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) +#define ST_MIN_BUFFER ST_MAX_BUFFER + +#define DRV_NAME "acp_audio_dma" + static const struct snd_pcm_hardware acp_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = { .periods_max = CAPTURE_MAX_NUM_PERIODS, }; -struct audio_drv_data { - struct snd_pcm_substream *play_stream; - struct snd_pcm_substream *capture_stream; - void __iomem *acp_mmio; +static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 96000, + .buffer_bytes_max = ST_MAX_BUFFER, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, +}; + +static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .buffer_bytes_max = ST_MAX_BUFFER, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, }; static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg) @@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio, * system memory <-> ACP SRAM */ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, - u32 size, int direction, - u32 pte_offset) + u32 size, int direction, + u32 pte_offset, u32 asic_type) { u16 i; u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; @@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, dmadscr[i].xfer_val = 0; if (direction == SNDRV_PCM_STREAM_PLAYBACK) { dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i; - dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS + - (size / 2) - (i * (size/2)); + dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS + + (i * (size/2)); dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + (pte_offset * SZ_4K) + (i * (size/2)); - dmadscr[i].xfer_val |= - (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | - (size / 2); + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].xfer_val |= + (ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) | + (size / 2); + break; + default: + dmadscr[i].xfer_val |= + (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | + (size / 2); + } } else { dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i; - dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS + - (i * (size/2)); - dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS - + (pte_offset * SZ_4K) + - (i * (size/2)); - dmadscr[i].xfer_val |= - BIT(22) | - (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | - (size / 2); + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].src = ACP_SHARED_RAM_BANK_3_ADDRESS + + (i * (size/2)); + dmadscr[i].dest = + ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + + (pte_offset * SZ_4K) + (i * (size/2)); + dmadscr[i].xfer_val |= + BIT(22) | + (ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) | + (size / 2); + break; + default: + dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS + + (i * (size/2)); + dmadscr[i].dest = + ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + + (pte_offset * SZ_4K) + (i * (size/2)); + dmadscr[i].xfer_val |= + BIT(22) | + (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | + (size / 2); + } } config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx, &dmadscr[i]); @@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, * ACP SRAM <-> I2S */ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, - u32 size, int direction) + u32 size, int direction, + u32 asic_type) { u16 i; @@ -213,8 +275,17 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i; /* dmadscr[i].src is unused by hardware. */ dmadscr[i].src = 0; - dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS + + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].dest = + ACP_SHARED_RAM_BANK_3_ADDRESS + (i * (size / 2)); + break; + default: + dmadscr[i].dest = + ACP_SHARED_RAM_BANK_5_ADDRESS + + (i * (size / 2)); + } dmadscr[i].xfer_val |= BIT(22) | (FROM_ACP_I2S_1 << 16) | (size / 2); } @@ -270,7 +341,8 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, } static void config_acp_dma(void __iomem *acp_mmio, - struct audio_substream_data *audio_config) + struct audio_substream_data *audio_config, + u32 asic_type) { u32 pte_offset; @@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio, /* Configure System memory <-> ACP SRAM DMA descriptors */ set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction, pte_offset); + audio_config->direction, pte_offset, asic_type); /* Configure ACP SRAM <-> I2S DMA descriptors */ set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction); + audio_config->direction, asic_type); } /* Start a given DMA channel transfer */ @@ -425,7 +497,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank, } /* Initialize and bring ACP hardware to default state. */ -static int acp_init(void __iomem *acp_mmio) +static int acp_init(void __iomem *acp_mmio, u32 asic_type) { u16 bank; u32 val, count, sram_pte_offset; @@ -499,10 +571,21 @@ static int acp_init(void __iomem *acp_mmio) /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on. * Now, turn off all of them. This can't be done in 'poweron' of * ACP pm domain, as this requires ACP to be initialized. + * For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. */ - for (bank = 1; bank < 48; bank++) - acp_set_sram_bank_state(acp_mmio, bank, false); + if (asic_type != CHIP_STONEY) { + for (bank = 1; bank < 48; bank++) + acp_set_sram_bank_state(acp_mmio, bank, false); + } + /* Stoney supports 16bit resolution */ + if (asic_type == CHIP_STONEY) { + val = acp_reg_read(acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); + val |= 0x03; + acp_reg_write(val, acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); + } return 0; } @@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) valid_irq = true; if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) == PLAYBACK_START_DMA_DESCR_CH13) - dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; - else dscr_idx = PLAYBACK_END_DMA_DESCR_CH12; + else + dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx, 1, 0); acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); @@ -626,10 +709,23 @@ static int acp_dma_open(struct snd_pcm_substream *substream) if (adata == NULL) return -ENOMEM; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - runtime->hw = acp_pcm_hardware_playback; - else - runtime->hw = acp_pcm_hardware_capture; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (intr_data->asic_type) { + case CHIP_STONEY: + runtime->hw = acp_st_pcm_hardware_playback; + break; + default: + runtime->hw = acp_pcm_hardware_playback; + } + } else { + switch (intr_data->asic_type) { + case CHIP_STONEY: + runtime->hw = acp_st_pcm_hardware_capture; + break; + default: + runtime->hw = acp_pcm_hardware_capture; + } + } ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { intr_data->play_stream = substream; - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(intr_data->acp_mmio, bank, - true); + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + */ + if (intr_data->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(intr_data->acp_mmio, + bank, true); + } } else { intr_data->capture_stream = substream; - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(intr_data->acp_mmio, bank, - true); + if (intr_data->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(intr_data->acp_mmio, + bank, true); + } } return 0; @@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, struct page *pg; struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev); runtime = substream->runtime; rtd = runtime->private_data; @@ -700,7 +806,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; rtd->direction = substream->stream; - config_acp_dma(rtd->acp_mmio, rtd); + config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type); status = 0; } else { status = -ENOMEM; @@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } +static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream) +{ + union acp_dma_count playback_dma_count; + union acp_dma_count capture_dma_count; + u64 bytescount = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + playback_dma_count.bcount.high = acp_reg_read(acp_mmio, + mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH); + playback_dma_count.bcount.low = acp_reg_read(acp_mmio, + mmACP_I2S_TRANSMIT_BYTE_CNT_LOW); + bytescount = playback_dma_count.bytescount; + } else { + capture_dma_count.bcount.high = acp_reg_read(acp_mmio, + mmACP_I2S_RECEIVED_BYTE_CNT_HIGH); + capture_dma_count.bcount.low = acp_reg_read(acp_mmio, + mmACP_I2S_RECEIVED_BYTE_CNT_LOW); + bytescount = capture_dma_count.bytescount; + } + return bytescount; +} + static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) { - u16 dscr; - u32 mul, dma_config, period_bytes; + u32 buffersize; u32 pos = 0; + u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; - period_bytes = frames_to_bytes(runtime, runtime->period_size); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13); + buffersize = frames_to_bytes(runtime, runtime->buffer_size); + bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream); - if (dscr == PLAYBACK_START_DMA_DESCR_CH13) - mul = 0; - else - mul = 1; - pos = (mul * period_bytes); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (bytescount > rtd->renderbytescount) + bytescount = bytescount - rtd->renderbytescount; } else { - dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14); - if (dma_config != 0) { - dscr = acp_reg_read(rtd->acp_mmio, - mmACP_DMA_CUR_DSCR_14); - if (dscr == CAPTURE_START_DMA_DESCR_CH14) - mul = 1; - else - mul = 2; - pos = (mul * period_bytes); - } - - if (pos >= (2 * period_bytes)) - pos = 0; - + if (bytescount > rtd->capturebytescount) + bytescount = bytescount - rtd->capturebytescount; } + pos = do_div(bytescount, buffersize); return bytes_to_frames(runtime, pos); } @@ -768,23 +882,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM, PLAYBACK_START_DMA_DESCR_CH13, NUM_DSCRS_PER_CHANNEL, 0); - /* Fill ACP SRAM (2 periods) with zeros from System RAM - * which is zero-ed in hw_params - */ - acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); - - /* ACP SRAM (2 periods of buffer size) is intially filled with - * zeros. Before rendering starts, 2nd half of SRAM will be - * filled with valid audio data DMA'ed from first half of system - * RAM and 1st half of SRAM will be filled with Zeros. This is - * the initial scenario when redering starts from SRAM. Later - * on, 2nd half of system memory will be DMA'ed to 1st half of - * SRAM, 1st half of system memory will be DMA'ed to 2nd half of - * SRAM in ping-pong way till rendering stops. - */ - config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, - PLAYBACK_START_DMA_DESCR_CH12, - 1, 0); } else { config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM, CAPTURE_START_DMA_DESCR_CH14, @@ -799,7 +896,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) { int ret; - u32 loops = 1000; + u32 loops = 4000; + u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -811,7 +909,11 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + bytescount = acp_get_byte_count(rtd->acp_mmio, + substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (rtd->renderbytescount == 0) + rtd->renderbytescount = bytescount; acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) & @@ -828,6 +930,8 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ACP_TO_I2S_DMA_CH_NUM, true); } else { + if (rtd->capturebytescount == 0) + rtd->capturebytescount = bytescount; acp_dma_start(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM, true); } @@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) * channels will stopped automatically after its transfer * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = acp_dma_stop(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM); - else + rtd->renderbytescount = 0; + } else { ret = acp_dma_stop(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM); + rtd->capturebytescount = 0; + } break; default: ret = -EINVAL; @@ -857,10 +964,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) { - return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + int ret; + struct audio_drv_data *adata = dev_get_drvdata(rtd->platform->dev); + + switch (adata->asic_type) { + case CHIP_STONEY: + ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + NULL, ST_MIN_BUFFER, + ST_MAX_BUFFER); + break; + default: + ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, NULL, MIN_BUFFER, MAX_BUFFER); + break; + } + if (ret < 0) + dev_err(rtd->platform->dev, + "buffer preallocation failer error:%d\n", ret); + return ret; } static int acp_dma_close(struct snd_pcm_substream *substream) @@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adata->play_stream = NULL; - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); - } else { + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + * added condition checks for Carrizo platform only + */ + if (adata->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, + false); + } + } else { adata->capture_stream = NULL; - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); + if (adata->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, + false); + } } /* Disable ACP irq, when the current stream is being closed and @@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev) int status; struct audio_drv_data *audio_drv_data; struct resource *res; + const u32 *pdata = pdev->dev.platform_data; audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data), GFP_KERNEL); @@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev) audio_drv_data->play_stream = NULL; audio_drv_data->capture_stream = NULL; + audio_drv_data->asic_type = *pdata; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, audio_drv_data); /* Initialize the ACP */ - acp_init(audio_drv_data->acp_mmio); + acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type); status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform); if (status != 0) { @@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev) u16 bank; struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio); + acp_init(adata->acp_mmio, adata->asic_type); if (adata->play_stream && adata->play_stream->runtime) { - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + */ + if (adata->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, true); + } config_acp_dma(adata->acp_mmio, - adata->play_stream->runtime->private_data); + adata->play_stream->runtime->private_data, + adata->asic_type); } if (adata->capture_stream && adata->capture_stream->runtime) { - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, + if (adata->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, true); + } config_acp_dma(adata->acp_mmio, - adata->capture_stream->runtime->private_data); + adata->capture_stream->runtime->private_data, + adata->asic_type); } acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; @@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *dev) { struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio); + acp_init(adata->acp_mmio, adata->asic_type); acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; } @@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = { .probe = acp_audio_probe, .remove = acp_audio_remove, .driver = { - .name = "acp_audio_dma", + .name = DRV_NAME, .pm = &acp_pm_ops, }, }; module_platform_driver(acp_dma_driver); +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); MODULE_DESCRIPTION("AMD ACP PCM Driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:acp-dma-audio"); +MODULE_ALIAS("platform:"DRV_NAME); diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c new file mode 100644 index 000000000000..941aed6bb364 --- /dev/null +++ b/sound/soc/amd/acp-rt5645.c @@ -0,0 +1,199 @@ +/* + * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec + * + * Copyright 2017 Advanced Micro Devices, Inc. + * + * This file is modified from rt288 machine driver + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/acpi.h> + +#include "../codecs/rt5645.h" + +#define CZ_PLAT_CLK 24000000 + +static struct snd_soc_jack cz_jack; + +static int cz_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, + CZ_PLAT_CLK, params_rate(params) * 512); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, + params_rate(params) * 512, SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return ret; +} + +static int cz_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_card *card; + struct snd_soc_codec *codec; + + codec = rtd->codec; + card = rtd->card; + + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "HP jack creation failed %d\n", ret); + return ret; + } + + rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack); + + return 0; +} + +static struct snd_soc_ops cz_aif1_ops = { + .hw_params = cz_aif1_hw_params, +}; + +static struct snd_soc_dai_link cz_dai_rt5650[] = { + { + .name = "amd-rt5645-play", + .stream_name = "RT5645_AIF1", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.1.auto", + .codec_dai_name = "rt5645-aif1", + .codec_name = "i2c-10EC5650:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .init = cz_init, + .ops = &cz_aif1_ops, + }, + { + .name = "amd-rt5645-cap", + .stream_name = "RT5645_AIF1", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.2.auto", + .codec_dai_name = "rt5645-aif1", + .codec_name = "i2c-10EC5650:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ops = &cz_aif1_ops, + }, +}; + +static const struct snd_soc_dapm_widget cz_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static const struct snd_soc_dapm_route cz_audio_route[] = { + {"Headphones", NULL, "HPOL"}, + {"Headphones", NULL, "HPOR"}, + {"RECMIXL", NULL, "Headset Mic"}, + {"RECMIXR", NULL, "Headset Mic"}, + {"Speakers", NULL, "SPOL"}, + {"Speakers", NULL, "SPOR"}, + {"DMIC L2", NULL, "Int Mic"}, + {"DMIC R2", NULL, "Int Mic"}, +}; + +static const struct snd_kcontrol_new cz_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static struct snd_soc_card cz_card = { + .name = "acprt5650", + .owner = THIS_MODULE, + .dai_link = cz_dai_rt5650, + .num_links = ARRAY_SIZE(cz_dai_rt5650), + .dapm_widgets = cz_widgets, + .num_dapm_widgets = ARRAY_SIZE(cz_widgets), + .dapm_routes = cz_audio_route, + .num_dapm_routes = ARRAY_SIZE(cz_audio_route), + .controls = cz_mc_controls, + .num_controls = ARRAY_SIZE(cz_mc_controls), +}; + +static int cz_probe(struct platform_device *pdev) +{ + int ret; + struct snd_soc_card *card; + + card = &cz_card; + cz_card.dev = &pdev->dev; + platform_set_drvdata(pdev, card); + ret = devm_snd_soc_register_card(&pdev->dev, &cz_card); + if (ret) { + dev_err(&pdev->dev, + "devm_snd_soc_register_card(%s) failed: %d\n", + cz_card.name, ret); + return ret; + } + return 0; +} + +static const struct acpi_device_id cz_audio_acpi_match[] = { + { "AMDI1002", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match); + +static struct platform_driver cz_pcm_driver = { + .driver = { + .name = "cz-rt5645", + .acpi_match_table = ACPI_PTR(cz_audio_acpi_match), + .pm = &snd_soc_pm_ops, + }, + .probe = cz_probe, +}; + +module_platform_driver(cz_pcm_driver); + +MODULE_AUTHOR("akshu.agrawal@amd.com"); +MODULE_DESCRIPTION("cz-rt5645 audio support"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 330832ef4e5e..ecb458935d1e 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __ACP_HW_H #define __ACP_HW_H @@ -19,6 +20,7 @@ /* Capture SRAM address (as a source in dma descriptor) */ #define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000 +#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000 #define ACP_DMA_RESET_TIME 10000 #define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF @@ -67,6 +69,7 @@ #define CAPTURE_START_DMA_DESCR_CH15 6 #define CAPTURE_END_DMA_DESCR_CH15 7 +#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209 enum acp_dma_priority_level { /* 0x0 Specifies the DMA channel is given normal priority */ ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0, @@ -81,9 +84,26 @@ struct audio_substream_data { u16 num_of_pages; u16 direction; uint64_t size; + u64 renderbytescount; + u64 capturebytescount; void __iomem *acp_mmio; }; +struct audio_drv_data { + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; + void __iomem *acp_mmio; + u32 asic_type; +}; + +union acp_dma_count { + struct { + u32 low; + u32 high; + } bcount; + u64 bytescount; +}; + enum { ACP_TILE_P1 = 0, ACP_TILE_P2, diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 67e10cbd4ed7..4440646416e8 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # AT91 Platform Support snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h index 73f8fdd1ca83..0f2e25aeb458 100644 --- a/sound/soc/atmel/atmel-classd.h +++ b/sound/soc/atmel/atmel-classd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __ATMEL_CLASSD_H_ #define __ATMEL_CLASSD_H_ diff --git a/sound/soc/atmel/atmel-pdmic.h b/sound/soc/atmel/atmel-pdmic.h index 4527ac741919..1dd35187102c 100644 --- a/sound/soc/atmel/atmel-pdmic.h +++ b/sound/soc/atmel/atmel-pdmic.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __ATMEL_PDMIC_H_ #define __ATMEL_PDMIC_H_ diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile index 920710514ea0..33183d7fe057 100644 --- a/sound/soc/au1x/Makefile +++ b/sound/soc/au1x/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Au1200/Au1550 PSC audio snd-soc-au1xpsc-dbdma-objs := dbdma2.o snd-soc-au1xpsc-i2s-objs := psc-i2s.o diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 6ba20498202e..2e449d7173fc 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -31,6 +31,7 @@ * General Public License for more details. */ +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> @@ -99,6 +100,8 @@ #define BCM2835_I2S_CHWID(v) (v) #define BCM2835_I2S_CH1(v) ((v) << 16) #define BCM2835_I2S_CH2(v) (v) +#define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v)) +#define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_TX_PANIC(v) ((v) << 24) #define BCM2835_I2S_RX_PANIC(v) ((v) << 16) @@ -110,12 +113,19 @@ #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) +/* Frame length register is 10 bit, maximum length 1024 */ +#define BCM2835_I2S_MAX_FRAME_LENGTH 1024 + /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; struct snd_dmaengine_dai_dma_data dma_data[2]; unsigned int fmt; - unsigned int bclk_ratio; + unsigned int tdm_slots; + unsigned int rx_mask; + unsigned int tx_mask; + unsigned int slot_width; + unsigned int frame_length; struct regmap *i2s_regmap; struct clk *clk; @@ -225,19 +235,120 @@ static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - dev->bclk_ratio = ratio; + + if (!ratio) { + dev->tdm_slots = 0; + return 0; + } + + if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH) + return -EINVAL; + + dev->tdm_slots = 2; + dev->rx_mask = 0x03; + dev->tx_mask = 0x03; + dev->slot_width = ratio / 2; + dev->frame_length = ratio; + return 0; } +static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int width) +{ + struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + if (slots) { + if (slots < 0 || width < 0) + return -EINVAL; + + /* Limit masks to available slots */ + rx_mask &= GENMASK(slots - 1, 0); + tx_mask &= GENMASK(slots - 1, 0); + + /* + * The driver is limited to 2-channel setups. + * Check that exactly 2 bits are set in the masks. + */ + if (hweight_long((unsigned long) rx_mask) != 2 + || hweight_long((unsigned long) tx_mask) != 2) + return -EINVAL; + + if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH) + return -EINVAL; + } + + dev->tdm_slots = slots; + + dev->rx_mask = rx_mask; + dev->tx_mask = tx_mask; + dev->slot_width = width; + dev->frame_length = slots * width; + + return 0; +} + +/* + * Convert logical slot number into physical slot number. + * + * If odd_offset is 0 sequential number is identical to logical number. + * This is used for DSP modes with slot numbering 0 1 2 3 ... + * + * Otherwise odd_offset defines the physical offset for odd numbered + * slots. This is used for I2S and left/right justified modes to + * translate from logical slot numbers 0 1 2 3 ... into physical slot + * numbers 0 2 ... 3 4 ... + */ +static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset) +{ + if (!odd_offset) + return slot; + + if (slot & 1) + return (slot >> 1) + odd_offset; + + return slot >> 1; +} + +/* + * Calculate channel position from mask and slot width. + * + * Mask must contain exactly 2 set bits. + * Lowest set bit is channel 1 position, highest set bit channel 2. + * The constant offset is added to both channel positions. + * + * If odd_offset is > 0 slot positions are translated to + * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd + * logical slot numbers starting at physical slot odd_offset. + */ +static void bcm2835_i2s_calc_channel_pos( + unsigned int *ch1_pos, unsigned int *ch2_pos, + unsigned int mask, unsigned int width, + unsigned int bit_offset, unsigned int odd_offset) +{ + *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset) + * width + bit_offset; + *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset) + * width + bit_offset; +} + static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - unsigned int sampling_rate = params_rate(params); - unsigned int data_length, data_delay, bclk_ratio; - unsigned int ch1pos, ch2pos, mode, format; + unsigned int data_length, data_delay, framesync_length; + unsigned int slots, slot_width, odd_slot_offset; + int frame_length, bclk_rate; + unsigned int rx_mask, tx_mask; + unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos; + unsigned int mode, format; + bool bit_clock_master = false; + bool frame_sync_master = false; + bool frame_start_falling_edge = false; uint32_t csreg; + int ret = 0; /* * If a stream is already enabled, @@ -248,42 +359,70 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON)) return 0; - /* - * Adjust the data length according to the format. - * We prefill the half frame length with an integer - * divider of 2400 as explained at the clock settings. - * Maybe it is overwritten there, if the Integer mode - * does not apply. - */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - data_length = 16; - break; - case SNDRV_PCM_FORMAT_S24_LE: - data_length = 24; + data_length = params_width(params); + data_delay = 0; + odd_slot_offset = 0; + mode = 0; + + if (dev->tdm_slots) { + slots = dev->tdm_slots; + slot_width = dev->slot_width; + frame_length = dev->frame_length; + rx_mask = dev->rx_mask; + tx_mask = dev->tx_mask; + bclk_rate = dev->frame_length * params_rate(params); + } else { + slots = 2; + slot_width = params_width(params); + rx_mask = 0x03; + tx_mask = 0x03; + + frame_length = snd_soc_params_to_frame_size(params); + if (frame_length < 0) + return frame_length; + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) + return bclk_rate; + } + + /* Check if data fits into slots */ + if (data_length > slot_width) + return -EINVAL; + + /* Check if CPU is bit clock master */ + switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + bit_clock_master = true; break; - case SNDRV_PCM_FORMAT_S32_LE: - data_length = 32; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + bit_clock_master = false; break; default: return -EINVAL; } - /* If bclk_ratio already set, use that one. */ - if (dev->bclk_ratio) - bclk_ratio = dev->bclk_ratio; - else - /* otherwise calculate a fitting block ratio */ - bclk_ratio = 2 * data_length; - - /* Clock should only be set up here if CPU is clock master */ + /* Check if CPU is frame sync master */ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + frame_sync_master = true; + break; case SND_SOC_DAIFMT_CBS_CFM: - clk_set_rate(dev->clk, sampling_rate * bclk_ratio); + case SND_SOC_DAIFMT_CBM_CFM: + frame_sync_master = false; break; default: - break; + return -EINVAL; + } + + /* Clock should only be set up here if CPU is clock master */ + if (bit_clock_master) { + ret = clk_set_rate(dev->clk, bclk_rate); + if (ret) + return ret; } /* Setup the frame format */ @@ -294,43 +433,94 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, format |= BCM2835_I2S_CHWID((data_length-8)&0xf); + /* CH2 format is the same as for CH1 */ + format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); + switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - data_delay = 1; - break; - default: + /* I2S mode needs an even number of slots */ + if (slots & 1) + return -EINVAL; + /* - * TODO - * Others are possible but are not implemented at the moment. + * Use I2S-style logical slot numbering: even slots + * are in first half of frame, odd slots in second half. */ - dev_err(dev->dev, "%s:bad format\n", __func__); - return -EINVAL; - } + odd_slot_offset = slots >> 1; - ch1pos = data_delay; - ch2pos = bclk_ratio / 2 + data_delay; + /* MSB starts one cycle after frame start */ + data_delay = 1; - switch (params_channels(params)) { - case 2: - format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); - format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos)); - format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos)); + /* Setup frame sync signal for 50% duty cycle */ + framesync_length = frame_length / 2; + frame_start_falling_edge = true; + break; + case SND_SOC_DAIFMT_LEFT_J: + if (slots & 1) + return -EINVAL; + + odd_slot_offset = slots >> 1; + data_delay = 0; + framesync_length = frame_length / 2; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_RIGHT_J: + if (slots & 1) + return -EINVAL; + + /* Odd frame lengths aren't supported */ + if (frame_length & 1) + return -EINVAL; + + odd_slot_offset = slots >> 1; + data_delay = slot_width - data_length; + framesync_length = frame_length / 2; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_DSP_A: + data_delay = 1; + framesync_length = 1; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_DSP_B: + data_delay = 0; + framesync_length = 1; + frame_start_falling_edge = false; break; default: return -EINVAL; } + bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos, + rx_mask, slot_width, data_delay, odd_slot_offset); + bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos, + tx_mask, slot_width, data_delay, odd_slot_offset); + + /* + * Transmitting data immediately after frame start, eg + * in left-justified or DSP mode A, only works stable + * if bcm2835 is the frame clock master. + */ + if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master) + dev_warn(dev->dev, + "Unstable slave config detected, L/R may be swapped"); + /* * Set format for both streams. * We cannot set another frame length * (and therefore word length) anyway, * so the format will be the same. */ - regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); - regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); + regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, + format + | BCM2835_I2S_CH1_POS(rx_ch1_pos) + | BCM2835_I2S_CH2_POS(rx_ch2_pos)); + regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, + format + | BCM2835_I2S_CH1_POS(tx_ch1_pos) + | BCM2835_I2S_CH2_POS(tx_ch2_pos)); /* Setup the I2S mode */ - mode = 0; if (data_length <= 16) { /* @@ -342,65 +532,41 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; } - mode |= BCM2835_I2S_FLEN(bclk_ratio - 1); - mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2); + mode |= BCM2835_I2S_FLEN(frame_length - 1); + mode |= BCM2835_I2S_FSLEN(framesync_length); - /* Master or slave? */ - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* CPU is master */ - break; - case SND_SOC_DAIFMT_CBM_CFS: - /* - * CODEC is bit clock master - * CPU is frame master - */ + /* CLKM selects bcm2835 clock slave mode */ + if (!bit_clock_master) mode |= BCM2835_I2S_CLKM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /* - * CODEC is frame master - * CPU is bit clock master - */ + + /* FSM selects bcm2835 frame sync slave mode */ + if (!frame_sync_master) mode |= BCM2835_I2S_FSM; + + /* CLKI selects normal clocking mode, sampling on rising edge */ + switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_NB_IF: + mode |= BCM2835_I2S_CLKI; break; - case SND_SOC_DAIFMT_CBM_CFM: - /* CODEC is master */ - mode |= BCM2835_I2S_CLKM; - mode |= BCM2835_I2S_FSM; + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: break; default: - dev_err(dev->dev, "%s:bad master\n", __func__); return -EINVAL; } - /* - * Invert clocks? - * - * The BCM approach seems to be inverted to the classical I2S approach. - */ + /* FSI selects frame start on falling edge */ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: - /* None. Therefore, both for BCM */ - mode |= BCM2835_I2S_CLKI; - mode |= BCM2835_I2S_FSI; - break; - case SND_SOC_DAIFMT_IB_IF: - /* Both. Therefore, none for BCM */ + case SND_SOC_DAIFMT_IB_NF: + if (frame_start_falling_edge) + mode |= BCM2835_I2S_FSI; break; case SND_SOC_DAIFMT_NB_IF: - /* - * Invert only frame sync. Therefore, - * invert only bit clock for BCM - */ - mode |= BCM2835_I2S_CLKI; - break; - case SND_SOC_DAIFMT_IB_NF: - /* - * Invert only bit clock. Therefore, - * invert only frame sync for BCM - */ - mode |= BCM2835_I2S_FSI; + case SND_SOC_DAIFMT_IB_IF: + if (!frame_start_falling_edge) + mode |= BCM2835_I2S_FSI; break; default: return -EINVAL; @@ -423,7 +589,27 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* Clear FIFOs */ bcm2835_i2s_clear_fifos(dev, true, true); - return 0; + dev_dbg(dev->dev, + "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n", + slots, slot_width, rx_mask, tx_mask); + + dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n", + frame_length, framesync_length, data_length); + + dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n", + rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos); + + dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n", + params_rate(params), bclk_rate); + + dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n", + !!(mode & BCM2835_I2S_CLKM), + !!(mode & BCM2835_I2S_CLKI), + !!(mode & BCM2835_I2S_FSM), + !!(mode & BCM2835_I2S_FSI), + (mode & BCM2835_I2S_FSI) ? "falling" : "rising"); + + return ret; } static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream, @@ -559,6 +745,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, + .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) @@ -578,7 +765,9 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE @@ -586,13 +775,16 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .ops = &bcm2835_i2s_dai_ops, - .symmetric_rates = 1 + .symmetric_rates = 1, + .symmetric_samplebits = 1, }; static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -699,9 +891,6 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; - /* BCLK ratio - use default */ - dev->bclk_ratio = 0; - /* Store the pdev */ dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 15c438f0f22d..abafadc0b534 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -655,23 +655,10 @@ static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE); - /* Configure channels as mono or stereo/TDM */ - if (params_channels(params) == 1) - value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); - else - value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); + value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - if (aio->port_type == PORT_SPDIF) { - dev_err(aio->cygaud->dev, - "SPDIF does not support 8bit format\n"); - return -EINVAL; - } - bitres = 8; - break; - case SNDRV_PCM_FORMAT_S16_LE: bitres = 16; break; @@ -842,6 +829,7 @@ int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len) return -EINVAL; } } +EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width); static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -998,7 +986,7 @@ static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, active_slots = hweight32(tx_mask); - if ((active_slots < 0) || (active_slots > 16)) + if (active_slots > 16) return -EINVAL; /* Slot value must be even */ @@ -1136,15 +1124,21 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .set_tdm_slot = cygnus_set_dai_tdm_slot, }; +static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { + .startup = cygnus_ssp_startup, + .shutdown = cygnus_ssp_shutdown, + .trigger = cygnus_ssp_trigger, + .hw_params = cygnus_ssp_hw_params, + .set_sysclk = cygnus_ssp_set_sysclk, +}; #define INIT_CPU_DAI(num) { \ .name = "cygnus-ssp" #num, \ .playback = { \ - .channels_min = 1, \ + .channels_min = 2, \ .channels_max = 16, \ .rates = SNDRV_PCM_RATE_KNOT, \ - .formats = SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .capture = { \ @@ -1152,7 +1146,7 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .channels_max = 16, \ .rates = SNDRV_PCM_RATE_KNOT, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ + SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .ops = &cygnus_ssp_dai_ops, \ .suspend = cygnus_ssp_suspend, \ @@ -1174,7 +1168,7 @@ static const struct snd_soc_dai_driver cygnus_spdif_dai_info = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, - .ops = &cygnus_ssp_dai_ops, + .ops = &cygnus_spdif_dai_ops, .suspend = cygnus_ssp_suspend, .resume = cygnus_ssp_resume, }; diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index f21e948b2e9b..ebeb6a9cedd2 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Blackfin Platform Support snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o diff --git a/sound/soc/cirrus/Makefile b/sound/soc/cirrus/Makefile index 5514146cbdf0..bfb8dc409f53 100644 --- a/sound/soc/cirrus/Makefile +++ b/sound/soc/cirrus/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # EP93xx Platform Support snd-soc-ep93xx-objs := ep93xx-pcm.o snd-soc-ep93xx-i2s-objs := ep93xx-i2s.o diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0838ae710941..a42ddbc93f3d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -749,6 +749,10 @@ config SND_SOC_RT5514 config SND_SOC_RT5514_SPI tristate +config SND_SOC_RT5514_SPI_BUILTIN + bool # force RT5514_SPI to be built-in to avoid link errors + default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m + config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 77c18189c9ad..0001069ce2a7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-88pm860x-objs := 88pm860x-codec.o snd-soc-ab8500-codec-objs := ab8500-codec.o snd-soc-ac97-objs := ac97.o @@ -359,6 +360,7 @@ obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o +obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h index 939b5f37762f..bf5947b35390 100644 --- a/sound/soc/codecs/adau-utils.h +++ b/sound/soc/codecs/adau-utils.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef SOUND_SOC_CODECS_ADAU_PLL_H #define SOUND_SOC_CODECS_ADAU_PLL_H diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h index c6ab5530760c..56320d5e32d8 100644 --- a/sound/soc/codecs/adau1373.h +++ b/sound/soc/codecs/adau1373.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __ADAU1373_H__ #define __ADAU1373_H__ diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 2c1bd2763864..6758f789b712 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -90,6 +90,27 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w, return 0; } +static int adau17x1_adc_fixup(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 adau *adau = snd_soc_codec_get_drvdata(codec); + + /* + * If we are capturing, toggle the ADOSR bit in Converter Control 0 to + * avoid losing SNR (workaround from ADI). This must be done after + * the ADC(s) have been enabled. According to the data sheet, it is + * normally illegal to set this bit when the sampling rate is 96 kHz, + * but according to ADI it is acceptable for this workaround. + */ + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR); + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, 0); + + return 0; +} + static const char * const adau17x1_mono_stereo_text[] = { "Stereo", "Mono Left Channel (L+R)", @@ -121,7 +142,8 @@ static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = { SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0, &adau17x1_dac_mode_mux), - SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0), + SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0, + adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0), SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0), SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0), diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h index bf04b7efee40..eaf8f933bab8 100644 --- a/sound/soc/codecs/adau17x1.h +++ b/sound/soc/codecs/adau17x1.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __ADAU17X1_H__ #define __ADAU17X1_H__ @@ -129,5 +130,7 @@ bool adau17x1_has_dsp(struct adau *adau); #define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7 +#define ADAU17X1_CONVERTER0_ADOSR BIT(3) + #endif diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index a1149f6a8450..b3375e19598a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/gcd.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_gpio); -int arizona_init_notifiers(struct snd_soc_codec *codec) +int arizona_init_common(struct arizona *arizona) { - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->arizona; + struct arizona_pdata *pdata = &arizona->pdata; + unsigned int val, mask; + int i; BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier); + for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { + /* Default is 0 so noop with defaults */ + if (pdata->out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (pdata->spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + pdata->spk_mute[i]); + + if (pdata->spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), + ARIZONA_SPK1_FMT_MASK, + pdata->spk_fmt[i]); + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT; + if (pdata->inmode[i] & ARIZONA_INMODE_DMIC) + val |= 1 << ARIZONA_IN1_MODE_SHIFT; + + switch (arizona->type) { + case WM8998: + case WM1814: + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8), + ARIZONA_IN1L_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1L_SRC_SE_SHIFT); + + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8), + ARIZONA_IN1R_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1R_SRC_SE_SHIFT); + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK; + break; + default: + if (pdata->inmode[i] & ARIZONA_INMODE_SE) + val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT; + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK | + ARIZONA_IN1_SINGLE_ENDED_MASK; + break; + } + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + mask, val); + } + return 0; } -EXPORT_SYMBOL_GPL(arizona_init_notifiers); +EXPORT_SYMBOL_GPL(arizona_init_common); + +int arizona_init_vol_limit(struct arizona *arizona) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) { + if (arizona->pdata.out_vol_limit[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4, + ARIZONA_OUT1L_VOL_LIM_MASK, + arizona->pdata.out_vol_limit[i]); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_vol_limit); const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); +int arizona_of_get_audio_pdata(struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + struct device_node *np = arizona->dev->of_node; + struct property *prop; + const __be32 *cur; + u32 val; + u32 pdm_val[ARIZONA_MAX_PDM_SPK]; + int ret; + int count = 0; + + count = 0; + of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->inmode)) + break; + + pdata->inmode[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->dmic_ref)) + break; + + pdata->dmic_ref[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_mono)) + break; + + pdata->out_mono[count] = !!val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->max_channels_clocked)) + break; + + pdata->max_channels_clocked[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_vol_limit)) + break; + + pdata->out_vol_limit[count] = val; + count++; + } + + ret = of_property_read_u32_array(np, "wlf,spk-fmt", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count) + pdata->spk_fmt[count] = pdm_val[count]; + + ret = of_property_read_u32_array(np, "wlf,spk-mute", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count) + pdata->spk_mute[count] = pdm_val[count]; + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 1822e3b3de80..dfdf6d8c9687 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -313,7 +313,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source, int arizona_init_spk(struct snd_soc_codec *codec); int arizona_init_gpio(struct snd_soc_codec *codec); int arizona_init_mono(struct snd_soc_codec *codec); -int arizona_init_notifiers(struct snd_soc_codec *codec); + +int arizona_init_common(struct arizona *arizona); +int arizona_init_vol_limit(struct arizona *arizona); int arizona_init_spk_irqs(struct arizona *arizona); int arizona_free_spk_irqs(struct arizona *arizona); @@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec, return blocking_notifier_chain_unregister(&arizona->notifier, nb); } +int arizona_of_get_audio_pdata(struct arizona *arizona); + #endif diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h index 9adad8eefdc9..290283a9149e 100644 --- a/sound/soc/codecs/cs4271.h +++ b/sound/soc/codecs/cs4271.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _CS4271_PRIV_H #define _CS4271_PRIV_H diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index 643e37fc218e..5ba0edc19df4 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -909,6 +909,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << CS43130_DSD_SRC_SHIFT); + break; } if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) @@ -1039,6 +1040,7 @@ static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol, else regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq, ARRAY_SIZE(pcm_ch_dis_seq)); + break; } return snd_soc_put_enum_double(kcontrol, ucontrol); @@ -1152,6 +1154,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dsd_seq, ARRAY_SIZE(dsd_seq)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1162,6 +1165,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); + break; } break; case SND_SOC_DAPM_PRE_PMD: @@ -1184,6 +1188,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; } break; default: @@ -1206,6 +1211,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pcm_seq, ARRAY_SIZE(pcm_seq)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1216,6 +1222,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); + break; } break; case SND_SOC_DAPM_PRE_PMD: @@ -1238,6 +1245,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; } break; default: @@ -1277,6 +1285,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pop_free_seq2, ARRAY_SIZE(pop_free_seq2)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1301,6 +1310,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: usleep_range(12000, 12010); regmap_write(cs43130->regmap, CS43130_DXD13, 0); + break; } regmap_write(cs43130->regmap, CS43130_DXD1, 0); @@ -1311,6 +1321,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq, ARRAY_SIZE(dac_postpmd_seq)); + break; } break; default: @@ -2133,6 +2144,7 @@ exit: cs43130_hpload_proc(cs43130, hp_dis_cal_seq2, ARRAY_SIZE(hp_dis_cal_seq2), CS43130_HPLOAD_OFF_INT, ac_idx); + break; } regmap_multi_reg_write(cs43130->regmap, hp_cln_seq, @@ -2543,6 +2555,7 @@ static int cs43130_i2c_probe(struct i2c_client *client, digital_hp_routes; soc_codec_dev_cs43130.component_driver.num_dapm_routes = ARRAY_SIZE(digital_hp_routes); + break; } ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_cs43130, @@ -2586,8 +2599,7 @@ static int cs43130_i2c_remove(struct i2c_client *client) device_remove_file(&client->dev, &dev_attr_hpload_ac_r); } - if (cs43130->reset_gpio) - gpiod_set_value_cansleep(cs43130->reset_gpio, 0); + gpiod_set_value_cansleep(cs43130->reset_gpio, 0); pm_runtime_disable(&client->dev); regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index e09fc8f037f1..94c0209977d0 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - arizona_init_notifiers(codec); ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); if (ret) @@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev) if (!cs47l24) return -ENOMEM; + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + platform_set_drvdata(pdev, cs47l24); cs47l24->core.arizona = arizona; @@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index cc0b2d2eaf15..41d9b1da27c2 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1220,6 +1220,7 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct snd_soc_codec *codec = codec_dai->codec; struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); u8 dai_clk_mode = 0, dai_ctrl = 0; + u8 dai_offset = 0; /* Set master/slave mode */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1234,17 +1235,46 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) } /* Set clock normal/inverted */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - case SND_SOC_DAIFMT_NB_IF: - dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; - break; - case SND_SOC_DAIFMT_IB_NF: - dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } break; - case SND_SOC_DAIFMT_IB_IF: - dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV; + case SND_SOC_DAI_FORMAT_DSP_A: + case SND_SOC_DAI_FORMAT_DSP_B: + /* The bclk is inverted wrt ASoC conventions */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -1261,6 +1291,13 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_RIGHT_J: dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J; break; + case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + dai_offset = 1; + break; + case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + break; default: return -EINVAL; } @@ -1271,6 +1308,7 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode); snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK, dai_ctrl); + snd_soc_write(codec, DA7213_DAI_OFFSET, dai_offset); return 0; } diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 16ef56f77cd4..5a78dba1dcb5 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -188,6 +188,7 @@ #define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0) #define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0) #define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7213_DAI_FORMAT_DSP (0x3 << 0) #define DA7213_DAI_FORMAT_MASK (0x3 << 0) #define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2) #define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2) diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h index 8930322d712b..9109f6b5b045 100644 --- a/sound/soc/codecs/es8328.h +++ b/sound/soc/codecs/es8328.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * es8328.h -- ES8328 ALSA SoC Audio driver */ diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e824d47cc22b..f3b4f4dfae6a 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -942,7 +942,8 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, if (!se) return -ENOMEM; - sprintf(kc_name, "Pin %d port %d Input", pin->nid, port->id); + snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", + pin->nid, port->id); kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -1452,6 +1453,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, int i, num_nodes; struct hdac_device *hdac = &edev->hdac; struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_cvt *temp_cvt, *cvt_next; + struct hdac_hdmi_pin *temp_pin, *pin_next; int ret; hdac_hdmi_skl_enable_all_pins(hdac); @@ -1481,32 +1484,54 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, case AC_WID_AUD_OUT: ret = hdac_hdmi_add_cvt(edev, nid); if (ret < 0) - return ret; + goto free_widgets; break; case AC_WID_PIN: ret = hdac_hdmi_add_pin(edev, nid); if (ret < 0) - return ret; + goto free_widgets; break; } } hdac->end_nid = nid; - if (!hdmi->num_pin || !hdmi->num_cvt) - return -EIO; + if (!hdmi->num_pin || !hdmi->num_cvt) { + ret = -EIO; + goto free_widgets; + } ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); if (ret) { dev_err(&hdac->dev, "Failed to create dais with err: %d\n", ret); - return ret; + goto free_widgets; } *num_dais = hdmi->num_cvt; + ret = hdac_hdmi_init_dai_map(edev); + if (ret < 0) + goto free_widgets; + + return ret; + +free_widgets: + list_for_each_entry_safe(temp_cvt, cvt_next, &hdmi->cvt_list, head) { + list_del(&temp_cvt->head); + kfree(temp_cvt->name); + kfree(temp_cvt); + } + + list_for_each_entry_safe(temp_pin, pin_next, &hdmi->pin_list, head) { + for (i = 0; i < temp_pin->num_ports; i++) + temp_pin->ports[i].pin = NULL; + kfree(temp_pin->ports); + list_del(&temp_pin->head); + kfree(temp_pin); + } - return hdac_hdmi_init_dai_map(edev); + return ret; } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) @@ -1894,6 +1919,9 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; + if (!pcm) + return; + if (list_empty(&pcm->port_list)) return; @@ -1912,6 +1940,9 @@ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + if (!pcm) + return false; + if (list_empty(&pcm->port_list)) return false; @@ -1925,6 +1956,9 @@ 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_port *port; + if (!pcm) + return 0; + if (list_empty(&pcm->port_list)) return 0; @@ -1978,6 +2012,9 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + if (!hdac_id) + return -ENODEV; + if (hdac_id->driver_data) hdmi_priv->drv_data = (struct hdac_hdmi_drv_data *)hdac_id->driver_data; diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h index dfc3a9cf7199..b5b57a5cbbfd 100644 --- a/sound/soc/codecs/hdac_hdmi.h +++ b/sound/soc/codecs/hdac_hdmi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __HDAC_HDMI_H__ #define __HDAC_HDMI_H__ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 3abf82563408..5672e516bec3 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -303,11 +303,8 @@ enum { 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); + uinfo->count = FIELD_SIZEOF(struct hdmi_codec_priv, eld); return 0; } diff --git a/sound/soc/codecs/inno_rk3036.h b/sound/soc/codecs/inno_rk3036.h index da759c6c7501..44bb2404198d 100644 --- a/sound/soc/codecs/inno_rk3036.h +++ b/sound/soc/codecs/inno_rk3036.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Driver of Inno Codec for rk3036 by Rockchip Inc. * diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 13bcfb1ef9b4..f5075d1f79e6 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2115,7 +2115,7 @@ static void max98090_pll_work(struct work_struct *work) if (!snd_soc_codec_is_active(codec)) return; - dev_info(codec->dev, "PLL unlocked\n"); + dev_info_ratelimited(codec->dev, "PLL unlocked\n"); /* Toggle shutdown OFF then ON */ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index 327eaa25c9bd..921f95fc396d 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -579,7 +579,7 @@ static int max98925_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(max98925->regmap); dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); - goto err_out; + return ret; } if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { @@ -596,16 +596,20 @@ static int max98925_i2c_probe(struct i2c_client *i2c, } max98925->i_slot = value; } - ret = regmap_read(max98925->regmap, - MAX98925_REV_VERSION, ®); - if ((ret < 0) || - ((reg != MAX98925_VERSION) && - (reg != MAX98925_VERSION1))) { - dev_err(&i2c->dev, - "device initialization error (%d 0x%02X)\n", + + ret = regmap_read(max98925->regmap, MAX98925_REV_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Read revision failed\n"); + return ret; + } + + if ((reg != MAX98925_VERSION) && (reg != MAX98925_VERSION1)) { + ret = -ENODEV; + dev_err(&i2c->dev, "Invalid revision (%d 0x%02X)\n", ret, reg); - goto err_out; + return ret; } + dev_info(&i2c->dev, "device version 0x%02X\n", reg); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925, @@ -613,7 +617,6 @@ static int max98925_i2c_probe(struct i2c_client *i2c, if (ret < 0) dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); -err_out: return ret; } diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index d9dbbe72f8ad..a1d39353719d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -1,7 +1,7 @@ /* * max98927.c -- MAX98927 ALSA Soc Audio driver * - * Copyright (C) 2016 Maxim Integrated Products + * Copyright (C) 2016-2017 Maxim Integrated Products * Author: Ryan Lee <ryans.lee@maximintegrated.com> * * This program is free software; you can redistribute it and/or modify it @@ -146,6 +146,7 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); unsigned int mode = 0; unsigned int format = 0; + bool use_pdm = false; unsigned int invert = 0; dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); @@ -187,22 +188,27 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - max98927->iface |= SND_SOC_DAIFMT_I2S; format = MAX98927_PCM_FORMAT_I2S; break; case SND_SOC_DAIFMT_LEFT_J: - max98927->iface |= SND_SOC_DAIFMT_LEFT_J; format = MAX98927_PCM_FORMAT_LJ; break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98927_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98927_PCM_FORMAT_TDM_MODE0; + break; case SND_SOC_DAIFMT_PDM: - max98927->iface |= SND_SOC_DAIFMT_PDM; + use_pdm = true; break; default: return -EINVAL; } + max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK; - /* pcm channel configuration */ - if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) { + if (!use_pdm) { + /* pcm channel configuration */ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, @@ -217,13 +223,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) MAX98927_R003B_SPK_SRC_SEL, MAX98927_SPK_SRC_MASK, 0); - } else regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); - - /* pdm channel configuration */ - if (max98927->iface & SND_SOC_DAIFMT_PDM) { + MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); + } else { + /* pdm channel configuration */ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, MAX98927_PDM_RX_EN_MASK, 1); @@ -231,10 +235,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, MAX98927_SPK_SRC_MASK, 3); - } else + regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 0); + MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); + } return 0; } @@ -245,6 +250,21 @@ static const int rate_table[] = { 13000000, 19200000, }; +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, +}; + +static int max98927_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} static int max98927_set_clock(struct max98927_priv *max98927, struct snd_pcm_hw_params *params) { @@ -270,23 +290,20 @@ static int max98927_set_clock(struct max98927_priv *max98927, i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); } - switch (blr_clk_ratio) { - case 32: - value = 2; - break; - case 48: - value = 3; - break; - case 64: - value = 4; - break; - default: - return -EINVAL; + if (!max98927->tdm_mode) { + /* BCLK configuration */ + value = max98927_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(codec->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, + value); } - regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - value); return 0; } @@ -386,6 +403,78 @@ err: return -EINVAL; } +static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + int bsel = 0; + unsigned int chan_sz = 0; + + max98927->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98927_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(codec->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(codec->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R0018_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R0019_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); + + /* Tx slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R001A_PCM_TX_EN_A, + tx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R001B_PCM_TX_EN_B, + (tx_mask & 0xFF00) >> 8); + + /* Tx slot Hi-Z configuration */ + regmap_write(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + #define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 #define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -405,6 +494,7 @@ static const struct snd_soc_dai_ops max98927_dai_ops = { .set_sysclk = max98927_dai_set_sysclk, .set_fmt = max98927_dai_set_fmt, .hw_params = max98927_dai_hw_params, + .set_tdm_slot = max98927_dai_tdm_slot, }; static int max98927_dac_event(struct snd_soc_dapm_widget *w, @@ -414,6 +504,9 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w, struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); switch (event) { + case SND_SOC_DAPM_PRE_PMU: + max98927->tdm_mode = 0; + break; case SND_SOC_DAPM_POST_PMU: regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h index ece6a608cbe1..9ea839735433 100644 --- a/sound/soc/codecs/max98927.h +++ b/sound/soc/codecs/max98927.h @@ -1,7 +1,7 @@ /* * max98927.h -- MAX98927 ALSA Soc Audio driver * - * Copyright 2013-15 Maxim Integrated Products + * Copyright (C) 2016-2017 Maxim Integrated Products * Author: Ryan Lee <ryans.lee@maximintegrated.com> * * This program is free software; you can redistribute it and/or modify it @@ -161,7 +161,9 @@ #define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3) #define MAX98927_PCM_FORMAT_I2S (0x0 << 0) #define MAX98927_PCM_FORMAT_LJ (0x1 << 0) - +#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0) #define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) #define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) #define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) @@ -268,5 +270,6 @@ struct max98927_priv { unsigned int iface; unsigned int master; unsigned int digital_gain; + bool tdm_mode; }; #endif diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 549c269acc7d..5f3c42c4f74a 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -104,7 +104,7 @@ #define CDC_A_MICB_1_VAL (0xf141) #define MICB_MIN_VAL 1600 #define MICB_STEP_SIZE 50 -#define MICB_VOLTAGE_REGVAL(v) ((v - MICB_MIN_VAL)/MICB_STEP_SIZE) +#define MICB_VOLTAGE_REGVAL(v) (((v - MICB_MIN_VAL)/MICB_STEP_SIZE) << 3) #define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3) #define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3) #define MICB_1_VAL_MICB_OUT_VAL_V1P80V ((0x4) << 3) @@ -285,7 +285,7 @@ struct pm8916_wcd_analog_priv { u16 codec_version; bool mbhc_btn_enabled; /* special event to detect accessory type */ - bool mbhc_btn0_pressed; + int mbhc_btn0_released; bool detect_accessory_type; struct clk *mclk; struct snd_soc_codec *codec; @@ -349,8 +349,9 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec) | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE); if (wcd->micbias_mv) { - snd_soc_write(codec, CDC_A_MICB_1_VAL, - MICB_VOLTAGE_REGVAL(wcd->micbias_mv)); + snd_soc_update_bits(codec, CDC_A_MICB_1_VAL, + MICB_1_VAL_MICB_OUT_VAL_MASK, + MICB_VOLTAGE_REGVAL(wcd->micbias_mv)); /* * Special headset needs MICBIAS as 2.7V so wait for * 50 msec for the MICBIAS to reach 2.7 volts. @@ -443,50 +444,6 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct wcd->micbias1_cap_mode); } -static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd) -{ - struct snd_soc_codec *codec = wcd->codec; - u32 plug_type = 0; - u32 int_en_mask; - - snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1, - CDC_A_MBHC_DET_CTL_L_DET_EN | - CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION | - CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO | - CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN); - - if (wcd->hphl_jack_type_normally_open) - plug_type |= CDC_A_HPHL_PLUG_TYPE_NO; - - if (wcd->gnd_jack_type_normally_open) - plug_type |= CDC_A_GND_PLUG_TYPE_NO; - - snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2, - CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 | - CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD | - plug_type | - CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN); - - - snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER, - CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS | - CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS); - - /* enable MBHC clock */ - snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, - DIG_CLK_CTL_D_MBHC_CLK_EN_MASK, - DIG_CLK_CTL_D_MBHC_CLK_EN); - - int_en_mask = MBHC_SWITCH_INT; - if (wcd->mbhc_btn_enabled) - int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET; - - snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0); - snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask); - wcd->mbhc_btn0_pressed = false; - wcd->detect_accessory_type = true; -} - static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, bool micbias2_enabled) { @@ -534,6 +491,56 @@ static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, return 0; } +static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd) +{ + struct snd_soc_codec *codec = wcd->codec; + bool micbias_enabled = false; + u32 plug_type = 0; + u32 int_en_mask; + + snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1, + CDC_A_MBHC_DET_CTL_L_DET_EN | + CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION | + CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO | + CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN); + + if (wcd->hphl_jack_type_normally_open) + plug_type |= CDC_A_HPHL_PLUG_TYPE_NO; + + if (wcd->gnd_jack_type_normally_open) + plug_type |= CDC_A_GND_PLUG_TYPE_NO; + + snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2, + CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 | + CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD | + plug_type | + CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN); + + + snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER, + CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS | + CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS); + + /* enable MBHC clock */ + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_D_MBHC_CLK_EN_MASK, + DIG_CLK_CTL_D_MBHC_CLK_EN); + + if (snd_soc_read(codec, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE) + micbias_enabled = true; + + pm8916_mbhc_configure_bias(wcd, micbias_enabled); + + int_en_mask = MBHC_SWITCH_INT; + if (wcd->mbhc_btn_enabled) + int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET; + + snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0); + snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask); + wcd->mbhc_btn0_released = false; + wcd->detect_accessory_type = true; +} + static int pm8916_wcd_analog_enable_micbias_int2(struct snd_soc_dapm_widget *w, struct snd_kcontrol @@ -614,6 +621,7 @@ static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w, case CDC_A_TX_2_EN: snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, MICB_1_CTL_CFILT_REF_SEL_MASK, 0); + /* fall through */ case CDC_A_TX_3_EN: snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, CONN_TX2_SERIAL_TX2_MUX, @@ -950,7 +958,7 @@ static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg) /* check if its BTN0 thats released */ if ((val != -1) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK)) - priv->mbhc_btn0_pressed = false; + priv->mbhc_btn0_released = true; } else { snd_soc_jack_report(priv->jack, 0, btn_mask); @@ -983,9 +991,7 @@ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg) break; case 0x0: /* handle BTN_0 specially for type detection */ - if (priv->detect_accessory_type) - priv->mbhc_btn0_pressed = true; - else + if (!priv->detect_accessory_type) snd_soc_jack_report(priv->jack, SND_JACK_BTN_0, btn_mask); break; @@ -1029,19 +1035,19 @@ static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg) * both press and release event received then its * a headset. */ - if (priv->mbhc_btn0_pressed) + if (priv->mbhc_btn0_released) snd_soc_jack_report(priv->jack, - SND_JACK_HEADPHONE, hs_jack_mask); + SND_JACK_HEADSET, hs_jack_mask); else snd_soc_jack_report(priv->jack, - SND_JACK_HEADSET, hs_jack_mask); + SND_JACK_HEADPHONE, hs_jack_mask); priv->detect_accessory_type = false; } else { /* removal */ snd_soc_jack_report(priv->jack, 0, hs_jack_mask); priv->detect_accessory_type = true; - priv->mbhc_btn0_pressed = false; + priv->mbhc_btn0_released = false; } return IRQ_HANDLED; @@ -1241,6 +1247,8 @@ static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { { } }; +MODULE_DEVICE_TABLE(of, pm8916_wcd_analog_spmi_match_table); + static struct platform_driver pm8916_wcd_analog_spmi_driver = { .driver = { .name = "qcom,pm8916-wcd-spmi-codec", diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index 66df8f810f0d..a10a724eb448 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -238,7 +238,7 @@ static const struct soc_enum rx_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( static const struct soc_enum rx2_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), - SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text), }; /* RX2 MIX2 */ @@ -249,7 +249,7 @@ static const struct soc_enum rx2_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( static const struct soc_enum rx3_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), - SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text), }; /* DEC */ diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index dbff416e38be..5f9c069569d5 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or @@ -75,5 +75,5 @@ static struct i2c_driver pcm512x_i2c_driver = { module_i2c_driver(pcm512x_i2c_driver); MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C"); -MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 712ed6598c48..25c63510ae15 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 68feae262476..e0f3556d3872 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or @@ -1602,5 +1602,5 @@ const struct dev_pm_ops pcm512x_pm_ops = { EXPORT_SYMBOL_GPL(pcm512x_pm_ops); MODULE_DESCRIPTION("ASoC PCM512x codec driver"); -MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index b7c310207223..d70d9c0c2088 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 7b447d0b173a..974a9040651d 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -71,7 +71,7 @@ EXPORT_SYMBOL_GPL(rl6231_get_pre_div); */ int rl6231_calc_dmic_clk(int rate) { - int div[] = {2, 3, 4, 6, 8, 12}; + static const int div[] = {2, 3, 4, 6, 8, 12}; int i; if (rate < 1000000 * div[0]) { @@ -189,7 +189,8 @@ EXPORT_SYMBOL_GPL(rl6231_pll_calc); int rl6231_get_clk_info(int sclk, int rate) { - int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; if (sclk <= 0 || rate <= 0) return -EINVAL; diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index ed6e5373916c..2df91db765ac 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -145,11 +145,15 @@ done: mutex_unlock(&rt5514_dsp->dma_lock); } -static irqreturn_t rt5514_spi_irq(int irq, void *data) +static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) { - struct rt5514_dsp *rt5514_dsp = data; + size_t period_bytes; u8 buf[8]; + if (!rt5514_dsp->substream) + return; + + period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); rt5514_dsp->get_size = 0; /** @@ -177,9 +181,20 @@ static irqreturn_t rt5514_spi_irq(int irq, void *data) rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base; + if (rt5514_dsp->buf_size % period_bytes) + rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) * + period_bytes; + if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit && rt5514_dsp->buf_rp && rt5514_dsp->buf_size) schedule_delayed_work(&rt5514_dsp->copy_work, 0); +} + +static irqreturn_t rt5514_spi_irq(int irq, void *data) +{ + struct rt5514_dsp *rt5514_dsp = data; + + rt5514_schedule_copy(rt5514_dsp); return IRQ_HANDLED; } @@ -199,12 +214,19 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream, struct rt5514_dsp *rt5514_dsp = snd_soc_platform_get_drvdata(rtd->platform); int ret; + u8 buf[8]; mutex_lock(&rt5514_dsp->dma_lock); ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); rt5514_dsp->substream = substream; rt5514_dsp->dma_offset = 0; + + /* Read IRQ status and schedule copy accordingly. */ + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + mutex_unlock(&rt5514_dsp->dma_lock); return ret; @@ -434,9 +456,45 @@ static int rt5514_spi_probe(struct spi_device *spi) return ret; } + device_init_wakeup(&spi->dev, true); + return 0; } +static int __maybe_unused rt5514_suspend(struct device *dev) +{ + int irq = to_spi_device(dev)->irq; + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + + return 0; +} + +static int __maybe_unused rt5514_resume(struct device *dev) +{ + struct snd_soc_platform *platform = snd_soc_lookup_platform(dev); + struct rt5514_dsp *rt5514_dsp = + snd_soc_platform_get_drvdata(platform); + int irq = to_spi_device(dev)->irq; + u8 buf[8]; + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + + if (rt5514_dsp->substream) { + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + } + + return 0; +} + +static const struct dev_pm_ops rt5514_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume) +}; + static const struct of_device_id rt5514_of_match[] = { { .compatible = "realtek,rt5514", }, {}, @@ -446,6 +504,7 @@ MODULE_DEVICE_TABLE(of, rt5514_of_match); static struct spi_driver rt5514_spi_driver = { .driver = { .name = "rt5514", + .pm = &rt5514_pm_ops, .of_match_table = of_match_ptr(rt5514_of_match), }, .probe = rt5514_spi_probe, diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h index a6434ee6ff03..c1a36647c119 100644 --- a/sound/soc/codecs/rt5514-spi.h +++ b/sound/soc/codecs/rt5514-spi.h @@ -20,6 +20,9 @@ #define RT5514_BUFFER_VOICE_BASE 0x18000200 #define RT5514_BUFFER_VOICE_LIMIT 0x18000204 #define RT5514_BUFFER_VOICE_WP 0x1800020c +#define RT5514_IRQ_CTRL 0x18002094 + +#define RT5514_IRQ_STATUS_BIT (0x1 << 5) /* SPI Command */ enum { diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 0945d212b8dc..2a5b5d74e697 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -338,39 +338,6 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, fw = NULL; } - if (rt5514->model_buf && rt5514->model_len) { -#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) - int ret; - - ret = rt5514_spi_burst_write(0x4ff80000, - rt5514->model_buf, - ((rt5514->model_len / 8) + 1) * 8); - if (ret) { - dev_err(codec->dev, - "Model load failed %d\n", ret); - return ret; - } -#else - dev_err(codec->dev, - "No SPI driver for loading firmware\n"); -#endif - } else { - request_firmware(&fw, RT5514_FIRMWARE3, - codec->dev); - if (fw) { -#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) - rt5514_spi_burst_write(0x4ff80000, - fw->data, - ((fw->size/8)+1)*8); -#else - dev_err(codec->dev, - "No SPI driver to load fw\n"); -#endif - release_firmware(fw); - fw = NULL; - } - } - /* DSP run */ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055148); @@ -385,34 +352,6 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, return 0; } -static int rt5514_hotword_model_put(struct snd_kcontrol *kcontrol, - const unsigned int __user *bytes, unsigned int size) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); - struct snd_soc_codec *codec = rt5514->codec; - int ret = 0; - - if (rt5514->model_buf || rt5514->model_len < size) { - if (rt5514->model_buf) - devm_kfree(codec->dev, rt5514->model_buf); - rt5514->model_buf = devm_kmalloc(codec->dev, size, GFP_KERNEL); - if (!rt5514->model_buf) { - ret = -ENOMEM; - goto done; - } - } - - /* Skips the TLV header. */ - bytes += 2; - - if (copy_from_user(rt5514->model_buf, bytes, size)) - ret = -EFAULT; -done: - rt5514->model_len = (ret ? 0 : size); - return ret; -} - static const struct snd_kcontrol_new rt5514_snd_controls[] = { SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), @@ -424,8 +363,6 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = { adc_vol_tlv), SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0, rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put), - SND_SOC_BYTES_TLV("Hotword Model", 0x8504, - NULL, rt5514_hotword_model_put), }; /* ADC Mixer*/ @@ -1206,7 +1143,7 @@ static const struct acpi_device_id rt5514_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match); #endif -static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev) +static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev) { device_property_read_u32(dev, "realtek,dmic-init-delay-ms", &rt5514->pdata.dmic_init_delay); @@ -1246,8 +1183,8 @@ static int rt5514_i2c_probe(struct i2c_client *i2c, if (pdata) rt5514->pdata = *pdata; - else if (i2c->dev.of_node) - rt5514_parse_dt(rt5514, &i2c->dev); + else + rt5514_parse_dp(rt5514, &i2c->dev); rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); if (IS_ERR(rt5514->i2c_regmap)) { diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h index 803311cb7e2a..2dc40e6d8b3f 100644 --- a/sound/soc/codecs/rt5514.h +++ b/sound/soc/codecs/rt5514.h @@ -255,7 +255,6 @@ #define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin" #define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin" -#define RT5514_FIRMWARE3 "rt5514_dsp_fw3.bin" /* System Clock Source */ enum { @@ -282,8 +281,6 @@ struct rt5514_priv { int pll_in; int pll_out; int dsp_enabled; - u8 *model_buf; - unsigned int model_len; }; #endif /* __RT5514_H__ */ diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index c94e94fe8297..0e5f54a9bc7e 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -98,7 +98,7 @@ static const struct reg_default rt5616_reg[] = { { 0x8e, 0x0004 }, { 0x8f, 0x1100 }, { 0x90, 0x0000 }, - { 0x91, 0x0000 }, + { 0x91, 0x0c00 }, { 0x92, 0x0000 }, { 0x93, 0x2000 }, { 0x94, 0x0200 }, diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h index 13401581b0df..8a6b99a48c7c 100644 --- a/sound/soc/codecs/rt5631.h +++ b/sound/soc/codecs/rt5631.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __RTCODEC5631_H__ #define __RTCODEC5631_H__ diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index a98647ac497c..5f24df4fae8e 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -55,6 +55,8 @@ MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override"); #define RT5645_HWEQ_NUM 57 +#define TIME_TO_POWER_MS 400 + static const struct regmap_range_cfg rt5645_ranges[] = { { .name = "PR", @@ -432,6 +434,7 @@ struct rt5645_priv { int jack_type; bool en_button_func; bool hp_on; + int v_id; }; static int rt5645_reset(struct snd_soc_codec *codec) @@ -2516,9 +2519,7 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "SPKVOL L", "Switch", "SPK MIXL" }, { "SPKVOL R", "Switch", "SPK MIXR" }, - { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, { "SPOL MIX", "DAC L1 Switch", "DAC L1" }, - { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" }, { "SPOR MIX", "DAC R1 Switch", "DAC R1" }, { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" }, @@ -2707,6 +2708,11 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = { { "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" }, }; +static const struct snd_soc_dapm_route rt5645_old_dapm_routes[] = { + { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, +}; + static int rt5645_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -3363,6 +3369,11 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, rt5645_specific_dapm_routes, ARRAY_SIZE(rt5645_specific_dapm_routes)); + if (rt5645->v_id < 3) { + snd_soc_dapm_add_routes(dapm, + rt5645_old_dapm_routes, + ARRAY_SIZE(rt5645_old_dapm_routes)); + } break; case CODEC_TYPE_RT5650: snd_soc_dapm_new_controls(dapm, @@ -3637,14 +3648,14 @@ static const struct dmi_system_id dmi_platform_gpd_win[] = { {} }; -static struct rt5645_platform_data general_platform_data2 = { +static const struct rt5645_platform_data general_platform_data2 = { .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, .dmic2_data_pin = RT5645_DMIC2_DISABLE, .jd_mode = 3, .inv_jd1_1 = true, }; -static struct dmi_system_id dmi_platform_asus_t100ha[] = { +static const struct dmi_system_id dmi_platform_asus_t100ha[] = { { .ident = "ASUS T100HAN", .matches = { @@ -3655,11 +3666,11 @@ static struct dmi_system_id dmi_platform_asus_t100ha[] = { { } }; -static struct rt5645_platform_data minix_z83_4_platform_data = { +static const struct rt5645_platform_data minix_z83_4_platform_data = { .jd_mode = 3, }; -static struct dmi_system_id dmi_platform_minix_z83_4[] = { +static const struct dmi_system_id dmi_platform_minix_z83_4[] = { { .ident = "MINIX Z83-4", .matches = { @@ -3775,6 +3786,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret); return ret; } + + /* + * Read after 400msec, as it is the interval required between + * read and power On. + */ + msleep(TIME_TO_POWER_MS); regmap_read(regmap, RT5645_VENDOR_ID2, &val); switch (val) { @@ -3803,6 +3820,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_write(rt5645->regmap, RT5645_RESET, 0); + regmap_read(regmap, RT5645_VENDOR_ID, &val); + rt5645->v_id = val & 0xff; + ret = regmap_register_patch(rt5645->regmap, init_list, ARRAY_SIZE(init_list)); if (ret != 0) diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index da60b28ba3df..831b297978a4 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -26,10 +27,15 @@ #include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <sound/jack.h> #include "rl6231.h" #include "rt5651.h" +#define RT5651_JD_MAP(quirk) ((quirk) & GENMASK(7, 0)) +#define RT5651_IN2_DIFF BIT(16) +#define RT5651_DMIC_EN BIT(17) + #define RT5651_DEVICE_ID_VALUE 0x6281 #define RT5651_PR_RANGE_BASE (0xff + 1) @@ -37,6 +43,8 @@ #define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING)) +static unsigned long rt5651_quirk; + static const struct regmap_range_cfg rt5651_ranges[] = { { .name = "PR", .range_min = RT5651_PR_BASE, .range_max = RT5651_PR_BASE + 0xb4, @@ -880,11 +888,14 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2, RT5651_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ + SND_SOC_DAPM_SUPPLY("JD Power", RT5651_PWR_ANLG2, + RT5651_PWM_JD_M_BIT, 0, NULL, 0), + /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1, RT5651_PWR_LDO_BIT, 0, NULL, 0), - SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2, - RT5651_PWR_MB1_BIT, 0), + SND_SOC_DAPM_SUPPLY("micbias1", RT5651_PWR_ANLG2, + RT5651_PWR_MB1_BIT, 0, NULL, 0), /* Input Lines */ SND_SOC_DAPM_INPUT("MIC1"), SND_SOC_DAPM_INPUT("MIC2"), @@ -1528,6 +1539,8 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5651_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { @@ -1556,8 +1569,13 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000); snd_soc_write(codec, RT5651_PWR_VOL, 0x0000); snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + if (rt5651->pdata.jd_src) { + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0204); + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0002); + } else { + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + } break; default: @@ -1570,6 +1588,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, static int rt5651_probe(struct snd_soc_codec *codec) { struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); rt5651->codec = codec; @@ -1585,6 +1604,15 @@ static int rt5651_probe(struct snd_soc_codec *codec) snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + if (rt5651->pdata.jd_src) { + snd_soc_dapm_force_enable_pin(dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt5651->regmap, RT5651_MICBIAS, + 0x38, 0x38); + } + return 0; } @@ -1718,15 +1746,130 @@ static const struct i2c_device_id rt5651_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); +static int rt5651_quirk_cb(const struct dmi_system_id *id) +{ + rt5651_quirk = (unsigned long) id->driver_data; + return 1; +} + +static const struct dmi_system_id rt5651_quirk_table[] = { + { + .callback = rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), + DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), + }, + .driver_data = (unsigned long *) RT5651_JD1_1, + }, + {} +}; + static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np) { - rt5651->pdata.in2_diff = of_property_read_bool(np, - "realtek,in2-differential"); - rt5651->pdata.dmic_en = of_property_read_bool(np, - "realtek,dmic-en"); + if (of_property_read_bool(np, "realtek,in2-differential")) + rt5651_quirk |= RT5651_IN2_DIFF; + if (of_property_read_bool(np, "realtek,dmic-en")) + rt5651_quirk |= RT5651_DMIC_EN; + + return 0; +} + +static void rt5651_set_pdata(struct rt5651_priv *rt5651) +{ + if (rt5651_quirk & RT5651_IN2_DIFF) + rt5651->pdata.in2_diff = true; + if (rt5651_quirk & RT5651_DMIC_EN) + rt5651->pdata.dmic_en = true; + if (RT5651_JD_MAP(rt5651_quirk)) + rt5651->pdata.jd_src = RT5651_JD_MAP(rt5651_quirk); +} + +static irqreturn_t rt5651_irq(int irq, void *data) +{ + struct rt5651_priv *rt5651 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5651->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static int rt5651_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int jack_type; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK | + RT5651_MIC1_OVTH_MASK | + RT5651_PWR_CLK12M_MASK | + RT5651_PWR_MB_MASK, + RT5651_MIC1_OVCD_EN | + RT5651_MIC1_OVTH_600UA | + RT5651_PWR_MB_PU | + RT5651_PWR_CLK12M_PU); + msleep(100); + if (snd_soc_read(codec, RT5651_IRQ_CTRL2) & RT5651_MB1_OC_CLR) + jack_type = SND_JACK_HEADPHONE; + else + jack_type = SND_JACK_HEADSET; + snd_soc_update_bits(codec, RT5651_IRQ_CTRL2, + RT5651_MB1_OC_CLR, 0); + } else { /* jack out */ + jack_type = 0; + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK, + RT5651_MIC1_OVCD_DIS); + } + + return jack_type; +} + +static void rt5651_jack_detect_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, jack_detect_work.work); + + int report, val = 0; + + if (!rt5651->codec) + return; + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x1000; + break; + case RT5651_JD1_2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x2000; + break; + case RT5651_JD2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x4000; + break; + default: + break; + } + + report = rt5651_jack_detect(rt5651->codec, !val); + + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); +} + +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + rt5651->hp_jack = hp_jack; + rt5651_irq(0, rt5651); return 0; } +EXPORT_SYMBOL_GPL(rt5651_set_jack_detect); static int rt5651_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -1746,6 +1889,10 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->pdata = *pdata; else if (i2c->dev.of_node) rt5651_parse_dt(rt5651, i2c->dev.of_node); + else + dmi_check_system(rt5651_quirk_table); + + rt5651_set_pdata(rt5651); rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap); if (IS_ERR(rt5651->regmap)) { @@ -1779,6 +1926,59 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->hp_mute = 1; + if (rt5651->pdata.jd_src) { + + /* IRQ output on GPIO1 */ + regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1, + RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_1); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_1_IRQ_EN, + RT5651_JD1_1_IRQ_EN); + break; + case RT5651_JD1_2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_2_IRQ_EN, + RT5651_JD1_2_IRQ_EN); + break; + case RT5651_JD2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD2_IRQ_EN, + RT5651_JD2_IRQ_EN); + break; + case RT5651_JD_NULL: + break; + default: + dev_warn(&i2c->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); + break; + } + } + + INIT_DELAYED_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5651_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "rt5651", rt5651); + if (ret) { + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651, rt5651_dai, ARRAY_SIZE(rt5651_dai)); @@ -1787,6 +1987,9 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, static int rt5651_i2c_remove(struct i2c_client *i2c) { + struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c); + + cancel_delayed_work_sync(&rt5651->jack_detect_work); snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index 1bd33cfa6411..4f8b202121d7 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2062,6 +2062,8 @@ struct rt5651_priv { struct snd_soc_codec *codec; struct rt5651_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *hp_jack; + struct delayed_work jack_detect_work; int sysclk; int sysclk_src; @@ -2077,4 +2079,6 @@ struct rt5651_priv { bool hp_mute; }; +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack); #endif /* __RT5651_H__ */ diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 71216db15eab..07e7757417bc 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -2744,7 +2744,8 @@ static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5659_hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), - SND_SOC_DAPM_PGA("LOUT Amp", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5659_PWR_ANLG_1, RT5659_PWR_LM_BIT, + 0, NULL, 0), SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, rt5659_charge_pump_event, SND_SOC_DAPM_PRE_PMU | @@ -3208,6 +3209,7 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = { { "LOUT R MIX", "OUTVOL R Switch", "OUTVOL R" }, { "LOUT Amp", NULL, "LOUT L MIX" }, { "LOUT Amp", NULL, "LOUT R MIX" }, + { "LOUT Amp", NULL, "Charge Pump" }, { "LOUT Amp", NULL, "SYS CLK DET" }, { "LOUT L Playback", "Switch", "LOUT Amp" }, { "LOUT R Playback", "Switch", "LOUT Amp" }, @@ -3383,10 +3385,9 @@ static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) +static int rt5659_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; @@ -3412,20 +3413,21 @@ static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai, rt5659->sysclk = freq; rt5659->sysclk_src = clk_id; - dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); return 0; } -static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, - unsigned int freq_in, unsigned int freq_out) +static int rt5659_set_codec_pll(struct snd_soc_codec *codec, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) { - struct snd_soc_codec *codec = dai->codec; struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec); struct rl6231_pll_code pll_code; int ret; - if (Source == rt5659->pll_src && freq_in == rt5659->pll_in && + if (source == rt5659->pll_src && freq_in == rt5659->pll_in && freq_out == rt5659->pll_out) return 0; @@ -3439,7 +3441,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, return 0; } - switch (Source) { + switch (source) { case RT5659_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5659_GLB_CLK, RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_MCLK); @@ -3457,7 +3459,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK3); break; default: - dev_err(codec->dev, "Unknown PLL Source %d\n", Source); + dev_err(codec->dev, "Unknown PLL source %d\n", source); return -EINVAL; } @@ -3479,7 +3481,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, rt5659->pll_in = freq_in; rt5659->pll_out = freq_out; - rt5659->pll_src = Source; + rt5659->pll_src = source; return 0; } @@ -3664,9 +3666,7 @@ static int rt5659_resume(struct snd_soc_codec *codec) static const struct snd_soc_dai_ops rt5659_aif_dai_ops = { .hw_params = rt5659_hw_params, .set_fmt = rt5659_set_dai_fmt, - .set_sysclk = rt5659_set_dai_sysclk, .set_tdm_slot = rt5659_set_tdm_slot, - .set_pll = rt5659_set_dai_pll, .set_bclk_ratio = rt5659_set_bclk_ratio, }; @@ -3745,6 +3745,8 @@ static const struct snd_soc_codec_driver soc_codec_dev_rt5659 = { .dapm_routes = rt5659_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes), }, + .set_sysclk = rt5659_set_codec_sysclk, + .set_pll = rt5659_set_codec_pll, }; diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index ab9e0ebff5a7..b036c9dc0c8c 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -38,13 +38,24 @@ enum { CODEC_VER_0, }; +struct impedance_mapping_table { + unsigned int imp_min; + unsigned int imp_max; + unsigned int vol; + unsigned int dc_offset_l_manual; + unsigned int dc_offset_r_manual; + unsigned int dc_offset_l_manual_mic; + unsigned int dc_offset_r_manual_mic; +}; + struct rt5663_priv { struct snd_soc_codec *codec; struct rt5663_platform_data pdata; struct regmap *regmap; - struct delayed_work jack_detect_work; + struct delayed_work jack_detect_work, jd_unplug_work; struct snd_soc_jack *hs_jack; struct timer_list btn_check_timer; + struct impedance_mapping_table *imp_table; int codec_ver; int sysclk; @@ -1575,6 +1586,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) rt5663->jack_type = SND_JACK_HEADSET; rt5663_enable_push_button_irq(codec, true); + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual_mic) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual_mic >> @@ -1596,6 +1610,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) default: rt5663->jack_type = SND_JACK_HEADPHONE; + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual >> 16); @@ -1623,6 +1640,177 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) return rt5663->jack_type; } +static int rt5663_impedance_sensing(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80; + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (rt5663->imp_table[i].vol == 7) + break; + } + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + reg84 = snd_soc_read(codec, RT5663_ASRC_2); + reg26 = snd_soc_read(codec, RT5663_STO1_ADC_MIXER); + reg2fa = snd_soc_read(codec, RT5663_DUMMY_1); + reg91 = snd_soc_read(codec, RT5663_HP_CHARGE_PUMP_1); + reg10 = snd_soc_read(codec, RT5663_RECMIX); + reg80 = snd_soc_read(codec, RT5663_GLB_CLK); + + snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000, 0); + snd_soc_write(codec, RT5663_ASRC_2, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, 0x4040); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + usleep_range(10000, 10005); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + RT5663_SCLK_SRC_RCCLK); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_EN); + snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, 0x1232); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0x0005); + snd_soc_write(codec, RT5663_DEPOP_2, 0x3003); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0030); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0x0003); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1); + msleep(40); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2); + msleep(30); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_write(codec, RT5663_STO_DAC_MIXER, 0); + snd_soc_write(codec, RT5663_BYPASS_STO_DAC, 0x000c); + snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa); + snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x8088); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x3000); + snd_soc_write(codec, RT5663_ADDA_RST, 0xc000); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0x3320); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c9); + snd_soc_write(codec, RT5663_DUMMY_1, 0x004c); + snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7733); + snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_write(codec, RT5663_STO_DRE_9, 0x0007); + snd_soc_write(codec, RT5663_STO_DRE_10, 0x0007); + snd_soc_write(codec, RT5663_DUMMY_2, 0x02a4); + snd_soc_write(codec, RT5663_RECMIX, 0x0005); + snd_soc_write(codec, RT5663_HP_IMP_SEN_1, 0x4334); + snd_soc_update_bits(codec, RT5663_IRQ_3, 0x0004, 0x0004); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x2200); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x3000); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x6200); + + for (i = 0; i < 100; i++) { + msleep(20); + if (snd_soc_read(codec, RT5663_INT_ST_1) & 0x2) + break; + } + + value = snd_soc_read(codec, RT5663_HP_IMP_SEN_4); + + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0); + snd_soc_write(codec, RT5663_INT_ST_1, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_DIS); + snd_soc_write(codec, RT5663_GLB_CLK, reg80); + snd_soc_write(codec, RT5663_RECMIX, reg10); + snd_soc_write(codec, RT5663_DUMMY_2, 0x00a4); + snd_soc_write(codec, RT5663_DUMMY_1, reg2fa); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c8); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0xb320); + snd_soc_write(codec, RT5663_ADDA_RST, 0xe400); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x2000); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x0008); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, reg91); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, reg26); + snd_soc_write(codec, RT5663_ASRC_2, reg84); + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (value >= rt5663->imp_table[i].imp_min && + value <= rt5663->imp_table[i].imp_max) + break; + } + + snd_soc_update_bits(codec, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + snd_soc_update_bits(codec, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + return 0; +} + static int rt5663_button_detect(struct snd_soc_codec *codec) { int btn_type, val; @@ -1639,7 +1827,8 @@ static irqreturn_t rt5663_irq(int irq, void *data) { struct rt5663_priv *rt5663 = data; - dev_dbg(rt5663->codec->dev, "%s IRQ queue work\n", __func__); + dev_dbg(regmap_get_device(rt5663->regmap), "%s IRQ queue work\n", + __func__); queue_delayed_work(system_wq, &rt5663->jack_detect_work, msecs_to_jiffies(250)); @@ -1701,6 +1890,8 @@ static void rt5663_jack_detect_work(struct work_struct *work) break; case CODEC_VER_0: report = rt5663_jack_detect(rt5663->codec, 1); + if (rt5663->pdata.impedance_sensing_num) + rt5663_impedance_sensing(rt5663->codec); break; default: dev_err(codec->dev, "Unknown CODEC Version\n"); @@ -1750,8 +1941,15 @@ static void rt5663_jack_detect_work(struct work_struct *work) break; } /* button release or spurious interrput*/ - if (btn_type == 0) + if (btn_type == 0) { report = rt5663->jack_type; + cancel_delayed_work_sync( + &rt5663->jd_unplug_work); + } else { + queue_delayed_work(system_wq, + &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } } } else { /* jack out */ @@ -1772,6 +1970,37 @@ static void rt5663_jack_detect_work(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3); } +static void rt5663_jd_unplug_work(struct work_struct *work) +{ + struct rt5663_priv *rt5663 = + container_of(work, struct rt5663_priv, jd_unplug_work.work); + struct snd_soc_codec *codec = rt5663->codec; + + if (!codec) + return; + + if (!rt5663_check_jd_status(codec)) { + /* jack out */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + rt5663_v2_jack_detect(rt5663->codec, 0); + break; + case CODEC_VER_0: + rt5663_jack_detect(rt5663->codec, 0); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + + snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + queue_delayed_work(system_wq, &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } +} + static const struct snd_kcontrol_new rt5663_snd_controls[] = { /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL, @@ -1796,10 +2025,6 @@ static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = { }; static const struct snd_kcontrol_new rt5663_specific_controls[] = { - /* Headphone Output Volume */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, - RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, - rt5663_hp_vol_tlv), /* Mic Boost Volume*/ SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2, RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv), @@ -1807,6 +2032,13 @@ static const struct snd_kcontrol_new rt5663_specific_controls[] = { SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum), }; +static const struct snd_kcontrol_new rt5663_hpvol_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, + RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, + rt5663_hp_vol_tlv), +}; + static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_widget *sink) { @@ -2889,6 +3121,10 @@ static int rt5663_probe(struct snd_soc_codec *codec) ARRAY_SIZE(rt5663_specific_dapm_routes)); snd_soc_add_codec_controls(codec, rt5663_specific_controls, ARRAY_SIZE(rt5663_specific_controls)); + + if (!rt5663->imp_table) + snd_soc_add_codec_controls(codec, rt5663_hpvol_controls, + ARRAY_SIZE(rt5663_hpvol_controls)); break; } @@ -3177,6 +3413,8 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) { + int table_size; + device_property_read_u32(dev, "realtek,dc_offset_l_manual", &rt5663->pdata.dc_offset_l_manual); device_property_read_u32(dev, "realtek,dc_offset_r_manual", @@ -3185,6 +3423,17 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) &rt5663->pdata.dc_offset_l_manual_mic); device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic", &rt5663->pdata.dc_offset_r_manual_mic); + device_property_read_u32(dev, "realtek,impedance_sensing_num", + &rt5663->pdata.impedance_sensing_num); + + if (rt5663->pdata.impedance_sensing_num) { + table_size = sizeof(struct impedance_mapping_table) * + rt5663->pdata.impedance_sensing_num; + rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); + device_property_read_u32_array(dev, + "realtek,impedance_sensing_table", + (u32 *)rt5663->imp_table, table_size); + } return 0; } @@ -3218,7 +3467,16 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret); return ret; } - regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + + ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + if (ret || (val != RT5663_DEVICE_ID_2 && val != RT5663_DEVICE_ID_1)) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5663, retry one time.\n", + val); + msleep(100); + regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + } + switch (val) { case RT5663_DEVICE_ID_2: rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap); @@ -3337,6 +3595,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, } INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work); + INIT_DELAYED_WORK(&rt5663->jd_unplug_work, rt5663_jd_unplug_work); if (i2c->irq) { ret = request_irq(i2c->irq, rt5663_irq, diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 9545764ef3eb..c5094b4399e2 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -34,6 +34,24 @@ #include "rt5670.h" #include "rt5670-dsp.h" +#define RT5670_DEV_GPIO BIT(0) +#define RT5670_IN2_DIFF BIT(1) +#define RT5670_DMIC_EN BIT(2) +#define RT5670_DMIC1_IN2P BIT(3) +#define RT5670_DMIC1_GPIO6 BIT(4) +#define RT5670_DMIC1_GPIO7 BIT(5) +#define RT5670_DMIC2_INR BIT(6) +#define RT5670_DMIC2_GPIO8 BIT(7) +#define RT5670_DMIC3_GPIO5 BIT(8) +#define RT5670_JD_MODE1 BIT(9) +#define RT5670_JD_MODE2 BIT(10) +#define RT5670_JD_MODE3 BIT(11) + +static unsigned long rt5670_quirk; +static unsigned int quirk_override; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + #define RT5670_DEVICE_ID 0x6271 #define RT5670_PR_RANGE_BASE (0xff + 1) @@ -2582,6 +2600,24 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, return 0; } +static int rt5670_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + + dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio); + if (dai->id != RT5670_AIF1) + return 0; + + if ((ratio % 50) == 0) + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_50FS); + else + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_NOR); + + return 0; +} + static int rt5670_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -2712,6 +2748,7 @@ static const struct snd_soc_dai_ops rt5670_aif_dai_ops = { .set_fmt = rt5670_set_dai_fmt, .set_tdm_slot = rt5670_set_tdm_slot, .set_pll = rt5670_set_dai_pll, + .set_bclk_ratio = rt5670_set_bclk_ratio, }; static struct snd_soc_dai_driver rt5670_dai[] = { @@ -2808,56 +2845,84 @@ static const struct acpi_device_id rt5670_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); #endif -static const struct dmi_system_id dmi_platform_intel_braswell[] = { +static int rt5670_quirk_cb(const struct dmi_system_id *id) +{ + rt5670_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id dmi_platform_intel_quirks[] = { { + .callback = rt5670_quirk_cb, .ident = "Intel Braswell", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Dell Wyse 3040", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, - {} -}; - -static const struct dmi_system_id dmi_platform_intel_bytcht_jdmode2[] = { { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE2), }, - {} -}; - -static const struct dmi_system_id dmi_platform_intel_bytcht_jdmode3[] = { { + .callback = rt5670_quirk_cb, .ident = "Dell Venue 8 Pro 5855", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5855"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_DEV_GPIO | + RT5670_JD_MODE3), }, {} }; @@ -2881,21 +2946,61 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (pdata) rt5670->pdata = *pdata; - if (dmi_check_system(dmi_platform_intel_braswell)) { - rt5670->pdata.dmic_en = true; - rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + dmi_check_system(dmi_platform_intel_quirks); + if (quirk_override) { + dev_info(&i2c->dev, "Overriding quirk 0x%x => 0x%x\n", + (unsigned int)rt5670_quirk, quirk_override); + rt5670_quirk = quirk_override; + } + + if (rt5670_quirk & RT5670_DEV_GPIO) { rt5670->pdata.dev_gpio = true; - rt5670->pdata.jd_mode = 1; - } else if (dmi_check_system(dmi_platform_intel_bytcht_jdmode2)) { + dev_info(&i2c->dev, "quirk dev_gpio\n"); + } + if (rt5670_quirk & RT5670_IN2_DIFF) { + rt5670->pdata.in2_diff = true; + dev_info(&i2c->dev, "quirk IN2_DIFF\n"); + } + if (rt5670_quirk & RT5670_DMIC_EN) { rt5670->pdata.dmic_en = true; + dev_info(&i2c->dev, "quirk DMIC enabled\n"); + } + if (rt5670_quirk & RT5670_DMIC1_IN2P) { rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; - rt5670->pdata.dev_gpio = true; + dev_info(&i2c->dev, "quirk DMIC1 on IN2P pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO6) { + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO6; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO6 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO7) { + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO7; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO7 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_INR) { + rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_IN3N; + dev_info(&i2c->dev, "quirk DMIC2 on INR pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_GPIO8) { + rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_GPIO8; + dev_info(&i2c->dev, "quirk DMIC2 on GPIO8 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC3_GPIO5) { + rt5670->pdata.dmic3_data_pin = RT5670_DMIC_DATA_GPIO5; + dev_info(&i2c->dev, "quirk DMIC3 on GPIO5 pin\n"); + } + + if (rt5670_quirk & RT5670_JD_MODE1) { + rt5670->pdata.jd_mode = 1; + dev_info(&i2c->dev, "quirk JD mode 1\n"); + } + if (rt5670_quirk & RT5670_JD_MODE2) { rt5670->pdata.jd_mode = 2; - } else if (dmi_check_system(dmi_platform_intel_bytcht_jdmode3)) { - rt5670->pdata.dmic_en = true; - rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; - rt5670->pdata.dev_gpio = true; + dev_info(&i2c->dev, "quirk JD mode 2\n"); + } + if (rt5670_quirk & RT5670_JD_MODE3) { rt5670->pdata.jd_mode = 3; + dev_info(&i2c->dev, "quirk JD mode 3\n"); } rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 5ba485cae4e6..265df80d504e 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1816,6 +1816,10 @@ #define RT5670_ZCD_HP_DIS (0x0 << 15) #define RT5670_ZCD_HP_EN (0x1 << 15) +/* General Control 3 (0xfc) */ +#define RT5670_TDM_DATA_MODE_SEL (0x1 << 11) +#define RT5670_TDM_DATA_MODE_NOR (0x0 << 11) +#define RT5670_TDM_DATA_MODE_50FS (0x1 << 11) /* Codec Private Register definition */ /* 3D Speaker Control (0x63) */ diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 810369f687d7..a09499977be4 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -697,7 +697,8 @@ static int tas571x_i2c_probe(struct i2c_client *client, return PTR_ERR(priv->mclk); } - BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); + if (WARN_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES)) + return -EINVAL; for (i = 0; i < priv->chip->num_supply_names; i++) priv->supplies[i].supply = priv->chip->supply_names[i]; diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 95e0a7abeb7a..f8dd67ca0744 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -312,9 +312,15 @@ static const struct i2c_device_id tfa9879_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); +static const struct of_device_id tfa9879_of_match[] = { + { .compatible = "nxp,tfa9879", }, + { } +}; + static struct i2c_driver tfa9879_i2c_driver = { .driver = { .name = "tfa9879", + .of_match_table = tfa9879_of_match, }, .probe = tfa9879_i2c_probe, .remove = tfa9879_i2c_remove, diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 3d42138a7974..74909211c608 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -454,6 +454,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, break; case SND_SOC_DAIFMT_DSP_A: iface_reg |= TLV320AIC23_LRP_ON; + /* fall through */ case SND_SOC_DAIFMT_DSP_B: iface_reg |= TLV320AIC23_FOR_DSP; break; diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h index 629b85e75409..1f2879b7a080 100644 --- a/sound/soc/codecs/tlv320aic26.h +++ b/sound/soc/codecs/tlv320aic26.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Texas Instruments TLV320AIC26 low power audio CODEC * register definitions diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 54a87a905eb6..e2862372c26e 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -929,7 +929,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_I2S: break; case SND_SOC_DAIFMT_DSP_A: - dsp_a_val = 0x1; + dsp_a_val = 0x1; /* fall through */ case SND_SOC_DAIFMT_DSP_B: /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 2e014c80d113..616cd4bebd01 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -274,6 +274,7 @@ static int tpa6130a2_probe(struct i2c_client *client, default: dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", data->id); + /* fall through */ case TPA6130A2: regulator = "Vdd"; break; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 43568435c208..738e04b09116 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/regmap.h> +#include <linux/acpi.h> #include <sound/core.h> #include <sound/jack.h> @@ -374,11 +375,20 @@ static const struct of_device_id ts3a227e_of_match[] = { }; MODULE_DEVICE_TABLE(of, ts3a227e_of_match); +#ifdef CONFIG_ACPI +static struct acpi_device_id ts3a227e_acpi_match[] = { + { "104C227E", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ts3a227e_acpi_match); +#endif + static struct i2c_driver ts3a227e_driver = { .driver = { .name = "ts3a227e", .pm = &ts3a227e_pm, .of_match_table = of_match_ptr(ts3a227e_of_match), + .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, .probe = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h index e41ab38c6f69..664618c2571c 100644 --- a/sound/soc/codecs/uda134x.h +++ b/sound/soc/codecs/uda134x.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _UDA134X_CODEC_H #define _UDA134X_CODEC_H diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 72486bf072f2..4f0481d3c7a7 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_gpio(codec); - arizona_init_notifiers(codec); snd_soc_component_disable_pin(component, "HAPTICS"); @@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5102); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + mutex_init(&arizona->dac_comp_lock); wm5102->core.arizona = arizona; @@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 858a24fc28e8..6ed1e1f9ce51 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - arizona_init_notifiers(codec); for (i = 0; i < WM5110_NUM_ADSP; ++i) { ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); @@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5110); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm5110->core.arizona = arizona; wm5110->core.num_inputs = 8; @@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index b8c1940f2243..a394dbee77aa 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -196,7 +196,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); - u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; + unsigned int iface; int i; /* The set of sample rates that can be supported depends on the @@ -223,15 +223,16 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, /* bit size */ switch (params_width(params)) { case 16: + iface = 0x0; break; case 20: - iface |= 0x0001; + iface = 0x1; break; case 24: - iface |= 0x0002; + iface = 0x2; break; case 32: - iface |= 0x0003; + iface = 0x3; break; default: dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d", @@ -242,7 +243,9 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", params_width(params), params_rate(params)); - snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); + snd_soc_update_bits(codec, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK, + iface); + return 0; } @@ -295,7 +298,7 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3; + unsigned int iface; /* check master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -308,18 +311,19 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - iface |= 0x0008; + iface = 0x08; break; case SND_SOC_DAIFMT_RIGHT_J: + iface = 0x00; break; case SND_SOC_DAIFMT_LEFT_J: - iface |= 0x0004; + iface = 0x04; break; case SND_SOC_DAIFMT_DSP_A: - iface |= 0x000C; + iface = 0x0C; break; case SND_SOC_DAIFMT_DSP_B: - iface |= 0x001C; + iface = 0x1C; break; default: return -EINVAL; @@ -329,14 +333,14 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; - case SND_SOC_DAIFMT_IB_IF: - iface |= 0x0010; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x10; break; case SND_SOC_DAIFMT_IB_NF: - iface |= 0x0020; + iface |= 0x20; break; - case SND_SOC_DAIFMT_NB_IF: - iface |= 0x0030; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x30; break; default: return -EINVAL; @@ -347,7 +351,10 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, fmt & SND_SOC_DAIFMT_FORMAT_MASK, ((fmt & SND_SOC_DAIFMT_INV_MASK))); - snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); + snd_soc_update_bits(codec, WM8741_FORMAT_CONTROL, + WM8741_BCP_MASK | WM8741_LRP_MASK | WM8741_FMT_MASK, + iface); + return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index d05d76e79c70..0271a5253bd3 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -971,7 +971,7 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec, case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: - ioctl |= 0x2; + ioctl |= 0x2; /* fall through */ case SND_SOC_DAIFMT_CBM_CFS: voice |= 0x0040; break; @@ -1096,7 +1096,7 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec, case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: - ioctl |= 0x1; + ioctl |= 0x1; /* fall through */ case SND_SOC_DAIFMT_CBM_CFS: hifi |= 0x0040; break; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 195f7bf6eb22..830ffd80de4a 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1076,6 +1076,7 @@ static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai, switch (clk_id) { case WM8993_SYSCLK_MCLK: wm8993->mclk_rate = freq; + /* fall through */ case WM8993_SYSCLK_FLL: wm8993->sysclk_source = clk_id; break; @@ -1123,6 +1124,7 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8993_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h index 4478b40c86e3..91811aa158d8 100644 --- a/sound/soc/codecs/wm8993.h +++ b/sound/soc/codecs/wm8993.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef WM8993_H #define WM8993_H diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 3896523b71e9..f91b49e1ece3 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -860,6 +860,7 @@ static void vmid_reference(struct snd_soc_codec *codec) switch (wm8994->vmid_mode) { default: WARN_ON(NULL == "Invalid VMID mode"); + /* fall through */ case WM8994_VMID_NORMAL: /* Startup bias, VMID ramp & buffer */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2, @@ -2654,6 +2655,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8994_AIF1_LRCLK_INV; lrclk |= WM8958_AIF1_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 49401a8aae64..77f512767273 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec) if (ret < 0) return ret; - arizona_init_notifiers(codec); - snd_soc_component_disable_pin(component, "HAPTICS"); priv->core.arizona->dapm = dapm; @@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8997); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm8997->core.arizona = arizona; wm8997->core.num_inputs = 4; @@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + return ret; ret = arizona_init_spk_irqs(arizona); if (ret < 0) return ret; diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 44f447136e22..2d211dbe7422 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -101,7 +101,7 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, return 0; } -static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, +static int wm8998_inmux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); @@ -109,84 +109,38 @@ static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = wm8998->core.arizona; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux, inmode; - unsigned int mode_val, src_val; + unsigned int mode_reg, mode_index; + unsigned int mux, inmode, src_val, mode_val; mux = ucontrol->value.enumerated.item[0]; if (mux > 1) return -EINVAL; - /* L and R registers have same shift and mask */ - inmode = arizona->pdata.inmode[2 * mux]; - src_val = mux << ARIZONA_IN1L_SRC_SHIFT; - if (inmode & ARIZONA_INMODE_SE) - src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; - - switch (arizona->pdata.inmode[0]) { - case ARIZONA_INMODE_DMIC: - if (mux) - mode_val = 0; /* B always analogue */ - else - mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; - - snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL, - ARIZONA_IN1_MODE_MASK, mode_val); - - /* IN1A is digital so L and R must change together */ - /* src_val setting same for both registers */ - snd_soc_update_bits(codec, - ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_SRC_MASK | - ARIZONA_IN1L_SRC_SE_MASK, src_val); - snd_soc_update_bits(codec, - ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_SRC_MASK | - ARIZONA_IN1R_SRC_SE_MASK, src_val); + switch (e->reg) { + case ARIZONA_ADC_DIGITAL_VOLUME_2L: + mode_reg = ARIZONA_IN2L_CONTROL; + mode_index = 1 + (2 * mux); break; default: - /* both analogue */ - snd_soc_update_bits(codec, - e->reg, - ARIZONA_IN1L_SRC_MASK | - ARIZONA_IN1L_SRC_SE_MASK, - src_val); + mode_reg = ARIZONA_IN1L_CONTROL; + mode_index = (2 * mux); break; } - return snd_soc_dapm_mux_update_power(dapm, kcontrol, - ucontrol->value.enumerated.item[0], - e, NULL); -} - -static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); - struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = wm8998->core.arizona; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux, inmode, src_val, mode_val; - - mux = ucontrol->value.enumerated.item[0]; - if (mux > 1) - return -EINVAL; - - inmode = arizona->pdata.inmode[1 + (2 * mux)]; + inmode = arizona->pdata.inmode[mode_index]; if (inmode & ARIZONA_INMODE_DMIC) - mode_val = 1 << ARIZONA_IN2_MODE_SHIFT; + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; else mode_val = 0; - src_val = mux << ARIZONA_IN2L_SRC_SHIFT; + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; if (inmode & ARIZONA_INMODE_SE) - src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT; + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; - snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL, - ARIZONA_IN2_MODE_MASK, mode_val); + snd_soc_update_bits(codec, mode_reg, ARIZONA_IN1_MODE_MASK, mode_val); - snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK, + snd_soc_update_bits(codec, e->reg, + ARIZONA_IN1L_SRC_MASK | ARIZONA_IN1L_SRC_SE_MASK, src_val); return snd_soc_dapm_mux_update_power(dapm, kcontrol, @@ -216,14 +170,14 @@ static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, static const struct snd_kcontrol_new wm8998_in1mux[2] = { SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, - snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + snd_soc_dapm_get_enum_double, wm8998_inmux_put), SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, - snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + snd_soc_dapm_get_enum_double, wm8998_inmux_put), }; static const struct snd_kcontrol_new wm8998_in2mux = SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, - snd_soc_dapm_get_enum_double, wm8998_in2mux_put); + snd_soc_dapm_get_enum_double, wm8998_inmux_put); static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); @@ -1330,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_gpio(codec); - arizona_init_notifiers(codec); snd_soc_component_disable_pin(component, "HAPTICS"); @@ -1399,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8998); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm8998->core.arizona = arizona; wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ @@ -1423,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + arizona_init_common(arizona); + ret = arizona_init_spk_irqs(arizona); if (ret < 0) return ret; diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h index 7ecffc563016..f0800dcca9b8 100644 --- a/sound/soc/codecs/wm9713.h +++ b/sound/soc/codecs/wm9713.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * wm9713.h -- WM9713 Soc Audio driver */ diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index f883933c1a19..23c6592eb31a 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # DAVINCI Platform Support snd-soc-edma-objs := edma-pcm.o snd-soc-davinci-i2s-objs := davinci-i2s.o diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index f395bbc7c354..804c6f2bcf21 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1721,7 +1721,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) PTR_ERR(chan)); return PTR_ERR(chan); } - BUG_ON(!chan->device || !chan->device->dev); + if (WARN_ON(!chan->device || !chan->device->dev)) + return -EINVAL; if (chan->device->dev->of_node) ret = of_property_read_string(chan->device->dev->of_node, @@ -1867,6 +1868,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_common_irq_handler, IRQF_ONESHOT | IRQF_SHARED, @@ -1884,6 +1889,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_rx_irq_handler, IRQF_ONESHOT, irq_name, mcasp); @@ -1899,6 +1908,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_tx_irq_handler, IRQF_ONESHOT, irq_name, mcasp); @@ -1982,8 +1995,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) GFP_KERNEL); if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || - !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) - return -ENOMEM; + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) { + ret = -ENOMEM; + goto err; + } ret = davinci_mcasp_set_ch_constraints(mcasp); if (ret) diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index c6fd95fa5ca6..aa0c6ec4d93c 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -4,8 +4,8 @@ config SND_DESIGNWARE_I2S select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for - Synopsys desigwnware I2S device. The device supports upto - maximum of 8 channels each for play and record. + Synopsys designware I2S device. The device supports up to + a maximum of 8 channels each for play and record. config SND_DESIGNWARE_PCM bool "PCM PIO extension for I2S driver" diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index d28dc25c9375..c67bf1139e1e 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # MPC8610 HPCD Machine Support snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 2db4d0c80d33..1225e0399de8 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -166,7 +166,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], cpu_priv->sysclk_freq[tx], cpu_priv->sysclk_dir[tx]); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk for cpu dai\n"); return ret; } @@ -174,7 +174,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, if (cpu_priv->slot_width) { ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, cpu_priv->slot_width); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set TDM slot for cpu dai\n"); return ret; } @@ -270,7 +270,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id, pll_out, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set SYSCLK: %d\n", ret); return ret; } @@ -283,7 +283,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to switch away from FLL: %d\n", ret); return ret; } @@ -459,7 +459,7 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk in %s\n", __func__); return ret; } @@ -639,6 +639,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); + if (!priv->dai_link[0].codec_name) { + ret = -ENOMEM; + goto asrc_fail; + } } priv->dai_link[0].platform_of_node = cpu_np; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 7e6cc4da0088..4f7469c1864c 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1110,7 +1110,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk); u64 rate_ideal, rate_actual, sub; u32 sysclk_dfmin, sysclk_dfmax; @@ -1169,7 +1169,7 @@ out: static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; struct platform_device *pdev = spdif_priv->pdev; struct device *dev = &pdev->dev; u64 savesub = 100000, ret; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 64598d1183f8..f2f51e06e22c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -197,12 +197,13 @@ struct fsl_ssi_soc_data { * @use_dma: DMA is used or FIQ with stream filter * @use_dual_fifo: DMA with support for both FIFOs used * @fifo_deph: Depth of the SSI FIFOs + * @slot_width: width of each DAI slot + * @slots: number of slots * @rxtx_reg_val: Specific register settings for receive/transmit configuration * * @clk: SSI clock * @baudclk: SSI baud clock for master mode * @baudclk_streams: Active streams that are using baudclk - * @bitclk_freq: bitclock frequency set by .set_dai_sysclk * * @dma_params_tx: DMA transmit parameters * @dma_params_rx: DMA receive parameters @@ -233,12 +234,13 @@ struct fsl_ssi_private { bool use_dual_fifo; bool has_ipg_clk_name; unsigned int fifo_depth; + unsigned int slot_width; + unsigned int slots; struct fsl_ssi_rxtx_reg_val rxtx_reg_val; struct clk *clk; struct clk *baudclk; unsigned int baudclk_streams; - unsigned int bitclk_freq; /* regcache for volatile regs */ u32 regcache_sfcsr; @@ -700,8 +702,8 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, * Note: This function can be only called when using SSI as DAI master * * Quick instruction for parameters: - * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels - * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. + * freq: Output BCLK frequency = samplerate * slots * slot_width + * (In 2-channel I2S Master mode, slot_width is fixed 32) */ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai, @@ -712,15 +714,21 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; unsigned long clkrate, baudrate, tmprate; + unsigned int slots = params_channels(hw_params); + unsigned int slot_width = 32; u64 sub, savesub = 100000; unsigned int freq; bool baudclk_is_used; - /* Prefer the explicitly set bitclock frequency */ - if (ssi_private->bitclk_freq) - freq = ssi_private->bitclk_freq; - else - freq = params_channels(hw_params) * 32 * params_rate(hw_params); + /* Override slots and slot_width if being specifically set... */ + if (ssi_private->slots) + slots = ssi_private->slots; + /* ...but keep 32 bits if slots is 2 -- I2S Master mode */ + if (ssi_private->slot_width && slots != 2) + slot_width = ssi_private->slot_width; + + /* Generate bit clock based on the slot number and slot width */ + freq = slots * slot_width * params_rate(hw_params); /* Don't apply it to any non-baudclk circumstance */ if (IS_ERR(ssi_private->baudclk)) @@ -805,16 +813,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, return 0; } -static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - - ssi_private->bitclk_freq = freq; - - return 0; -} - /** * fsl_ssi_hw_params - program the sample size * @@ -1095,6 +1093,12 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, struct regmap *regs = ssi_private->regs; u32 val; + /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ + if (slot_width & 1 || slot_width < 8 || slot_width > 24) { + dev_err(cpu_dai->dev, "invalid slot width: %d\n", slot_width); + return -EINVAL; + } + /* The slot number should be >= 2 if using Network mode or I2S mode */ regmap_read(regs, CCSR_SSI_SCR, &val); val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET; @@ -1121,6 +1125,9 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); + ssi_private->slot_width = slot_width; + ssi_private->slots = slots; + return 0; } @@ -1191,7 +1198,6 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .hw_params = fsl_ssi_hw_params, .hw_free = fsl_ssi_hw_free, .set_fmt = fsl_ssi_set_dai_fmt, - .set_sysclk = fsl_ssi_set_dai_sysclk, .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, .trigger = fsl_ssi_trigger, }; diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index 38a4209af7c6..f75b4d3aeacc 100644 --- a/sound/soc/fsl/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef __IMX_AUDMUX_H #define __IMX_AUDMUX_H diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h index dff253fde29a..d7ee33b5b9a8 100644 --- a/sound/soc/fsl/mpc5200_dma.h +++ b/sound/soc/fsl/mpc5200_dma.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Freescale MPC5200 Audio DMA driver */ diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 9e000523a3b4..9dec293a4c4d 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 488c52f9405f..1b6164249341 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -29,7 +29,9 @@ struct graph_card_data { struct graph_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; } *dai_props; + unsigned int mclk_fs; struct snd_soc_dai_link *dai_link; struct gpio_desc *pa_gpio; }; @@ -95,9 +97,43 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) asoc_simple_card_clk_disable(&dai_props->codec_dai); } +static int asoc_graph_card_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; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + unsigned int mclk, mclk_fs = 0; + int ret = 0; + + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; + } + return 0; +err: + return ret; +} + static const struct snd_soc_ops asoc_graph_card_ops = { .startup = asoc_graph_card_startup, .shutdown = asoc_graph_card_shutdown, + .hw_params = asoc_graph_card_hw_params, }; static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) @@ -146,10 +182,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, if (ret < 0) goto dai_link_of_err; - /* - * we need to consider "mclk-fs" around here - * see simple-card - */ + of_property_read_u32(rcpu_ep, "mclk-fs", &dai_props->mclk_fs); ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) @@ -217,10 +250,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - /* - * we need to consider "mclk-fs" around here - * see simple-card - */ + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); diff --git a/sound/soc/img/Makefile b/sound/soc/img/Makefile index 0508c1ced636..3e7b0fd4fcbf 100644 --- a/sound/soc/img/Makefile +++ b/sound/soc/img/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SND_SOC_IMG_I2S_IN) += img-i2s-in.o obj-$(CONFIG_SND_SOC_IMG_I2S_OUT) += img-i2s-out.o obj-$(CONFIG_SND_SOC_IMG_PARALLEL_OUT) += img-parallel-out.o diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 567f9767fb73..d7fbb0a0a28b 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <sound/core.h> @@ -60,8 +61,33 @@ struct img_i2s_in { void __iomem *channel_base; unsigned int active_channels; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; +static int img_i2s_in_runtime_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static int img_i2s_in_runtime_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg) { writel(val, i2s->base + reg); @@ -279,7 +305,7 @@ static int img_i2s_in_hw_params(struct snd_pcm_substream *substream, static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0; u32 reg; @@ -319,6 +345,10 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_disable(i2s, i); @@ -338,6 +368,8 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_enable(i2s, i); + pm_runtime_put(i2s->dev); + return 0; } @@ -427,9 +459,15 @@ static int img_i2s_in_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_sys); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; @@ -447,7 +485,7 @@ static int img_i2s_in_probe(struct platform_device *pdev) if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_suspend; } dev_dbg(dev, "No top level reset found\n"); @@ -469,42 +507,110 @@ static int img_i2s_in_probe(struct platform_device *pdev) IMG_I2S_IN_CH_CTL_JUST_MASK | IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL); + pm_runtime_put(&pdev->dev); + + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) { + ret = -ENOMEM; + goto err_suspend; + } + ret = devm_snd_soc_register_component(dev, &img_i2s_in_component, &i2s->dai_driver, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(i2s->clk_sys); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_i2s_in_dev_remove(struct platform_device *pdev) { - struct img_i2s_in *i2s = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_in_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + + img_i2s_in_runtime_suspend(dev); return 0; } +static int img_i2s_in_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + img_i2s_in_writel(i2s, i2s->suspend_ctl, IMG_I2S_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_i2s_in_of_match[] = { { .compatible = "img,i2s-in" }, {} }; MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); +static const struct dev_pm_ops img_i2s_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, + img_i2s_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume) +}; + static struct platform_driver img_i2s_in_driver = { .driver = { .name = "img-i2s-in", - .of_match_table = img_i2s_in_of_match + .of_match_table = img_i2s_in_of_match, + .pm = &img_i2s_in_pm_ops }, .probe = img_i2s_in_probe, .remove = img_i2s_in_dev_remove diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 78b7f6cd675b..30a95bcef2db 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -63,29 +63,36 @@ struct img_i2s_out { unsigned int active_channels; struct reset_control *rst; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; -static int img_i2s_out_suspend(struct device *dev) +static int img_i2s_out_runtime_suspend(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); - if (!i2s->force_clk_active) - clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_sys); return 0; } -static int img_i2s_out_resume(struct device *dev) +static int img_i2s_out_runtime_resume(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); int ret; - if (!i2s->force_clk_active) { - ret = clk_prepare_enable(i2s->clk_ref); - if (ret) { - dev_err(dev, "clk_enable failed: %d\n", ret); - return ret; - } + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(i2s->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(i2s->clk_sys); + return ret; } return 0; @@ -287,7 +294,7 @@ static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; bool force_clk_active; u32 chan_control_mask, control_mask, chan_control_set = 0; u32 reg, control_set = 0; @@ -342,6 +349,10 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + img_i2s_out_disable(i2s); reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); @@ -361,6 +372,7 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) img_i2s_out_ch_enable(i2s, i); img_i2s_out_enable(i2s); + pm_runtime_put(i2s->dev); i2s->force_clk_active = force_clk_active; @@ -467,9 +479,20 @@ static int img_i2s_out_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_ref); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_out_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); @@ -483,13 +506,7 @@ static int img_i2s_out_probe(struct platform_device *pdev) img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); img_i2s_out_reset(i2s); - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_i2s_out_resume(&pdev->dev); - if (ret) - goto err_pm_disable; - } + pm_runtime_put(&pdev->dev); i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; @@ -517,26 +534,70 @@ static int img_i2s_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); return ret; } static int img_i2s_out_dev_remove(struct platform_device *pdev) { - struct img_i2s_out *i2s = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_out_suspend(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + + img_i2s_out_runtime_suspend(dev); + + return 0; +} + +static int img_i2s_out_resume(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + } + + img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_out_runtime_suspend(dev); return 0; } +#endif static const struct of_device_id img_i2s_out_of_match[] = { { .compatible = "img,i2s-out" }, @@ -545,8 +606,9 @@ static const struct of_device_id img_i2s_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); static const struct dev_pm_ops img_i2s_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_i2s_out_suspend, - img_i2s_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, + img_i2s_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) }; static struct platform_driver img_i2s_out_driver = { diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index 23b0f0f6ec9c..acc005217be0 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -153,6 +153,7 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); u32 reg, control_set = 0; + int ret; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -164,9 +165,14 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + ret = pm_runtime_get_sync(prl->dev); + if (ret < 0) + return ret; + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + pm_runtime_put(prl->dev); return 0; } diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 8adfd65d4390..cedd40c8d1f3 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <sound/core.h> @@ -82,11 +83,36 @@ struct img_spdif_in { unsigned int single_freq; unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; bool active; + u32 suspend_clkgen; + u32 suspend_ctl; /* Write-only registers */ unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; }; +static int img_spdif_in_runtime_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static int img_spdif_in_runtime_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_spdif_in_writel(struct img_spdif_in *spdif, u32 val, u32 reg) { @@ -723,15 +749,21 @@ static int img_spdif_in_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_sys); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_spdif_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_pm_put; } dev_dbg(dev, "No top level reset found\n"); img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, @@ -759,42 +791,98 @@ static int img_spdif_in_probe(struct platform_device *pdev) IMG_SPDIF_IN_CTL_TRK_MASK; img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + pm_runtime_put(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &img_spdif_in_component, &img_spdif_in_dai, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(spdif->clk_sys); +err_pm_put: + pm_runtime_put(&pdev->dev); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_spdif_in_dev_remove(struct platform_device *pdev) { - struct img_spdif_in *spdif = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_in_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_clkgen = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CLKGEN); + spdif->suspend_ctl = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + + img_spdif_in_runtime_suspend(dev); return 0; } +static int img_spdif_in_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int i, ret; + + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) + img_spdif_in_aclkgen_writel(spdif, i); + + img_spdif_in_writel(spdif, spdif->suspend_clkgen, IMG_SPDIF_IN_CLKGEN); + img_spdif_in_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_spdif_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_spdif_in_of_match[] = { { .compatible = "img,spdif-in" }, {} }; MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); +static const struct dev_pm_ops img_spdif_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, + img_spdif_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume) +}; + static struct platform_driver img_spdif_in_driver = { .driver = { .name = "img-spdif-in", - .of_match_table = img_spdif_in_of_match + .of_match_table = img_spdif_in_of_match, + .pm = &img_spdif_in_pm_ops }, .probe = img_spdif_in_probe, .remove = img_spdif_in_dev_remove diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 383655da2e60..934ed3df2ebf 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -47,25 +47,36 @@ struct img_spdif_out { struct snd_dmaengine_dai_dma_data dma_data; struct device *dev; struct reset_control *rst; + u32 suspend_ctl; + u32 suspend_csl; + u32 suspend_csh; }; -static int img_spdif_out_suspend(struct device *dev) +static int img_spdif_out_runtime_suspend(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); clk_disable_unprepare(spdif->clk_ref); + clk_disable_unprepare(spdif->clk_sys); return 0; } -static int img_spdif_out_resume(struct device *dev) +static int img_spdif_out_runtime_resume(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(spdif->clk_ref); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(spdif->clk_sys); return ret; } @@ -355,21 +366,21 @@ static int img_spdif_out_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_ref); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; - - img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, - IMG_SPDIF_OUT_CTL); - - img_spdif_out_reset(spdif); - pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_spdif_out_resume(&pdev->dev); + ret = img_spdif_out_runtime_resume(&pdev->dev); if (ret) goto err_pm_disable; } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; + + img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, + IMG_SPDIF_OUT_CTL); + + img_spdif_out_reset(spdif); + pm_runtime_put(&pdev->dev); spin_lock_init(&spdif->lock); @@ -393,27 +404,62 @@ static int img_spdif_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); return ret; } static int img_spdif_out_dev_remove(struct platform_device *pdev) { - struct img_spdif_out *spdif = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_out_suspend(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + + img_spdif_out_runtime_suspend(dev); return 0; } +static int img_spdif_out_resume(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + + img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL); + img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL); + img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV); + + if (pm_runtime_status_suspended(dev)) + img_spdif_out_runtime_suspend(dev); + + return 0; +} +#endif static const struct of_device_id img_spdif_out_of_match[] = { { .compatible = "img,spdif-out" }, {} @@ -421,8 +467,9 @@ static const struct of_device_id img_spdif_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); static const struct dev_pm_ops img_spdif_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_spdif_out_suspend, - img_spdif_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, + img_spdif_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) }; static struct platform_driver img_spdif_out_driver = { diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3c7f554ec30..bb8be10b8437 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,19 +1,3 @@ -config SND_MFLD_MACHINE - tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC - select SND_SOC_SN95031 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_PCI - help - This adds support for ASoC machine driver for Intel(R) MID Medfield platform - used as alsa device in audio substem in Intel(R) MID devices - Say Y if you have such a device. - If unsure select "N". - -config SND_SST_ATOM_HIFI2_PLATFORM - tristate - select SND_SOC_COMPRESS - config SND_SST_IPC tristate @@ -27,10 +11,12 @@ config SND_SST_IPC_ACPI select SND_SOC_INTEL_SST select IOSF_MBI +config SND_SOC_INTEL_COMMON + tristate + config SND_SOC_INTEL_SST tristate select SND_SOC_INTEL_SST_ACPI if ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI config SND_SOC_INTEL_SST_FIRMWARE tristate @@ -39,280 +25,42 @@ config SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_SST_ACPI tristate -config SND_SOC_INTEL_SST_MATCH +config SND_SOC_ACPI_INTEL_MATCH tristate + select SND_SOC_ACPI if ACPI + +config SND_SOC_INTEL_SST_TOPLEVEL + tristate "Intel ASoC SST drivers" + depends on X86 || COMPILE_TEST + select SND_SOC_INTEL_MACH + select SND_SOC_INTEL_COMMON config SND_SOC_INTEL_HASWELL - tristate + tristate "Intel ASoC SST driver for Haswell/Broadwell" + depends on SND_SOC_INTEL_SST_TOPLEVEL && SND_DMA_SGBUF + depends on DMADEVICES select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_BAYTRAIL - tristate - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE - -config SND_SOC_INTEL_HASWELL_MACH - tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT5640 - help - This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell - Ultrabook platforms. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH - tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" - depends on X86 && ACPI && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_DA7219 - select SND_SOC_MAX98357A - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - select SND_HDA_DSP_LOADER - help - This adds support for ASoC machine driver for Broxton-P platforms - with DA7219 + MAX98357A I2S audio codec. - 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_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 + tristate "Intel ASoC SST driver for Baytrail (legacy)" + depends on SND_SOC_INTEL_SST_TOPLEVEL depends on DMADEVICES - depends on SND_SST_IPC_ACPI = n - select SND_SOC_INTEL_BAYTRAIL - select SND_SOC_RT5640 - help - This adds audio driver for Intel Baytrail platform based boards - with the RT5640 audio codec. This driver is deprecated, use - SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. - -config SND_SOC_INTEL_BYT_MAX98090_MACH - tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" - depends on X86_INTEL_LPSS && I2C - depends on DMADEVICES - depends on SND_SST_IPC_ACPI = n - select SND_SOC_INTEL_BAYTRAIL - select SND_SOC_MAX98090 - help - This adds audio driver for Intel Baytrail platform based boards - with the MAX98090 audio codec. - -config SND_SOC_INTEL_BDW_RT5677_MACH - tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" - depends on X86_INTEL_LPSS && GPIOLIB && I2C - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT5677 - help - This adds support for Intel Broadwell platform based boards with - the RT5677 audio codec. - -config SND_SOC_INTEL_BROADWELL_MACH - tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT286 - help - This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell - Ultrabook platforms. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BYTCR_RT5640_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" - depends on X86 && I2C && ACPI - select SND_SOC_RT5640 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5640 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BYTCR_RT5651_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" - depends on X86 && I2C && ACPI - select SND_SOC_RT5651 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5651 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_RT5672_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT5670 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5672 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_RT5645_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT5645 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5645/5650 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_MAX98090 - select SND_SOC_TS3A227E - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with MAX98090 audio codec it also can support TI jack chip as aux device. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_DA7213_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_DA7213 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail - platforms with DA7212/7213 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_ES8316_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_ES8316 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail & - Cherrytrail platforms with ES8316 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for the MinnowBoard Max or - Up boards and provides access to I2S signals on the Low-Speed - connector - If unsure select "N". - -config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT5663 - select SND_SOC_MAX98927 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for RT5663 + MAX98927. - Say Y if you have such a device. - If unsure select "N". + select SND_SOC_INTEL_SST_FIRMWARE -config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C && SPI - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT5663 - select SND_SOC_RT5514 - select SND_SOC_RT5514_SPI - select SND_SOC_MAX98927 - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for RT5663 + RT5514 + MAX98927. - Say Y if you have such a device. - If unsure select "N". +config SND_SST_ATOM_HIFI2_PLATFORM + tristate "Intel ASoC SST driver for HiFi2 platforms (*field, *trail)" + depends on SND_SOC_INTEL_SST_TOPLEVEL && X86 + select SND_SOC_COMPRESS config SND_SOC_INTEL_SKYLAKE - tristate + tristate "Intel ASoC SST driver for SKL/BXT/KBL/GLK/CNL" + depends on SND_SOC_INTEL_SST_TOPLEVEL && PCI && ACPI select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST -config SND_SOC_INTEL_SKL_RT286_MACH - tristate "ASoC Audio driver for SKL with RT286 I2S mode" - depends on X86 && ACPI && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT286 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC machine driver for Skylake platforms - with RT286 I2S audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" - depends on X86_INTEL_LPSS && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_NAU8825 - select SND_SOC_SSM4567 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for NAU88L25 + SSM4567. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" - depends on X86_INTEL_LPSS && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_NAU8825 - select SND_SOC_MAX98357A - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for NAU88L25 + MAX98357A. - Say Y if you have such a device. - If unsure select "N". +# ASoC codec drivers +source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index cdd495f7ee2c..b973d457e834 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,5 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 # Core support -obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ +obj-$(CONFIG_SND_SOC_INTEL_COMMON) += common/ # Platform Support obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile index aa6548c6feab..1dc60471b399 100644 --- a/sound/soc/intel/atom/Makefile +++ b/sound/soc/intel/atom/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-sst-atom-hifi2-platform-objs := sst-mfld-platform-pcm.o \ sst-mfld-platform-compress.o \ sst-atom-controls.o diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c index 1bead81bb510..1dbcab5a6ff0 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-compress.c +++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c @@ -259,7 +259,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream, return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata); } -struct snd_compr_ops sst_platform_compr_ops = { +const struct snd_compr_ops sst_platform_compr_ops = { .open = sst_platform_compr_open, .free = sst_platform_compr_free, diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index cb32cc7e5ec1..31a58c25472c 100644 --- a/sound/soc/intel/atom/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -25,7 +25,7 @@ #include "sst-atom-controls.h" extern struct sst_device *sst; -extern struct snd_compr_ops sst_platform_compr_ops; +extern const struct snd_compr_ops sst_platform_compr_ops; #define SST_MONO 1 #define SST_STEREO 2 diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile index fd21726361b5..795d1cf8f386 100644 --- a/sound/soc/intel/atom/sst/Makefile +++ b/sound/soc/intel/atom/sst/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o snd-intel-sst-pci-objs += sst_pci.o snd-intel-sst-acpi-objs += sst_acpi.o diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 0e928d54305d..32d6e02e2104 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -23,7 +23,6 @@ #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/io.h> -#include <linux/miscdevice.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <linux/pm_runtime.h> @@ -41,9 +40,10 @@ #include <acpi/acpi_bus.h> #include <asm/cpu_device_id.h> #include <asm/iosf_mbi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" -#include "../../common/sst-acpi.h" #include "sst.h" /* LPE viewpoint addresses */ @@ -239,19 +239,26 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } +static int is_byt(void) +{ + bool status = false; + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + if (x86_match_cpu(cpu_ids)) + status = true; + return status; +} static int is_byt_cr(struct device *dev, bool *bytcr) { int status = 0; if (IS_ENABLED(CONFIG_IOSF_MBI)) { - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; u32 bios_status; - if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) { + if (!is_byt() || !iosf_mbi_available()) { /* bail silently */ return status; } @@ -285,7 +292,7 @@ static int sst_acpi_probe(struct platform_device *pdev) int ret = 0; struct intel_sst_drv *ctx; const struct acpi_device_id *id; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct platform_device *mdev; struct platform_device *plat_dev; struct sst_platform_info *pdata; @@ -297,13 +304,17 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; dev_dbg(dev, "for %s\n", id->id); - mach = (struct sst_acpi_mach *)id->driver_data; - mach = sst_acpi_find_machine(mach); + mach = (struct snd_soc_acpi_mach *)id->driver_data; + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } + if (is_byt()) + mach->pdata = &byt_rvp_platform_data; + else + mach->pdata = &chv_platform_data; pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); @@ -381,286 +392,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static unsigned long cht_machine_id; - -#define CHT_SURFACE_MACH 1 -#define BYT_THINKPAD_10 2 - -static int cht_surface_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = CHT_SURFACE_MACH; - return 1; -} - -static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = BYT_THINKPAD_10; - return 1; -} - - -static const struct dmi_system_id byt_table[] = { - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), - }, - }, - { } -}; - -static const struct dmi_system_id cht_table[] = { - { - .callback = cht_surface_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), - }, - }, - { } -}; - - -static struct sst_acpi_mach cht_surface_mach = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data, -}; - -static struct sst_acpi_mach byt_thinkpad_10 = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data, -}; - -static struct sst_acpi_mach *cht_quirk(void *arg) -{ - struct sst_acpi_mach *mach = arg; - - dmi_check_system(cht_table); - - if (cht_machine_id == CHT_SURFACE_MACH) - return &cht_surface_mach; - else - return mach; -} - -static struct sst_acpi_mach *byt_quirk(void *arg) -{ - struct sst_acpi_mach *mach = arg; - - dmi_check_system(byt_table); - - if (cht_machine_id == BYT_THINKPAD_10) - return &byt_thinkpad_10; - else - return mach; -} - - -static struct sst_acpi_mach sst_acpi_bytcr[] = { - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .machine_quirk = byt_quirk, - .pdata = &byt_rvp_platform_data, - }, - { - .id = "10EC5642", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "INTCCFFD", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5651", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - /* some Baytrail platforms rely on RT5645, use CHT machine driver */ - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5648", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "80860F28", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_nocodec", - .pdata = &byt_rvp_platform_data - }, -#endif - {}, -}; - -/* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_acpi_mach sst_acpi_chv[] = { - { - .id = "10EC5670", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5672", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5650", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC3270", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - - { - .id = "193C9890", - .drv_name = "cht-bsw-max98090", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "ESSX8316", - .drv_name = "bytcht_es8316", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_es8316", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .machine_quirk = cht_quirk, - .pdata = &chv_platform_data - }, - { - .id = "10EC3276", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5651", - .pdata = &chv_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "808622A8", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_nocodec", - .pdata = &chv_platform_data - }, -#endif - {}, -}; - static const struct acpi_device_id sst_acpi_ids[] = { - { "80860F28", (unsigned long)&sst_acpi_bytcr}, - { "808622A8", (unsigned long) &sst_acpi_chv}, + { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines}, + { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines}, { }, }; diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 33917146d9c4..a686eef2cf7f 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -415,7 +415,6 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) return ret_val; } - BUG_ON(!sst_drv_ctx->fw_in_mem); block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); if (block == NULL) return -ENOMEM; diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 83d8dda15233..65e257b17a7e 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -45,7 +45,6 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) void *data = NULL; dev_dbg(sst_drv_ctx->dev, "Enter\n"); - BUG_ON(!params); str_params = (struct snd_sst_params *)params; memset(&alloc_param, 0, sizeof(alloc_param)); diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig new file mode 100644 index 000000000000..6f754708a48c --- /dev/null +++ b/sound/soc/intel/boards/Kconfig @@ -0,0 +1,265 @@ +config SND_SOC_INTEL_MACH + tristate "Intel Audio machine drivers" + depends on SND_SOC_INTEL_SST_TOPLEVEL + select SND_SOC_ACPI_INTEL_MATCH if ACPI + +if SND_SOC_INTEL_MACH + +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC + select SND_SOC_SN95031 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_PCI + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_HASWELL_MACH + tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT5640 + help + This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell + Ultrabook platforms. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BDW_RT5677_MACH + tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" + depends on X86_INTEL_LPSS && GPIOLIB && I2C + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT5677 + help + This adds support for Intel Broadwell platform based boards with + the RT5677 audio codec. + +config SND_SOC_INTEL_BROADWELL_MACH + tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT286 + help + This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell + Ultrabook platforms. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BYT_MAX98090_MACH + tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" + depends on X86_INTEL_LPSS && I2C + depends on SND_SST_IPC_ACPI = n + depends on SND_SOC_INTEL_BAYTRAIL + select SND_SOC_MAX98090 + help + This adds audio driver for Intel Baytrail platform based boards + with the MAX98090 audio codec. + +config SND_SOC_INTEL_BYT_RT5640_MACH + tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" + depends on X86_INTEL_LPSS && I2C + depends on SND_SST_IPC_ACPI = n + depends on SND_SOC_INTEL_BAYTRAIL + select SND_SOC_RT5640 + help + This adds audio driver for Intel Baytrail platform based boards + with the RT5640 audio codec. This driver is deprecated, use + SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. + +config SND_SOC_INTEL_BYTCR_RT5640_MACH + tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" + depends on X86 && I2C && ACPI + select SND_SOC_RT5640 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5640 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BYTCR_RT5651_MACH + tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" + depends on X86 && I2C && ACPI + select SND_SOC_RT5651 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5651 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_RT5672_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5670 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5672 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_RT5645_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5645 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5645/5650 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_MAX98090 + select SND_SOC_TS3A227E + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with MAX98090 audio codec it also can support TI jack chip as aux device. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_DA7213_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_DA7213 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail + platforms with DA7212/7213 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_ES8316_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ES8316 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail & + Cherrytrail platforms with ES8316 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" + depends on X86_INTEL_LPSS && I2C && ACPI + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for the MinnowBoard Max or + Up boards and provides access to I2S signals on the Low-Speed + connector + If unsure select "N". + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_SSM4567 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + SSM4567. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" + depends on X86_INTEL_LPSS && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + MAX98357A. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH + tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_DA7219 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton-P platforms + with DA7219 + MAX98357A I2S audio codec. + 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 + depends on 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_KBL_RT5663_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_MAX98927 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C && SPI + select SND_SOC_INTEL_SST + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_RT5514 + select SND_SOC_RT5514_SPI + select SND_SOC_MAX98927 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + RT5514 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + +endif diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index a5c5bc5732a2..69d2dfaeb00c 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 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 diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index ce35ec7884d1..f8a91a6f2a17 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -55,20 +55,6 @@ enum { BXT_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *bxt_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, BXT_DIALOG_CODEC_DAI, - strlen(BXT_DIALOG_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -77,7 +63,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_card *card = dapm->card; struct snd_soc_dai *codec_dai; - codec_dai = bxt_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, BXT_DIALOG_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); return -EIO; diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 18873e23f404..c4d82ad41bd7 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -27,9 +27,9 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/da7213.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), @@ -185,19 +185,11 @@ static struct snd_soc_dai_link dailink[] = { .dpcm_playback = 1, .ops = &aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -231,19 +223,18 @@ static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ static int bytcht_da7213_probe(struct platform_device *pdev) { - int ret_val = 0; - int i; struct snd_soc_card *card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; int dai_index = 0; + int ret_val = 0; + int i; mach = (&pdev->dev)->platform_data; card = &bytcht_da7213_card; card->dev = &pdev->dev; /* fix index of codec dai */ - dai_index = MERR_DPCM_COMPR + 1; for (i = 0; i < ARRAY_SIZE(dailink); i++) { if (!strcmp(dailink[i].codec_name, "i2c-DLGS7213:00")) { dai_index = i; @@ -252,8 +243,8 @@ static int bytcht_da7213_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(codec_name, sizeof(codec_name), "%s%s", "i2c-", i2c_name); dailink[dai_index].codec_name = codec_name; diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 52635462dac6..8088396717e3 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -29,28 +29,14 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" struct byt_cht_es8316_private { struct clk *mclk; }; -#define CODEC_DAI1 "ES8316 HiFi" - -static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CODEC_DAI1, - strlen(CODEC_DAI1))) - return rtd->codec_dai; - } - return NULL; -} - static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), @@ -208,22 +194,13 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = { .ops = &byt_cht_es8316_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, - /* back ends */ { /* Only SSP2 has been tested here, so BYT-CR platforms that * require SSP0 will not work. */ .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 1dd9441806fa..b80ec027a0e8 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -133,19 +133,11 @@ static struct snd_soc_dai_link dais[] = { .dpcm_playback = 1, .ops = &aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-LowSpeed Connector", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 4a76b099a508..f2c0fc415e52 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -22,19 +22,19 @@ #include <linux/moduleparam.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/platform_sst_audio.h> -#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" enum { @@ -44,13 +44,13 @@ enum { BYT_RT5640_IN3_MAP, }; -#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_MAP(quirk) ((quirk) & GENMASK(7, 0)) #define BYT_RT5640_DMIC_EN BIT(16) #define BYT_RT5640_MONO_SPEAKER BIT(17) #define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */ -#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ -#define BYT_RT5640_SSP0_AIF1 BIT(20) -#define BYT_RT5640_SSP0_AIF2 BIT(21) +#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ +#define BYT_RT5640_SSP0_AIF1 BIT(20) +#define BYT_RT5640_SSP0_AIF2 BIT(21) #define BYT_RT5640_MCLK_EN BIT(22) #define BYT_RT5640_MCLK_25MHZ BIT(23) @@ -145,22 +145,6 @@ static void log_quirks(struct device *dev) #define BYT_CODEC_DAI1 "rt5640-aif1" #define BYT_CODEC_DAI2 "rt5640-aif2" -static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, - strlen(BYT_CODEC_DAI1))) - return rtd->codec_dai; - if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, - strlen(BYT_CODEC_DAI2))) - return rtd->codec_dai; - - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -170,7 +154,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); int ret; - codec_dai = byt_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1); + if (!codec_dai) + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI2); + if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); @@ -178,7 +165,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, } if (SND_SOC_DAPM_EVENT_ON(event)) { - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { dev_err(card->dev, @@ -199,7 +186,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, 48000 * 512, SND_SOC_CLOCK_IN); if (!ret) { - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) clk_disable_unprepare(priv->mclk); } } @@ -376,8 +363,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -385,12 +372,11 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MONO_SPEAKER | - BYT_RT5640_DIFF_MIC | - BYT_RT5640_SSP0_AIF2 | - BYT_RT5640_MCLK_EN - ), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -398,9 +384,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), }, - .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | - BYT_RT5640_DMIC_EN | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -408,8 +394,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -417,8 +403,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), }, - .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN), + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -426,9 +412,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), DMI_MATCH(DMI_BOARD_NAME, "tPAD"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), }, { .callback = byt_rt5640_quirk_cb, @@ -436,7 +422,7 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_MCLK_EN | BYT_RT5640_SSP0_AIF1), @@ -446,9 +432,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), }, {} @@ -456,12 +442,12 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { - int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; - const struct snd_soc_dapm_route *custom_map; struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_codec *codec = runtime->codec; + const struct snd_soc_dapm_route *custom_map; int num_routes; + int ret; card->dapm.idle_bias_off = true; @@ -549,7 +535,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { /* * The firmware might enable the clock at * boot (this information may or may not @@ -693,18 +679,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { .dpcm_playback = 1, .ops = &byt_rt5640_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Baytrail Compressed Port", - .stream_name = "Baytrail Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */ .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -758,12 +736,12 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { - int ret_val = 0; - struct sst_acpi_mach *mach; + struct byt_rt5640_private *priv; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; + int ret_val = 0; + int dai_index = 0; int i; - int dai_index; - struct byt_rt5640_private *priv; is_bytcr = false; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); @@ -776,7 +754,6 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&byt_rt5640_card, priv); /* fix index of codec dai */ - dai_index = MERR_DPCM_COMPR + 1; for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) { if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) { dai_index = i; @@ -785,8 +762,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), "%s%s", "i2c-", i2c_name); @@ -819,7 +796,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -831,7 +808,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); @@ -891,7 +869,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_cpu_dai_name; } - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { ret_val = PTR_ERR(priv->mclk); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 4a3516b38c2c..d955836c6870 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -21,24 +21,124 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5651.h" #include "../atom/sst-atom-controls.h" +enum { + BYT_RT5651_DMIC_MAP, + BYT_RT5651_IN1_MAP, + BYT_RT5651_IN2_MAP, +}; + +#define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(7, 0)) +#define BYT_RT5651_DMIC_EN BIT(16) +#define BYT_RT5651_MCLK_EN BIT(17) +#define BYT_RT5651_MCLK_25MHZ BIT(18) + +struct byt_rt5651_private { + struct clk *mclk; + struct snd_soc_jack jack; +}; + +static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC_MAP | + BYT_RT5651_DMIC_EN | + BYT_RT5651_MCLK_EN; + +static void log_quirks(struct device *dev) +{ + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP) + dev_info(dev, "quirk DMIC_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP) + dev_info(dev, "quirk IN1_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) + dev_info(dev, "quirk IN2_MAP enabled"); + if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) + dev_info(dev, "quirk DMIC enabled"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) + dev_info(dev, "quirk MCLK_EN enabled"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) + dev_info(dev, "quirk MCLK_25MHZ enabled"); +} + +#define BYT_CODEC_DAI1 "rt5651-aif1" + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1); + if (!codec_dai) { + dev_err(card->dev, + "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } + ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1, + 48000 * 512, + SND_SOC_CLOCK_IN); + } else { + /* + * Set codec clock source to internal clock before + * turning off the platform clock. Codec needs clock + * for Jack detection and button press + */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK, + 48000 * 512, + SND_SOC_CLOCK_IN); + if (!ret) + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) + clk_disable_unprepare(priv->mclk); + } + + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return 0; +} + static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + }; static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Internal Mic", NULL, "Platform Clock"}, + {"Speaker", NULL, "Platform Clock"}, + {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -47,38 +147,30 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"ssp2 Rx", NULL, "AIF1 Capture"}, {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */ - {"IN2P", NULL, "Headset Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, {"Speaker", NULL, "LOUTL"}, {"Speaker", NULL, "LOUTR"}, }; -static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic1_map[] = { - {"DMIC1", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic2_map[] = { - {"DMIC2", NULL, "Internal Mic"}, +static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { + {"IN2P", NULL, "Headset Mic"}, + {"DMIC L1", NULL, "Internal Mic"}, + {"DMIC R1", NULL, "Internal Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { {"Internal Mic", NULL, "micbias1"}, + {"IN2P", NULL, "Headset Mic"}, {"IN1P", NULL, "Internal Mic"}, }; -enum { - BYT_RT5651_DMIC1_MAP, - BYT_RT5651_DMIC2_MAP, - BYT_RT5651_IN1_MAP, +static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { + {"Internal Mic", NULL, "micbias1"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN2P", NULL, "Internal Mic"}, }; -#define BYT_RT5651_MAP(quirk) ((quirk) & 0xff) -#define BYT_RT5651_DMIC_EN BIT(16) - -static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP | - BYT_RT5651_DMIC_EN; - static const struct snd_kcontrol_new byt_rt5651_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -86,6 +178,17 @@ static const struct snd_kcontrol_new byt_rt5651_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; +static struct snd_soc_jack_pin bytcr_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -103,9 +206,26 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1, - params_rate(params) * 50, - params_rate(params) * 512); + if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) { + /* 2x25 bit slots on SSP2 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + } else { + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_MCLK, + 25000000, + params_rate(params) * 512); + } else { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_MCLK, + 19200000, + params_rate(params) * 512); + } + } + if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; @@ -114,33 +234,60 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } +static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) +{ + byt_rt5651_quirk = (unsigned long)id->driver_data; + return 1; +} + static const struct dmi_system_id byt_rt5651_quirk_table[] = { + { + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (void *)(BYT_RT5651_DMIC_MAP | + BYT_RT5651_DMIC_EN), + }, + { + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), + DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), + }, + .driver_data = (void *)(BYT_RT5651_IN2_MAP), + }, {} }; static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) { - int ret; struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); const struct snd_soc_dapm_route *custom_map; int num_routes; + int ret; card->dapm.idle_bias_off = true; - dmi_check_system(byt_rt5651_quirk_table); switch (BYT_RT5651_MAP(byt_rt5651_quirk)) { case BYT_RT5651_IN1_MAP: custom_map = byt_rt5651_intmic_in1_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map); break; - case BYT_RT5651_DMIC2_MAP: - custom_map = byt_rt5651_intmic_dmic2_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic2_map); + case BYT_RT5651_IN2_MAP: + custom_map = byt_rt5651_intmic_in2_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_map); break; default: - custom_map = byt_rt5651_intmic_dmic1_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map); + custom_map = byt_rt5651_intmic_dmic_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); } + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); + if (ret) + return ret; ret = snd_soc_add_card_controls(card, byt_rt5651_controls, ARRAY_SIZE(byt_rt5651_controls)); @@ -151,6 +298,40 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); + + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); + + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); + } + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &priv->jack, + bytcr_jack_pins, ARRAY_SIZE(bytcr_jack_pins)); + if (ret) { + dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); + return ret; + } + + rt5651_set_jack_detect(codec, &priv->jack); + return ret; } @@ -253,19 +434,11 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { .dpcm_playback = 1, .ops = &byt_rt5651_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -296,13 +469,65 @@ static struct snd_soc_card byt_rt5651_card = { .fully_routed = true, }; +static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ + static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { + struct byt_rt5651_private *priv; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; int ret_val = 0; + int dai_index = 0; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + if (!priv) + return -ENOMEM; /* register the soc card */ byt_rt5651_card.dev = &pdev->dev; + mach = byt_rt5651_card.dev->platform_data; + snd_soc_card_set_drvdata(&byt_rt5651_card, priv); + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) { + if (!strcmp(byt_rt5651_dais[i].codec_name, "i2c-10EC5651:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { + snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name), + "%s%s", "i2c-", i2c_name); + + byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; + } + + /* check quirks before creating card */ + dmi_check_system(byt_rt5651_quirk_table); + log_quirks(&pdev->dev); + + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(priv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(priv->mclk)); + /* + * Fall back to bit clock usage for -ENOENT (clock not + * available likely due to missing dependencies), bail + * for all other errors, including -EPROBE_DEFER + */ + if (ret_val != -ENOENT) + return ret_val; + byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN; + } + } + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); if (ret_val) { diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 20755ecc7f9e..d3e1c7e12004 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -35,15 +36,48 @@ #define CHT_CODEC_DAI "HiFi" struct cht_mc_private { + struct clk *mclk; struct snd_soc_jack jack; bool ts3a227e_present; }; +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = clk_prepare_enable(ctx->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } else { + clk_disable_unprepare(ctx->mclk); + } + + return 0; +} + static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route cht_audio_map[] = { @@ -60,6 +94,10 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "HiFi Capture"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Int Mic", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -109,6 +147,40 @@ static struct notifier_block cht_jack_nb = { .notifier_call = cht_ti_jack_event, }; +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "hp", + .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, + .debounce_time = 200, + }, + { + .name = "mic", + .invert = 1, + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, + }, +}; + +static const struct acpi_gpio_params hp_gpios = { 0, 0, false }; +static const struct acpi_gpio_params mic_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_max98090_gpios[] = { + { "hp-gpios", &hp_gpios, 1 }, + { "mic-gpios", &mic_gpios, 1 }, + {}, +}; + static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; @@ -116,30 +188,55 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); struct snd_soc_jack *jack = &ctx->jack; - /** - * TI supports 4 butons headset detection - * KEY_MEDIA - * KEY_VOICECOMMAND - * KEY_VOLUMEUP - * KEY_VOLUMEDOWN - */ - if (ctx->ts3a227e_present) - jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3; - else - jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; + if (ctx->ts3a227e_present) { + /* + * The jack has already been created in the + * cht_max98090_headset_init() function. + */ + snd_soc_jack_notifier_register(jack, &cht_jack_nb); + return 0; + } - ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", - jack_type, jack, NULL, 0); + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; + ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + jack_type, jack, + hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret); return ret; } - if (ctx->ts3a227e_present) - snd_soc_jack_notifier_register(jack, &cht_jack_nb); + ret = snd_soc_jack_add_gpiods(runtime->card->dev->parent, jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) { + /* + * flag error but don't bail if jack detect is broken + * due to platform issues or bad BIOS/configuration + */ + dev_err(runtime->dev, + "jack detection gpios not added, error %d\n", ret); + } + + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(ctx->mclk); + if (!ret) + clk_disable_unprepare(ctx->mclk); + + ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + + if (ret) + dev_err(runtime->dev, "unable to set MCLK rate\n"); return ret; } @@ -160,7 +257,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); @@ -173,8 +270,8 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + /* set SSP2 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -188,8 +285,29 @@ static int cht_max98090_headset_init(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_jack *jack = &ctx->jack; + int jack_type; + int ret; - return ts3a227e_enable_jack_detect(component, &ctx->jack); + /* + * TI supports 4 butons headset detection + * KEY_MEDIA + * KEY_VOICECOMMAND + * KEY_VOLUMEUP + * KEY_VOLUMEDOWN + */ + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3; + + ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, + jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + return ts3a227e_enable_jack_detect(component, jack); } static const struct snd_soc_ops cht_aif1_ops = { @@ -232,18 +350,10 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -277,6 +387,7 @@ static struct snd_soc_card snd_soc_card_cht = { static int snd_cht_mc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int ret_val = 0; struct cht_mc_private *drv; @@ -289,11 +400,25 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* no need probe TI jack detection chip */ snd_soc_card_cht.aux_dev = NULL; snd_soc_card_cht.num_aux_devs = 0; + + ret_val = devm_acpi_dev_add_driver_gpios(dev->parent, + acpi_max98090_gpios); + if (ret_val) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); } /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); + + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); + } + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); if (ret_val) { dev_err(&pdev->dev, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 5bcde01d15e6..18d129caa974 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -21,20 +21,20 @@ */ #include <linux/module.h> -#include <linux/acpi.h> #include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/clk.h> #include <linux/dmi.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/platform_sst_audio.h> -#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5645.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI1 "rt5645-aif1" @@ -53,7 +53,7 @@ struct cht_mc_private { struct clk *mclk; }; -#define CHT_RT5645_MAP(quirk) ((quirk) & 0xff) +#define CHT_RT5645_MAP(quirk) ((quirk) & GENMASK(7, 0)) #define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */ #define CHT_RT5645_SSP0_AIF1 BIT(17) #define CHT_RT5645_SSP0_AIF2 BIT(18) @@ -70,21 +70,6 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk SSP0_AIF2 enabled"); } -static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1, - strlen(CHT_CODEC_DAI1))) - return rtd->codec_dai; - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2, - strlen(CHT_CODEC_DAI2))) - return rtd->codec_dai; - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -94,20 +79,21 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; - codec_dai = cht_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI1); + if (!codec_dai) + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI2); + if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; } if (SND_SOC_DAPM_EVENT_ON(event)) { - if (ctx->mclk) { - ret = clk_prepare_enable(ctx->mclk); - if (ret < 0) { - dev_err(card->dev, - "could not configure MCLK state"); - return ret; - } + ret = clk_prepare_enable(ctx->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; } } else { /* Set codec sysclk source to its internal clock because codec PLL will @@ -122,8 +108,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } - if (ctx->mclk) - clk_disable_unprepare(ctx->mclk); + clk_disable_unprepare(ctx->mclk); } return 0; @@ -258,11 +243,11 @@ static const struct dmi_system_id cht_rt5645_quirk_table[] = { static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { - int ret; - int jack_type; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); + struct snd_soc_codec *codec = runtime->codec; + int jack_type; + int ret; if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { @@ -320,26 +305,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack); - if (ctx->mclk) { - /* - * The firmware might enable the clock at - * boot (this information may or may not - * be reflected in the enable clock register). - * To change the rate we must disable the clock - * first to cover these cases. Due to common - * clock framework restrictions that do not allow - * to disable a clock that has not been enabled, - * we need to enable the clock first. - */ - ret = clk_prepare_enable(ctx->mclk); - if (!ret) - clk_disable_unprepare(ctx->mclk); - ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(ctx->mclk); + if (!ret) + clk_disable_unprepare(ctx->mclk); + + ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + + if (ret) + dev_err(runtime->dev, "unable to set MCLK rate\n"); - if (ret) - dev_err(runtime->dev, "unable to set MCLK rate\n"); - } return ret; } @@ -460,19 +445,11 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -545,15 +522,15 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_cht_mc_probe(struct platform_device *pdev) { - int ret_val = 0; - int i; - struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; + struct cht_mc_private *drv; const char *i2c_name = NULL; - int dai_index = 0; bool found = false; bool is_bytcr = false; + int dai_index = 0; + int ret_val = 0; + int i; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) @@ -589,8 +566,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "%s%s", "i2c-", i2c_name); cht_dailink[dai_index].codec_name = cht_rt5645_codec_name; @@ -622,7 +599,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -634,7 +611,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); @@ -682,14 +660,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) cht_rt5645_cpu_dai_name; } - if (is_valleyview()) { - drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); - if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(drv->mclk)); - return PTR_ERR(drv->mclk); - } + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); } snd_soc_card_set_drvdata(card, drv); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index f597d5582223..f8f21eee9b2d 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -20,14 +20,14 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/clk.h> -#include <asm/cpu_device_id.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5670.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" + /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 @@ -51,18 +51,6 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { }, }; -static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, - strlen(CHT_CODEC_DAI))) - return rtd->codec_dai; - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -72,7 +60,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; - codec_dai = cht_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; @@ -315,20 +303,12 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* Back End DAI links */ { /* SSP2 - Codec */ .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -348,9 +328,11 @@ static struct snd_soc_dai_link cht_dailink[] = { static int cht_suspend_pre(struct snd_soc_card *card) { struct snd_soc_component *component; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); list_for_each_entry(component, &card->component_dev_list, card_list) { - if (!strcmp(component->name, "i2c-10EC5670:00")) { + if (!strncmp(component->name, + ctx->codec_name, sizeof(ctx->codec_name))) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); @@ -364,9 +346,11 @@ static int cht_suspend_pre(struct snd_soc_card *card) static int cht_resume_post(struct snd_soc_card *card) { struct snd_soc_component *component; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); list_for_each_entry(component, &card->component_dev_list, card_list) { - if (!strcmp(component->name, "i2c-10EC5670:00")) { + if (!strncmp(component->name, + ctx->codec_name, sizeof(ctx->codec_name))) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); dev_dbg(codec->dev, "enabling jack detect for resume.\n"); @@ -380,7 +364,7 @@ static int cht_resume_post(struct snd_soc_card *card) /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { - .name = "cherrytrailcraudio", + .name = "cht-bsw-rt5672", .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), @@ -394,25 +378,13 @@ static struct snd_soc_card snd_soc_card_cht = { .resume_post = cht_resume_post, }; -static bool is_valleyview(void) -{ - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; - - if (!x86_match_cpu(cpu_ids)) - return false; - return true; -} - #define RT5672_I2C_DEFAULT "i2c-10EC5670:00" static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; struct cht_mc_private *drv; - struct sst_acpi_mach *mach = pdev->dev.platform_data; + struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; const char *i2c_name; int i; @@ -424,7 +396,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* fixup codec name based on HID */ if (mach) { - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", i2c_name); @@ -439,14 +411,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } } - if (is_valleyview()) { - drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); - if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(drv->mclk)); - return PTR_ERR(drv->mclk); - } + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); } snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 7f7607420706..6f9a8bcf20f3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -17,6 +17,7 @@ * GNU General Public License for more details. */ +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> @@ -208,6 +209,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) int ret; struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_jack *jack; /* * Headset buttons map to the google Reference headset. @@ -221,6 +223,13 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; } + + jack = &ctx->kabylake_headset; + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + rt5663_set_jack_detect(codec, &ctx->kabylake_headset); return ret; } @@ -341,13 +350,28 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, 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); + struct snd_soc_dpcm *dpcm = container_of( + params, struct snd_soc_dpcm, hw_params); + struct snd_soc_dai_link *fe_dai_link = dpcm->fe->dai_link; + struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link; - /* The ADSP will convert the FE rate to 48k, stereo */ - rate->min = rate->max = 48000; - channels->min = channels->max = 2; - /* set SSP1 to 24 bit */ - snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + /* + * The ADSP will convert the FE rate to 48k, stereo, 24 bit + */ + if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || + !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + } + /* + * The speaker on the SSP0 supports S16_LE and not S24_LE. + * thus changing the mask here + */ + if (!strcmp(be_dai_link->name, "SSP0-Codec")) + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -390,6 +414,43 @@ static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret = 0, j; + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { + /* + * Use channel 4 and 5 for the first amp + */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + /* + * Use channel 6 and 7 for the second amp + */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + } + return ret; +} + +static struct snd_soc_ops kabylake_ssp0_ops = { + .hw_params = kabylake_ssp0_hw_params, +}; + static unsigned int channels_dmic[] = { 2, 4, }; @@ -593,12 +654,13 @@ static struct snd_soc_dai_link kabylake_dais[] = { .no_pcm = 1, .codecs = max98927_codec_components, .num_codecs = ARRAY_SIZE(max98927_codec_components), - .dai_fmt = SND_SOC_DAIFMT_I2S | + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ignore_pmdown_time = 1, .be_hw_params_fixup = kabylake_ssp_fixup, .dpcm_playback = 1, + .ops = &kabylake_ssp0_ops, }, { /* SSP1 - Codec */ diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 88ff54220007..6072164f2d43 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -302,6 +302,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, * The ADSP will convert the FE rate to 48k, stereo, 24 bit */ if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { rate->min = rate->max = 48000; channels->min = channels->max = 2; @@ -604,6 +605,8 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { codec = pcm->codec_dai->codec; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP,pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, &ctx->kabylake_hdmi[i], NULL, 0); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 5ed0aa27b467..1b5a689dc99b 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -54,20 +54,6 @@ enum { SKL_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, - strlen(SKL_NUVOTON_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -76,7 +62,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dai *codec_dai; int ret; - codec_dai = skl_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 01b8b140bb08..7bea4bc77481 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -57,20 +57,6 @@ enum { SKL_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, - strlen(SKL_NUVOTON_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static const struct snd_kcontrol_new skylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -86,7 +72,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dai *codec_dai; int ret; - codec_dai = skl_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found\n"); return -EIO; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 1a35149bcad7..7379d8830c39 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,10 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o -snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o -obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o +obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c new file mode 100644 index 000000000000..bfe1ca68a542 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -0,0 +1,196 @@ +/* + * soc-apci-intel-byt-match.c - tables and support for BYT ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static unsigned long byt_machine_id; + +#define BYT_THINKPAD_10 1 + +static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) +{ + byt_machine_id = BYT_THINKPAD_10; + return 1; +} + + +static const struct dmi_system_id byt_table[] = { + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach byt_thinkpad_10 = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", +}; + +static struct snd_soc_acpi_mach *byt_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(byt_table); + + if (byt_machine_id == BYT_THINKPAD_10) + return &byt_thinkpad_10; + else + return mach; +} + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { + { + .id = "10EC5640", + .drv_name = "byt-rt5640", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + { + .id = "193C9890", + .drv_name = "byt-max98090", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_legacy_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .machine_quirk = byt_quirk, + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5642", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "INTCCFFD", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5651", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some Baytrail platforms rely on RT5645, use CHT machine driver */ + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5648", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* use CHT driver to Baytrail Chromebooks */ + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "80860F28", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c new file mode 100644 index 000000000000..b50a0d53846b --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -0,0 +1,194 @@ +/* + * soc-apci-intel-cht-match.c - tables and support for CHT ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static unsigned long cht_machine_id; + +#define CHT_SURFACE_MACH 1 + +static int cht_surface_quirk_cb(const struct dmi_system_id *id) +{ + cht_machine_id = CHT_SURFACE_MACH; + return 1; +} + +static const struct dmi_system_id cht_table[] = { + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach cht_surface_mach = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", +}; + +static struct snd_soc_acpi_mach *cht_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(cht_table); + + if (cht_machine_id == CHT_SURFACE_MACH) + return &cht_surface_mach; + else + return mach; +} + +/* Cherryview-based platforms: CherryTrail and Braswell */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { + { + .id = "10EC5670", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5672", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5650", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC3270", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "ESSX8316", + .drv_name = "bytcht_es8316", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_es8316", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .machine_quirk = cht_quirk, + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC3276", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5651", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "808622A8", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cherrytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c new file mode 100644 index 000000000000..e0e8c8c27528 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -0,0 +1,64 @@ +/* + * soc-apci-intel-hsw-bdw-match.c - tables and support for ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST1.bin", + .sof_fw_filename = "intel/reef-hsw.ri", + .sof_tplg_filename = "intel/reef-hsw.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_haswell_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { + { + .id = "INT343A", + .drv_name = "broadwell-audio", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + { + .id = "RT5677CE", + .drv_name = "bdw-rt5677", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_broadwell_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 1285cc597b6b..cf6fbbd4e378 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -21,7 +21,8 @@ #include <linux/platform_device.h> #include "sst-dsp.h" -#include "sst-acpi.h" +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 @@ -30,7 +31,7 @@ /* Descriptor for setting up SST platform data */ struct sst_acpi_desc { const char *drv_name; - struct sst_acpi_mach *machines; + struct snd_soc_acpi_mach *machines; /* Platform resource indexes. Must set to -1 if not used */ int resindex_lpe_base; int resindex_pcicfg_base; @@ -49,7 +50,7 @@ struct sst_acpi_priv { struct platform_device *pdev_pcm; struct sst_pdata sst_pdata; struct sst_acpi_desc *desc; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; }; static void sst_acpi_fw_cb(const struct firmware *fw, void *context) @@ -59,7 +60,7 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context) struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; struct sst_acpi_desc *desc = sst_acpi->desc; - struct sst_acpi_mach *mach = sst_acpi->mach; + struct snd_soc_acpi_mach *mach = sst_acpi->mach; sst_pdata->fw = fw; if (!fw) { @@ -85,7 +86,7 @@ static int sst_acpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sst_acpi_priv *sst_acpi; struct sst_pdata *sst_pdata; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct sst_acpi_desc *desc; struct resource *mmio; int ret = 0; @@ -99,7 +100,7 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; desc = (struct sst_acpi_desc *)id->driver_data; - mach = sst_acpi_find_machine(desc->machines); + mach = snd_soc_acpi_find_machine(desc->machines); if (mach == NULL) { dev_err(dev, "No matching ASoC machine driver found\n"); return -ENODEV; @@ -179,14 +180,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct sst_acpi_mach haswell_machines[] = { - { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_haswell_desc = { .drv_name = "haswell-pcm-audio", - .machines = haswell_machines, + .machines = snd_soc_acpi_intel_haswell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -197,15 +193,9 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; -static struct sst_acpi_mach broadwell_machines[] = { - { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, - { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_broadwell_desc = { .drv_name = "haswell-pcm-audio", - .machines = broadwell_machines, + .machines = snd_soc_acpi_intel_broadwell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -217,15 +207,9 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; #if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) -static struct sst_acpi_mach baytrail_machines[] = { - { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, - { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_baytrail_desc = { .drv_name = "baytrail-pcm-audio", - .machines = baytrail_machines, + .machines = snd_soc_acpi_intel_baytrail_legacy_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = 2, diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h deleted file mode 100644 index afe9b87b8bd5..000000000000 --- a/sound/soc/intel/common/sst-acpi.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013-15, Intel Corporation. All rights reserved. - * - * 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 <linux/stddef.h> -#include <linux/acpi.h> - -struct sst_acpi_package_context { - char *name; /* package name */ - int length; /* number of elements */ - struct acpi_buffer *format; - struct acpi_buffer *state; - bool data_valid; -}; - -#if IS_ENABLED(CONFIG_ACPI) -/* translation fron HID to I2C name, needed for DAI codec_name */ -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx); -#else -static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) -{ - return NULL; -} -static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) -{ - return false; -} -#endif - -/* acpi match */ -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); - -/* acpi check hid */ -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); - -/* Descriptor for SST ASoC machine driver */ -struct sst_acpi_mach { - /* ACPI ID for the matching machine driver. Audio codec for instance */ - const u8 id[ACPI_ID_LEN]; - /* machine driver name */ - const char *drv_name; - /* firmware file name */ - const char *fw_filename; - - /* board name */ - const char *board; - struct sst_acpi_mach * (*machine_quirk)(void *arg); - const void *quirk_data; - void *pdata; -}; - -#define SST_ACPI_MAX_CODECS 3 - -/** - * struct sst_codecs: Structure to hold secondary codec information apart from - * the matched one, this data will be passed to the quirk function to match - * with the ACPI detected devices - * - * @num_codecs: number of secondary codecs used in the platform - * @codecs: holds the codec IDs - * - */ -struct sst_codecs { - int num_codecs; - u8 codecs[SST_ACPI_MAX_CODECS][ACPI_ID_LEN]; -}; - -/* check all codecs */ -struct sst_acpi_mach *sst_acpi_codec_list(void *arg); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index a086c35f91bb..657afc02f1c4 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -19,6 +19,7 @@ #include <linux/sched.h> #include <linux/firmware.h> #include <linux/export.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> @@ -274,7 +275,6 @@ int sst_dma_new(struct sst_dsp *sst) struct sst_pdata *sst_pdata = sst->pdata; struct sst_dma *dma; struct resource mem; - const char *dma_dev_name; int ret = 0; if (sst->pdata->resindex_dma_base == -1) @@ -285,7 +285,6 @@ int sst_dma_new(struct sst_dsp *sst) * is attached to the ADSP IP. */ switch (sst->pdata->dma_engine) { case SST_DMA_TYPE_DW: - dma_dev_name = "dw_dmac"; break; default: dev_err(sst->dev, "error: invalid DMA engine %d\n", diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 3380deb81015..d1ccbecd141f 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ skl-topology.o diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 89f70133c8e4..61b5bfa79d13 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -613,8 +613,10 @@ skip_buf_size_calc: } #define DMA_CONTROL_ID 5 +#define DMA_I2S_BLOB_SIZE 21 -int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, + u32 caps_size, u32 node_id) { struct skl_dma_control *dma_ctrl; struct skl_ipc_large_config_msg msg = {0}; @@ -624,24 +626,27 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) /* * if blob size zero, then return */ - if (mconfig->formats_config.caps_size == 0) + if (caps_size == 0) return 0; msg.large_param_id = DMA_CONTROL_ID; - msg.param_data_size = sizeof(struct skl_dma_control) + - mconfig->formats_config.caps_size; + msg.param_data_size = sizeof(struct skl_dma_control) + caps_size; dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL); if (dma_ctrl == NULL) return -ENOMEM; - dma_ctrl->node_id = skl_get_node_id(ctx, mconfig); + dma_ctrl->node_id = node_id; - /* size in dwords */ - dma_ctrl->config_length = mconfig->formats_config.caps_size / 4; + /* + * NHLT blob may contain additional configs along with i2s blob. + * firmware expects only the i2s blob size as the config_length. + * So fix to i2s blob size. + * size in dwords. + */ + dma_ctrl->config_length = DMA_I2S_BLOB_SIZE; - memcpy(dma_ctrl->config_data, mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + memcpy(dma_ctrl->config_data, caps, caps_size); err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); @@ -702,18 +707,11 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx, struct skl_module *module = mconfig->module; struct skl_module_iface *iface = &module->formats[mconfig->fmt_idx]; struct skl_module_fmt *fmt = &iface->outputs[0].fmt; - int i = 0; skl_set_base_module_format(ctx, mconfig, (struct skl_base_cfg *)mixer_mconfig); mixer_mconfig->out_ch_cfg = fmt->ch_cfg; - - /* Select F/W default coefficient */ - mixer_mconfig->coeff_sel = 0x0; - - /* User coeff, don't care since we are selecting F/W defaults */ - for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++) - mixer_mconfig->coeff[i] = 0xDEADBEEF; + mixer_mconfig->ch_map = fmt->ch_map; } /* diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index e7d766d56c8e..d14c50a60289 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -20,6 +20,8 @@ #include <linux/pci.h> #include "skl.h" +#define NHLT_ACPI_HEADER_SIG "NHLT" + /* Unique identification for getting NHLT blobs */ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, @@ -45,6 +47,13 @@ struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) memremap(nhlt_ptr->min_addr, nhlt_ptr->length, MEMREMAP_WB); ACPI_FREE(obj); + if (nhlt_table && (strncmp(nhlt_table->header.signature, + NHLT_ACPI_HEADER_SIG, + strlen(NHLT_ACPI_HEADER_SIG)) != 0)) { + memunmap(nhlt_table); + dev_err(dev, "NHLT ACPI header signature incorrect\n"); + return NULL; + } return nhlt_table; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 2b1e513b1680..1dd97479e0c0 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -355,7 +355,8 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, } mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); - skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); + if (mconfig) + skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); kfree(dma_params); } @@ -652,7 +653,7 @@ static const struct snd_soc_dai_ops skl_link_dai_ops = { .trigger = skl_link_pcm_trigger, }; -static struct snd_soc_dai_driver skl_platform_dai[] = { +static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -796,8 +797,10 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .sig_bits = 32, }, }, +}; /* BE CPU Dais */ +static struct snd_soc_dai_driver skl_platform_dai[] = { { .name = "SSP0 Pin", .ops = &skl_be_ssp_dai_ops, @@ -975,6 +978,14 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai) +{ + pcm_dai->ops = &skl_pcm_dai_ops; + + return 0; +} + static int skl_platform_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -1362,6 +1373,8 @@ int skl_platform_register(struct device *dev) int ret; struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct skl *skl = ebus_to_skl(ebus); + struct snd_soc_dai_driver *dais; + int num_dais = ARRAY_SIZE(skl_platform_dai); INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); @@ -1371,14 +1384,38 @@ int skl_platform_register(struct device *dev) dev_err(dev, "soc platform registration failed %d\n", ret); return ret; } + + skl->dais = kmemdup(skl_platform_dai, sizeof(skl_platform_dai), + GFP_KERNEL); + if (!skl->dais) { + ret = -ENOMEM; + goto err; + } + + if (!skl->use_tplg_pcm) { + dais = krealloc(skl->dais, sizeof(skl_fe_dai) + + sizeof(skl_platform_dai), GFP_KERNEL); + if (!dais) { + ret = -ENOMEM; + goto err; + } + + skl->dais = dais; + memcpy(&skl->dais[ARRAY_SIZE(skl_platform_dai)], skl_fe_dai, + sizeof(skl_fe_dai)); + num_dais += ARRAY_SIZE(skl_fe_dai); + } + ret = snd_soc_register_component(dev, &skl_component, - skl_platform_dai, - ARRAY_SIZE(skl_platform_dai)); + skl->dais, num_dais); if (ret) { dev_err(dev, "soc component registration failed %d\n", ret); - snd_soc_unregister_platform(dev); + goto err; } + return 0; +err: + snd_soc_unregister_platform(dev); return ret; } @@ -1398,5 +1435,7 @@ int skl_platform_unregister(struct device *dev) snd_soc_unregister_component(dev); snd_soc_unregister_platform(dev); + kfree(skl->dais); + return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 369ef7ce981c..8ff89280d9fd 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -251,6 +251,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, struct uuid_module *module; struct firmware stripped_fw; unsigned int safe_file; + int ret = 0; /* Get the FW pointer to derive ADSP header */ stripped_fw.data = fw->data; @@ -299,8 +300,10 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, for (i = 0; i < num_entry; i++, mod_entry++) { module = kzalloc(sizeof(*module), GFP_KERNEL); - if (!module) - return -ENOMEM; + if (!module) { + ret = -ENOMEM; + goto free_uuid_list; + } uuid_bin = (uuid_le *)mod_entry->uuid.id; memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); @@ -311,8 +314,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, size = sizeof(int) * mod_entry->instance_max_count; module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); if (!module->instance_id) { - kfree(module); - return -ENOMEM; + ret = -ENOMEM; + goto free_uuid_list; } list_add_tail(&module->list, &skl->uuid_list); @@ -323,6 +326,10 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, } return 0; + +free_uuid_list: + skl_freeup_uuid_list(skl); + return ret; } void skl_freeup_uuid_list(struct skl_sst *ctx) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 22f768ca3c73..a072bcf209d2 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2036,21 +2036,45 @@ static int skl_tplg_add_pipe(struct device *dev, return 0; } -static int skl_tplg_fill_pin(struct device *dev, u32 tkn, +static int skl_tplg_get_uuid(struct device *dev, u8 *guid, + struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) +{ + if (uuid_tkn->token == SKL_TKN_UUID) { + memcpy(guid, &uuid_tkn->uuid, 16); + return 0; + } + + dev_err(dev, "Not an UUID token %d\n", uuid_tkn->token); + + return -EINVAL; +} + +static int skl_tplg_fill_pin(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, struct skl_module_pin *m_pin, - int pin_index, u32 value) + int pin_index) { - switch (tkn) { + int ret; + + switch (tkn_elem->token) { case SKL_TKN_U32_PIN_MOD_ID: - m_pin[pin_index].id.module_id = value; + m_pin[pin_index].id.module_id = tkn_elem->value; break; case SKL_TKN_U32_PIN_INST_ID: - m_pin[pin_index].id.instance_id = value; + m_pin[pin_index].id.instance_id = tkn_elem->value; + break; + + case SKL_TKN_UUID: + ret = skl_tplg_get_uuid(dev, m_pin[pin_index].id.mod_uuid.b, + (struct snd_soc_tplg_vendor_uuid_elem *)tkn_elem); + if (ret < 0) + return ret; + break; default: - dev_err(dev, "%d Not a pin token\n", value); + dev_err(dev, "%d Not a pin token\n", tkn_elem->token); return -EINVAL; } @@ -2083,9 +2107,7 @@ static int skl_tplg_fill_pins_info(struct device *dev, return -EINVAL; } - ret = skl_tplg_fill_pin(dev, tkn_elem->token, - m_pin, pin_count, tkn_elem->value); - + ret = skl_tplg_fill_pin(dev, tkn_elem, m_pin, pin_count); if (ret < 0) return ret; @@ -2170,19 +2192,6 @@ static int skl_tplg_widget_fill_fmt(struct device *dev, return skl_tplg_fill_fmt(dev, dst_fmt, tkn, val); } -static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig, - struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) -{ - if (uuid_tkn->token == SKL_TKN_UUID) - memcpy(&mconfig->guid, &uuid_tkn->uuid, 16); - else { - dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token); - return -EINVAL; - } - - return 0; -} - static void skl_tplg_fill_pin_dynamic_val( struct skl_module_pin *mpin, u32 pin_count, u32 value) { @@ -2382,7 +2391,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_MAX_MCPS: case SKL_TKN_U32_OBS: case SKL_TKN_U32_IBS: - ret = skl_tplg_fill_res_tkn(dev, tkn_elem, res, dir, pin_index); + ret = skl_tplg_fill_res_tkn(dev, tkn_elem, res, pin_index, dir); if (ret < 0) return ret; @@ -2488,6 +2497,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PIN_MOD_ID: case SKL_TKN_U32_PIN_INST_ID: + case SKL_TKN_UUID: ret = skl_tplg_fill_pins_info(dev, mconfig, tkn_elem, dir, pin_index); @@ -2550,6 +2560,7 @@ static int skl_tplg_get_tokens(struct device *dev, struct snd_soc_tplg_vendor_value_elem *tkn_elem; int tkn_count = 0, ret; int off = 0, tuple_size = 0; + bool is_module_guid = true; if (block_size <= 0) return -EINVAL; @@ -2565,7 +2576,15 @@ static int skl_tplg_get_tokens(struct device *dev, continue; case SND_SOC_TPLG_TUPLE_TYPE_UUID: - ret = skl_tplg_get_uuid(dev, mconfig, array->uuid); + if (is_module_guid) { + ret = skl_tplg_get_uuid(dev, mconfig->guid, + array->uuid); + is_module_guid = false; + } else { + ret = skl_tplg_get_token(dev, array->value, skl, + mconfig); + } + if (ret < 0) return ret; @@ -3331,6 +3350,7 @@ static struct snd_soc_tplg_ops skl_tplg_ops = { .io_ops = skl_tplg_kcontrol_ops, .io_ops_count = ARRAY_SIZE(skl_tplg_kcontrol_ops), .manifest = skl_manifest_load, + .dai_load = skl_dai_load, }; /* @@ -3404,7 +3424,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { - dev_err(bus->dev, "tplg fw %s load failed with %d\n", + dev_info(bus->dev, "tplg fw %s load failed with %d, falling back to dfw_sst.bin", skl->tplg_name, ret); ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 2717db92036b..b6496513fe55 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -34,7 +34,7 @@ #define MAX_FIXED_DMIC_PARAMS_SIZE 727 /* Maximum number of coefficients up down mixer module */ -#define UP_DOWN_MIXER_MAX_COEFF 6 +#define UP_DOWN_MIXER_MAX_COEFF 8 #define MODULE_MAX_IN_PINS 8 #define MODULE_MAX_OUT_PINS 8 @@ -161,6 +161,7 @@ struct skl_up_down_mixer_cfg { u32 coeff_sel; /* Pass the user coeff in this array */ s32 coeff[UP_DOWN_MIXER_MAX_COEFF]; + u32 ch_map; } __packed; struct skl_algo_cfg { @@ -455,8 +456,8 @@ static inline struct skl *get_skl_ctx(struct device *dev) int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); -int skl_dsp_set_dma_control(struct skl_sst *ctx, - struct skl_module_cfg *mconfig); +int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, + u32 caps_size, u32 node_id); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_platform *platform, @@ -501,4 +502,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); + +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai); #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f94b484abb99..31d8634e8aa1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -28,7 +28,7 @@ #include <linux/firmware.h> #include <linux/delay.h> #include <sound/pcm.h> -#include "../common/sst-acpi.h" +#include <sound/soc-acpi.h> #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> @@ -439,10 +439,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) { struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct platform_device *pdev; - struct sst_acpi_mach *mach = driver_data; + struct snd_soc_acpi_mach *mach = driver_data; int ret; - mach = sst_acpi_find_machine(mach); + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; @@ -462,8 +462,11 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) return -EIO; } - if (mach->pdata) + if (mach->pdata) { + skl->use_tplg_pcm = + ((struct skl_machine_pdata *)mach->pdata)->use_tplg_pcm; dev_set_drvdata(&pdev->dev, mach->pdata); + } skl->i2s_dev = pdev; @@ -875,33 +878,36 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct sst_codecs skl_codecs = { +static struct snd_soc_acpi_codecs skl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs kbl_codecs = { +static struct snd_soc_acpi_codecs kbl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs bxt_codecs = { +static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; -static struct sst_codecs kbl_poppy_codecs = { +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} }; -static struct sst_codecs kbl_5663_5514_codecs = { +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { .num_codecs = 2, .codecs = {"10EC5663", "10EC5514"} }; +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; -static struct sst_acpi_mach sst_skl_devdata[] = { +static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", .drv_name = "skl_alc286s_i2s", @@ -911,7 +917,7 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "INT343B", .drv_name = "skl_n88l25_s4567", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, @@ -919,14 +925,14 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "MX98357A", .drv_name = "skl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, {} }; -static struct sst_acpi_mach sst_bxtp_devdata[] = { +static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", @@ -936,13 +942,13 @@ static struct sst_acpi_mach sst_bxtp_devdata[] = { .id = "DLGS7219", .drv_name = "bxt_da7219_max98357a_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, {} }; -static struct sst_acpi_mach sst_kbl_devdata[] = { +static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", .drv_name = "kbl_alc286s_i2s", @@ -952,7 +958,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "INT343B", .drv_name = "kbl_n88l25_s4567", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -960,7 +966,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98357A", .drv_name = "kbl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -968,7 +974,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_r5514_5663_max", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_5663_5514_codecs, .pdata = &skl_dmic_data }, @@ -976,7 +982,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_rt5663_m98927", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_poppy_codecs, .pdata = &skl_dmic_data }, @@ -989,7 +995,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { {} }; -static struct sst_acpi_mach sst_glk_devdata[] = { +static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", @@ -998,12 +1004,14 @@ static struct sst_acpi_mach sst_glk_devdata[] = { {} }; -static const struct sst_acpi_mach sst_cnl_devdata[] = { +static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { { .id = "INT34C2", .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, }, + {} }; /* PCI IDs */ diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 8d9d6899f761..e00cde8200dd 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -53,6 +53,7 @@ struct skl { struct platform_device *dmic_dev; struct platform_device *i2s_dev; struct snd_soc_platform *platform; + struct snd_soc_dai_driver *dais; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -73,6 +74,7 @@ struct skl { struct skl_debug *debugfs; u8 nr_modules; struct skl_module **modules; + bool use_tplg_pcm; }; #define skl_to_ebus(s) (&(s)->ebus) @@ -85,9 +87,9 @@ struct skl_dma_params { u8 stream_tag; }; -/* to pass dmic data */ struct skl_machine_pdata { u32 dmic_num; + bool use_tplg_pcm; /* use dais and dai links from topology */ }; struct skl_dsp_ops { diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index cf23af159acf..505b0ff03c3b 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -318,7 +318,7 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) } } -struct snd_soc_platform_driver kirkwood_soc_platform = { +const struct snd_soc_platform_driver kirkwood_soc_platform = { .ops = &kirkwood_dma_ops, .pcm_new = kirkwood_dma_new, .pcm_free = kirkwood_dma_free_dma_buffers, diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 90e32a781424..783cb1a4f30e 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -143,6 +143,6 @@ struct kirkwood_dma_data { int burst; }; -extern struct snd_soc_platform_driver kirkwood_soc_platform; +extern const struct snd_soc_platform_driver kirkwood_soc_platform; #endif diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile index 0357b27d29f2..c1eed0d2653b 100644 --- a/sound/soc/mediatek/mt8173/Makefile +++ b/sound/soc/mediatek/mt8173/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # MTK Platform Support obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o # Machine support diff --git a/sound/soc/mxs/Makefile b/sound/soc/mxs/Makefile index 565b5b51e8b7..ab0a9a553702 100644 --- a/sound/soc/mxs/Makefile +++ b/sound/soc/mxs/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # MXS Platform Support snd-soc-mxs-objs := mxs-saif.o snd-soc-mxs-pcm-objs := mxs-pcm.o diff --git a/sound/soc/nuc900/Makefile b/sound/soc/nuc900/Makefile index 7e46c7150316..c7ba2b9549d2 100644 --- a/sound/soc/nuc900/Makefile +++ b/sound/soc/nuc900/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # NUC900 series audio snd-soc-nuc900-pcm-objs := nuc900-pcm.o snd-soc-nuc900-ac97-objs := nuc900-ac97.o diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index db36fbd5d1a0..a6785dc4fc90 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # OMAP Platform Support snd-soc-omap-objs := omap-pcm.o snd-soc-omap-dmic-objs := omap-dmic.o diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 3e9cc4842a1d..8eeac7cab1c1 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -362,6 +362,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->name = devm_kasprintf(dev, GFP_KERNEL, "HDMI %s", dev_name(ad->dssdev)); + if (!card->name) + return -ENOMEM; + card->owner = THIS_MODULE; card->dai_link = devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 2cff67b61dc3..5b265662f04f 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # PXA Platform Support snd-soc-pxa2xx-objs := pxa2xx-pcm.o snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index f49bf02e5ec2..803818aabee9 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -29,21 +29,41 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_warm_reset(ac97); + pxa2xx_ac97_try_warm_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); } static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_cold_reset(ac97); + pxa2xx_ac97_try_cold_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); +} + +static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int ret; + + ret = pxa2xx_ac97_read(ac97->num, reg); + if (ret < 0) + return 0; + else + return (unsigned short)(ret & 0xffff); +} + +static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + int ret; + + ret = pxa2xx_ac97_write(ac97->num, reg, val); } static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_read, - .write = pxa2xx_ac97_write, + .read = pxa2xx_ac97_legacy_read, + .write = pxa2xx_ac97_legacy_write, .warm_reset = pxa2xx_ac97_warm_reset, .reset = pxa2xx_ac97_cold_reset, }; diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 79e5c50a8f71..d5280355c24f 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Platform snd-soc-lpass-cpu-objs := lpass-cpu.o snd-soc-lpass-platform-objs := lpass-platform.o diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index e1945e1772cd..caf71aab8196 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -74,7 +74,6 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) data->i2s_port = cpu_dai->driver->id; runtime->private_data = data; - dma_ch = 0; if (v->alloc_dma_channel) dma_ch = v->alloc_dma_channel(drvdata, dir); else @@ -122,7 +121,6 @@ static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) struct lpass_pcm_data *data; data = runtime->private_data; - v = drvdata->variant; drvdata->substream[data->dma_ch] = NULL; if (v->free_dma_channel) v->free_dma_channel(drvdata, data->dma_ch); diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 105f0e14a4ab..05b078e7b87f 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o snd-soc-rockchip-pdm-objs := rockchip_pdm.o diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 0513fe480353..d64fbbd50544 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -23,6 +23,7 @@ #include <linux/of_gpio.h> #include <linux/delay.h> #include <linux/spi/spi.h> +#include <linux/i2c.h> #include <linux/input.h> #include <sound/core.h> #include <sound/jack.h> @@ -47,18 +48,7 @@ static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), -}; - -static const struct snd_soc_dapm_route rockchip_dapm_routes[] = { - /* Input Lines */ - {"MIC", NULL, "Headset Mic"}, - {"DMIC1L", NULL, "Int Mic"}, - {"DMIC1R", NULL, "Int Mic"}, - - /* Output Lines */ - {"Headphones", NULL, "HPL"}, - {"Headphones", NULL, "HPR"}, - {"Speakers", NULL, "Speaker"}, + SND_SOC_DAPM_LINE("HDMI", NULL), }; static const struct snd_kcontrol_new rockchip_controls[] = { @@ -66,6 +56,7 @@ static const struct snd_kcontrol_new rockchip_controls[] = { SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("HDMI"), }; static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream, @@ -314,8 +305,6 @@ static struct snd_soc_card rockchip_sound_card = { .owner = THIS_MODULE, .dapm_widgets = rockchip_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets), - .dapm_routes = rockchip_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes), .controls = rockchip_controls, .num_controls = ARRAY_SIZE(rockchip_controls), }; @@ -329,15 +318,6 @@ enum { DAILINK_RT5514_DSP, }; -static const char * const dailink_compat[] = { - [DAILINK_CDNDP] = "rockchip,rk3399-cdn-dp", - [DAILINK_DA7219] = "dlg,da7219", - [DAILINK_DMIC] = "dmic-codec", - [DAILINK_MAX98357A] = "maxim,max98357a", - [DAILINK_RT5514] = "realtek,rt5514-i2c", - [DAILINK_RT5514_DSP] = "realtek,rt5514-spi", -}; - static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_CDNDP] = { .name = "DP", @@ -391,13 +371,117 @@ static const struct snd_soc_dai_link rockchip_dais[] = { }, }; +static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = { + /* Output */ + {"HDMI", NULL, "TX"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = { + /* Output */ + {"Headphones", NULL, "HPL"}, + {"Headphones", NULL, "HPR"}, + + /* Input */ + {"MIC", NULL, "Headset Mic"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = { + /* Input */ + {"DMic", NULL, "Int Mic"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = { + /* Output */ + {"Speakers", NULL, "Speaker"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = { + /* Input */ + {"DMIC1L", NULL, "Int Mic"}, + {"DMIC1R", NULL, "Int Mic"}, +}; + +struct rockchip_sound_route { + const struct snd_soc_dapm_route *routes; + int num_routes; +}; + +static const struct rockchip_sound_route rockchip_routes[] = { + [DAILINK_CDNDP] = { + .routes = rockchip_sound_cdndp_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes), + }, + [DAILINK_DA7219] = { + .routes = rockchip_sound_da7219_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes), + }, + [DAILINK_DMIC] = { + .routes = rockchip_sound_dmic_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes), + }, + [DAILINK_MAX98357A] = { + .routes = rockchip_sound_max98357a_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes), + }, + [DAILINK_RT5514] = { + .routes = rockchip_sound_rt5514_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes), + }, + [DAILINK_RT5514_DSP] = {}, +}; + +struct dailink_match_data { + const char *compatible; + struct bus_type *bus_type; +}; + +static const struct dailink_match_data dailink_match[] = { + [DAILINK_CDNDP] = { + .compatible = "rockchip,rk3399-cdn-dp", + }, + [DAILINK_DA7219] = { + .compatible = "dlg,da7219", + }, + [DAILINK_DMIC] = { + .compatible = "dmic-codec", + }, + [DAILINK_MAX98357A] = { + .compatible = "maxim,max98357a", + }, + [DAILINK_RT5514] = { + .compatible = "realtek,rt5514", + .bus_type = &i2c_bus_type, + }, + [DAILINK_RT5514_DSP] = { + .compatible = "realtek,rt5514", + .bus_type = &spi_bus_type, + }, +}; + +static int of_dev_node_match(struct device *dev, void *data) +{ + return dev->of_node == data; +} + static int rockchip_sound_codec_node_match(struct device_node *np_codec) { + struct device *dev; int i; - for (i = 0; i < ARRAY_SIZE(dailink_compat); i++) { - if (of_device_is_compatible(np_codec, dailink_compat[i])) - return i; + for (i = 0; i < ARRAY_SIZE(dailink_match); i++) { + if (!of_device_is_compatible(np_codec, + dailink_match[i].compatible)) + continue; + + if (dailink_match[i].bus_type) { + dev = bus_find_device(dailink_match[i].bus_type, NULL, + np_codec, of_dev_node_match); + if (!dev) + continue; + put_device(dev); + } + + return i; } return -1; } @@ -408,16 +492,28 @@ static int rockchip_sound_of_parse_dais(struct device *dev, struct device_node *np_cpu, *np_cpu0, *np_cpu1; struct device_node *np_codec; struct snd_soc_dai_link *dai; + struct snd_soc_dapm_route *routes; int i, index; + int num_routes; card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais), GFP_KERNEL); if (!card->dai_link) return -ENOMEM; + num_routes = 0; + for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++) + num_routes += rockchip_routes[i].num_routes; + routes = devm_kzalloc(dev, num_routes * sizeof(*routes), + GFP_KERNEL); + if (!routes) + return -ENOMEM; + card->dapm_routes = routes; + np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0); np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1); + card->num_dapm_routes = 0; card->num_links = 0; for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) { np_codec = of_parse_phandle(dev->of_node, @@ -445,6 +541,17 @@ static int rockchip_sound_of_parse_dais(struct device *dev, dai->codec_of_node = np_codec; dai->platform_of_node = np_cpu; dai->cpu_of_node = np_cpu; + + if (card->num_dapm_routes + rockchip_routes[index].num_routes > + num_routes) { + dev_err(dev, "Too many routes\n"); + return -EINVAL; + } + + memcpy(routes + card->num_dapm_routes, + rockchip_routes[index].routes, + rockchip_routes[index].num_routes * sizeof(*routes)); + card->num_dapm_routes += rockchip_routes[index].num_routes; } return 0; diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b6590467fd14..908211e1d6fc 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -692,7 +692,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) i2s_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->mclk); clk_disable_unprepare(i2s->hclk); return 0; diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index b6c2ee358333..030949e1e434 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # S3c24XX Platform Support snd-soc-s3c-dma-objs := dmaengine.o snd-soc-idma-objs := idma.o diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 10a4da06c0a1..233f1c9a4b6c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -552,8 +552,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } ret = clk_prepare_enable(i2s->op_clk); - if (ret) + if (ret) { + clk_put(i2s->op_clk); + i2s->op_clk = NULL; goto err; + } i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); /* Over-ride the other's */ @@ -1096,6 +1099,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, i2s->pdev = pdev; i2s->pri_dai = NULL; i2s->sec_dai = NULL; + i2s->i2s_dai_drv.id = 1; i2s->i2s_dai_drv.symmetric_rates = 1; i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe; i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove; @@ -1108,10 +1112,13 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS; if (!sec) { + i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI; i2s->i2s_dai_drv.capture.channels_min = 1; i2s->i2s_dai_drv.capture.channels_max = 2; i2s->i2s_dai_drv.capture.rates = i2s_dai_data->pcm_rates; i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; + } else { + i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI_SEC; } return i2s; } @@ -1285,6 +1292,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) } } } + quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pri_dai->addr = devm_ioremap_resource(&pdev->dev, res); diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index 21ff24e930db..79781de2f247 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -13,6 +13,9 @@ #ifndef __SND_SOC_SAMSUNG_I2S_H #define __SND_SOC_SAMSUNG_I2S_H +#define SAMSUNG_I2S_DAI "samsung-i2s" +#define SAMSUNG_I2S_DAI_SEC "samsung-i2s-sec" + #define SAMSUNG_I2S_DIV_BCLK 1 #define SAMSUNG_I2S_RCLKSRC_0 0 diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 68698f3d72f9..a55d18703fe7 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -383,6 +383,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { { .name = "WM5110 AIF1", .stream_name = "HiFi Primary", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif1", .ops = &tm2_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -390,6 +391,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { }, { .name = "WM5110 Voice", .stream_name = "Voice call", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif2", .ops = &tm2_aif2_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -398,6 +400,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { }, { .name = "WM5110 BT", .stream_name = "Bluetooth", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif3", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, @@ -436,8 +439,7 @@ static int tm2_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); card->dev = dev; - priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", - GPIOF_OUT_INIT_LOW); + priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpio_mic_bias)) { dev_err(dev, "Failed to get mic bias gpio\n"); return PTR_ERR(priv->gpio_mic_bias); @@ -477,7 +479,6 @@ static int tm2_probe(struct platform_device *pdev) } for (i = 0; i < card->num_links; i++) { - card->dai_link[i].cpu_dai_name = NULL; card->dai_link[i].cpu_name = NULL; card->dai_link[i].platform_name = NULL; card->dai_link[i].codec_of_node = codec_dai_node; diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index aaf3dcd1ee2a..51bd7c81671c 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 ## DMA engines snd-soc-dma-sh7760-objs := dma-sh7760.o obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 6d3c7706d93f..c3aaf4788557 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1932,14 +1932,9 @@ static int fsi_probe(struct platform_device *pdev) core = NULL; if (np) { - const struct of_device_id *of_id; - - of_id = of_match_device(fsi_of_match, &pdev->dev); - if (of_id) { - core = of_id->data; - fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); - fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); - } + core = of_device_get_match_data(&pdev->dev); + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); } else { const struct platform_device_id *id_entry = pdev->id_entry; if (id_entry) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 938baff86ef2..8ddb08714faa 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -44,7 +44,6 @@ struct rsnd_adg { #define LRCLK_ASYNC (1 << 0) #define AUDIO_OUT_48 (1 << 1) -#define adg_mode_flags(adg) (adg->flags) #define for_each_rsnd_clk(pos, adg, i) \ for (i = 0; \ @@ -58,6 +57,13 @@ struct rsnd_adg { i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) +static const char * const clk_name[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", +}; + static u32 rsnd_adg_calculate_rbgx(unsigned long div) { int i, ratio; @@ -280,6 +286,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + struct device *dev = rsnd_priv_to_dev(priv); int id = rsnd_mod_id(ssi_mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; @@ -306,12 +313,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); break; } + + dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); } int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; int sel_table[] = { @@ -321,8 +329,6 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) [CLKI] = 0x0, }; - dev_dbg(dev, "request clock = %d\n", rate); - /* * find suitable clock from * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. @@ -366,8 +372,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_adg_set_ssi_clk(ssi_mod, data); - if (adg_mode_flags(adg) & LRCLK_ASYNC) { - if (adg_mode_flags(adg) & AUDIO_OUT_48) + if (rsnd_flags_has(adg, LRCLK_ASYNC)) { + if (rsnd_flags_has(adg, AUDIO_OUT_48)) ckr = 0x80000000; } else { if (0 == (rate % 8000)) @@ -378,9 +384,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_mod_write(adg_mod, BRRA, adg->rbga); rsnd_mod_write(adg_mod, BRRB, adg->rbgb); - dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", - rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod), - data, rate); + dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", + (ckr) ? 'B' : 'A', + (ckr) ? adg->rbgb_rate_for_48khz : + adg->rbga_rate_for_441khz); return 0; } @@ -409,21 +416,12 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; - static const char * const clk_name[] = { - [CLKA] = "clk_a", - [CLKB] = "clk_b", - [CLKC] = "clk_c", - [CLKI] = "clk_i", - }; int i; for (i = 0; i < CLKMAX; i++) { clk = devm_clk_get(dev, clk_name[i]); adg->clk[i] = IS_ERR(clk) ? NULL : clk; } - - for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); } static void rsnd_adg_get_clkout(struct rsnd_priv *priv, @@ -479,10 +477,10 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, } if (req_rate[0] % 48000 == 0) - adg->flags = AUDIO_OUT_48; + rsnd_flags_set(adg, AUDIO_OUT_48); if (of_get_property(np, "clkout-lr-asynchronous", NULL)) - adg->flags = LRCLK_ASYNC; + rsnd_flags_set(adg, LRCLK_ASYNC); /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC @@ -512,7 +510,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, adg->rbga_rate_for_441khz = rate / div; ckr |= brg_table[i] << 20; if (req_441kHz_rate && - !(adg_mode_flags(adg) & AUDIO_OUT_48)) + !rsnd_flags_has(adg, AUDIO_OUT_48)) parent_clk_name = __clk_get_name(clk); } } @@ -528,7 +526,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, adg->rbgb_rate_for_48khz = rate / div; ckr |= brg_table[i] << 16; if (req_48kHz_rate && - (adg_mode_flags(adg) & AUDIO_OUT_48)) + rsnd_flags_has(adg, AUDIO_OUT_48)) parent_clk_name = __clk_get_name(clk); } } @@ -572,12 +570,35 @@ rsnd_adg_get_clkout_end: adg->ckr = ckr; adg->rbga = rbga; adg->rbgb = rbgb; +} + +#ifdef DEBUG +static void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + dev_dbg(dev, "%s : %p : %ld\n", + clk_name[i], clk, clk_get_rate(clk)); - for_each_rsnd_clkout(clk, adg, i) - dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", - ckr, rbga, rbgb); + adg->ckr, adg->rbga, adg->rbgb); + dev_dbg(dev, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz); + dev_dbg(dev, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz); + + /* + * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() + * by BRGCKR::BRGCKR_31 + */ + for_each_rsnd_clkout(clk, adg, i) + dev_dbg(dev, "clkout %d : %p : %ld\n", i, + clk, clk_get_rate(clk)); } +#else +#define rsnd_adg_clk_dbg_info(priv, adg) +#endif int rsnd_adg_probe(struct rsnd_priv *priv) { @@ -596,6 +617,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); + rsnd_adg_clk_dbg_info(priv, adg); priv->adg = adg; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 107133297e8d..c70eb2097816 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -121,14 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) } } -char *rsnd_mod_name(struct rsnd_mod *mod) -{ - if (!mod || !mod->ops) - return "unknown"; - - return mod->ops->name; -} - struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -172,8 +164,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, void rsnd_mod_quit(struct rsnd_mod *mod) { - if (mod->clk) - clk_unprepare(mod->clk); + clk_unprepare(mod->clk); mod->clk = NULL; } @@ -200,7 +191,10 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, int rsnd_io_is_working(struct rsnd_dai_stream *io) { /* see rsnd_dai_stream_init/quit() */ - return !!io->substream; + if (io->substream) + return snd_pcm_running(io->substream); + + return 0; } int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) @@ -407,11 +401,9 @@ struct rsnd_mod *rsnd_mod_next(int *iterator, for (; *iterator < max; (*iterator)++) { type = (array) ? array[*iterator] : *iterator; - mod = io->mod[type]; - if (!mod) - continue; - - return mod; + mod = rsnd_io_to_mod(io, type); + if (mod) + return mod; } return NULL; @@ -1242,6 +1234,33 @@ struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg) return &cfg->cfg; } +const char * const volume_ramp_rate[] = { + "128 dB/1 step", /* 00000 */ + "64 dB/1 step", /* 00001 */ + "32 dB/1 step", /* 00010 */ + "16 dB/1 step", /* 00011 */ + "8 dB/1 step", /* 00100 */ + "4 dB/1 step", /* 00101 */ + "2 dB/1 step", /* 00110 */ + "1 dB/1 step", /* 00111 */ + "0.5 dB/1 step", /* 01000 */ + "0.25 dB/1 step", /* 01001 */ + "0.125 dB/1 step", /* 01010 = VOLUME_RAMP_MAX_MIX */ + "0.125 dB/2 steps", /* 01011 */ + "0.125 dB/4 steps", /* 01100 */ + "0.125 dB/8 steps", /* 01101 */ + "0.125 dB/16 steps", /* 01110 */ + "0.125 dB/32 steps", /* 01111 */ + "0.125 dB/64 steps", /* 10000 */ + "0.125 dB/128 steps", /* 10001 */ + "0.125 dB/256 steps", /* 10010 */ + "0.125 dB/512 steps", /* 10011 */ + "0.125 dB/1024 steps", /* 10100 */ + "0.125 dB/2048 steps", /* 10101 */ + "0.125 dB/4096 steps", /* 10110 */ + "0.125 dB/8192 steps", /* 10111 = VOLUME_RAMP_MAX_DVC */ +}; + int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index e7f53f44165d..d201d551866d 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -81,8 +81,11 @@ struct rsnd_ctu { struct rsnd_kctrl_cfg_m sv3; struct rsnd_kctrl_cfg_s reset; int channels; + u32 flags; }; +#define KCTRL_INITIALIZED (1 << 0) + #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) #define for_each_rsnd_ctu(pos, priv, i) \ for ((i) = 0; \ @@ -130,7 +133,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, int i; for (i = 0; i < RSND_MAX_CHANNELS; i++) { - u32 val = ctu->pass.val[i]; + u32 val = rsnd_kctrl_valm(ctu->pass, i); cpmdr |= val << (28 - (i * 4)); @@ -147,44 +150,44 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, CTU_SCMDR, scmdr); if (scmdr > 0) { - rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); - rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); - rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); - rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); - rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); - rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); - rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); - rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); + rsnd_mod_write(mod, CTU_SV00R, rsnd_kctrl_valm(ctu->sv0, 0)); + rsnd_mod_write(mod, CTU_SV01R, rsnd_kctrl_valm(ctu->sv0, 1)); + rsnd_mod_write(mod, CTU_SV02R, rsnd_kctrl_valm(ctu->sv0, 2)); + rsnd_mod_write(mod, CTU_SV03R, rsnd_kctrl_valm(ctu->sv0, 3)); + rsnd_mod_write(mod, CTU_SV04R, rsnd_kctrl_valm(ctu->sv0, 4)); + rsnd_mod_write(mod, CTU_SV05R, rsnd_kctrl_valm(ctu->sv0, 5)); + rsnd_mod_write(mod, CTU_SV06R, rsnd_kctrl_valm(ctu->sv0, 6)); + rsnd_mod_write(mod, CTU_SV07R, rsnd_kctrl_valm(ctu->sv0, 7)); } if (scmdr > 1) { - rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); - rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); - rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); - rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); - rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); - rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); - rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); - rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); + rsnd_mod_write(mod, CTU_SV10R, rsnd_kctrl_valm(ctu->sv1, 0)); + rsnd_mod_write(mod, CTU_SV11R, rsnd_kctrl_valm(ctu->sv1, 1)); + rsnd_mod_write(mod, CTU_SV12R, rsnd_kctrl_valm(ctu->sv1, 2)); + rsnd_mod_write(mod, CTU_SV13R, rsnd_kctrl_valm(ctu->sv1, 3)); + rsnd_mod_write(mod, CTU_SV14R, rsnd_kctrl_valm(ctu->sv1, 4)); + rsnd_mod_write(mod, CTU_SV15R, rsnd_kctrl_valm(ctu->sv1, 5)); + rsnd_mod_write(mod, CTU_SV16R, rsnd_kctrl_valm(ctu->sv1, 6)); + rsnd_mod_write(mod, CTU_SV17R, rsnd_kctrl_valm(ctu->sv1, 7)); } if (scmdr > 2) { - rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); - rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); - rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); - rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); - rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); - rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); - rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); - rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); + rsnd_mod_write(mod, CTU_SV20R, rsnd_kctrl_valm(ctu->sv2, 0)); + rsnd_mod_write(mod, CTU_SV21R, rsnd_kctrl_valm(ctu->sv2, 1)); + rsnd_mod_write(mod, CTU_SV22R, rsnd_kctrl_valm(ctu->sv2, 2)); + rsnd_mod_write(mod, CTU_SV23R, rsnd_kctrl_valm(ctu->sv2, 3)); + rsnd_mod_write(mod, CTU_SV24R, rsnd_kctrl_valm(ctu->sv2, 4)); + rsnd_mod_write(mod, CTU_SV25R, rsnd_kctrl_valm(ctu->sv2, 5)); + rsnd_mod_write(mod, CTU_SV26R, rsnd_kctrl_valm(ctu->sv2, 6)); + rsnd_mod_write(mod, CTU_SV27R, rsnd_kctrl_valm(ctu->sv2, 7)); } if (scmdr > 3) { - rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); - rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); - rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); - rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); - rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); - rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); - rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); - rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); + rsnd_mod_write(mod, CTU_SV30R, rsnd_kctrl_valm(ctu->sv3, 0)); + rsnd_mod_write(mod, CTU_SV31R, rsnd_kctrl_valm(ctu->sv3, 1)); + rsnd_mod_write(mod, CTU_SV32R, rsnd_kctrl_valm(ctu->sv3, 2)); + rsnd_mod_write(mod, CTU_SV33R, rsnd_kctrl_valm(ctu->sv3, 3)); + rsnd_mod_write(mod, CTU_SV34R, rsnd_kctrl_valm(ctu->sv3, 4)); + rsnd_mod_write(mod, CTU_SV35R, rsnd_kctrl_valm(ctu->sv3, 5)); + rsnd_mod_write(mod, CTU_SV36R, rsnd_kctrl_valm(ctu->sv3, 6)); + rsnd_mod_write(mod, CTU_SV37R, rsnd_kctrl_valm(ctu->sv3, 7)); } rsnd_mod_write(mod, CTU_CTUIR, 0); @@ -196,17 +199,17 @@ static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); int i; - if (!ctu->reset.val) + if (!rsnd_kctrl_vals(ctu->reset)) return; for (i = 0; i < RSND_MAX_CHANNELS; i++) { - ctu->pass.val[i] = 0; - ctu->sv0.val[i] = 0; - ctu->sv1.val[i] = 0; - ctu->sv2.val[i] = 0; - ctu->sv3.val[i] = 0; + rsnd_kctrl_valm(ctu->pass, i) = 0; + rsnd_kctrl_valm(ctu->sv0, i) = 0; + rsnd_kctrl_valm(ctu->sv1, i) = 0; + rsnd_kctrl_valm(ctu->sv2, i) = 0; + rsnd_kctrl_valm(ctu->sv3, i) = 0; } - ctu->reset.val = 0; + rsnd_kctrl_vals(ctu->reset) = 0; } static int rsnd_ctu_init(struct rsnd_mod *mod, @@ -277,6 +280,9 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); int ret; + if (rsnd_flags_has(ctu, KCTRL_INITIALIZED)) + return 0; + /* CTU Pass */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", rsnd_kctrl_accept_anytime, @@ -326,6 +332,8 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, rsnd_ctu_value_reset, &ctu->reset, 1); + rsnd_flags_set(ctu, KCTRL_INITIALIZED); + return ret; } diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 041ec1080d52..fd557abfe390 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -60,6 +60,14 @@ struct rsnd_dma_ctrl { #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) +/* for DEBUG */ +static struct rsnd_mod_ops mem_ops = { + .name = "mem", +}; + +static struct rsnd_mod mem = { +}; + /* * Audio DMAC */ @@ -211,11 +219,9 @@ static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod, dma->mod_from, dma->mod_to); if (IS_ERR_OR_NULL(dmaen->chan)) { - int ret = PTR_ERR(dmaen->chan); - dmaen->chan = NULL; dev_err(dev, "can't get dma channel\n"); - return ret; + return -EIO; } return 0; @@ -747,20 +753,22 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, rsnd_mod_name(this), rsnd_mod_id(this)); for (i = 0; i <= idx; i++) { dev_dbg(dev, " %s[%d]%s\n", - rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), - (mod[i] == *mod_from) ? " from" : - (mod[i] == *mod_to) ? " to" : ""); + rsnd_mod_name(mod[i] ? mod[i] : &mem), + rsnd_mod_id (mod[i] ? mod[i] : &mem), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); } } -int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - struct rsnd_mod **dma_mod) +static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) { struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma *dma; struct rsnd_mod_ops *ops; enum rsnd_mod_type type; int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, @@ -800,40 +808,47 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, type = RSND_MOD_AUDMA; } - if (!(*dma_mod)) { - struct rsnd_dma *dma; + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; + *dma_mod = rsnd_mod_get(dma); - *dma_mod = rsnd_mod_get(dma); + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; - ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, - rsnd_mod_get_status, type, dma_id); - if (ret < 0) - return ret; + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from ? mod_from : &mem), + rsnd_mod_id (mod_from ? mod_from : &mem), + rsnd_mod_name(mod_to ? mod_to : &mem), + rsnd_mod_id (mod_to ? mod_to : &mem)); - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + ret = attach(io, dma, mod_from, mod_to); + if (ret < 0) + return ret; + + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + dma->mod_from = mod_from; + dma->mod_to = mod_to; + + return 0; +} + +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) +{ + if (!(*dma_mod)) { + int ret = rsnd_dma_alloc(io, mod, dma_mod); - ret = attach(io, dma, mod_from, mod_to); if (ret < 0) return ret; - - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - dma->mod_from = mod_from; - dma->mod_to = mod_to; } - ret = rsnd_dai_connect(*dma_mod, io, type); - if (ret < 0) - return ret; - - return 0; + return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type); } int rsnd_dma_probe(struct rsnd_priv *priv) @@ -866,5 +881,6 @@ int rsnd_dma_probe(struct rsnd_priv *priv) priv->dma = dmac; - return 0; + /* dummy mem mod for debug */ + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 1743ade3cc55..dbe54f024d68 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -44,8 +44,11 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ + u32 flags; }; +#define KCTRL_INITIALIZED (1 << 0) + #define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr) @@ -58,33 +61,6 @@ struct rsnd_dvc { ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ i++) -static const char * const dvc_ramp_rate[] = { - "128 dB/1 step", /* 00000 */ - "64 dB/1 step", /* 00001 */ - "32 dB/1 step", /* 00010 */ - "16 dB/1 step", /* 00011 */ - "8 dB/1 step", /* 00100 */ - "4 dB/1 step", /* 00101 */ - "2 dB/1 step", /* 00110 */ - "1 dB/1 step", /* 00111 */ - "0.5 dB/1 step", /* 01000 */ - "0.25 dB/1 step", /* 01001 */ - "0.125 dB/1 step", /* 01010 */ - "0.125 dB/2 steps", /* 01011 */ - "0.125 dB/4 steps", /* 01100 */ - "0.125 dB/8 steps", /* 01101 */ - "0.125 dB/16 steps", /* 01110 */ - "0.125 dB/32 steps", /* 01111 */ - "0.125 dB/64 steps", /* 10000 */ - "0.125 dB/128 steps", /* 10001 */ - "0.125 dB/256 steps", /* 10010 */ - "0.125 dB/512 steps", /* 10011 */ - "0.125 dB/1024 steps", /* 10100 */ - "0.125 dB/2048 steps", /* 10101 */ - "0.125 dB/4096 steps", /* 10110 */ - "0.125 dB/8192 steps", /* 10111 */ -}; - static void rsnd_dvc_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, DVC_SWRSR, 0); @@ -97,8 +73,9 @@ static void rsnd_dvc_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, DVC_SWRSR, 0); } -#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val) -#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13)) +#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \ + rsnd_kctrl_vals(dvc->rdown)) +#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13)) static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) @@ -108,12 +85,12 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, int i; /* Enable Ramp */ - if (dvc->ren.val) + if (rsnd_kctrl_vals(dvc->ren)) for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = dvc->volume.cfg.max; + val[i] = rsnd_kctrl_max(dvc->volume); else for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = dvc->volume.val[i]; + val[i] = rsnd_kctrl_valm(dvc->volume, i); /* Enable Digital Volume */ rsnd_mod_write(mod, DVC_VOL0R, val[0]); @@ -143,7 +120,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, dvucr |= 0x101; /* Enable Ramp */ - if (dvc->ren.val) { + if (rsnd_kctrl_vals(dvc->ren)) { dvucr |= 0x10; /* @@ -185,10 +162,10 @@ static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, u32 vrdbr = 0; int i; - for (i = 0; i < dvc->mute.cfg.size; i++) - zcmcr |= (!!dvc->mute.cfg.val[i]) << i; + for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++) + zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i; - if (dvc->ren.val) { + if (rsnd_kctrl_vals(dvc->ren)) { vrpdr = rsnd_dvc_get_vrpdr(dvc); vrdbr = rsnd_dvc_get_vrdbr(dvc); } @@ -254,6 +231,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int channels = rsnd_rdai_channels_get(rdai); int ret; + if (rsnd_flags_has(dvc, KCTRL_INITIALIZED)) + return 0; + /* Volume */ ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? @@ -292,7 +272,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rup, - dvc_ramp_rate); + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); if (ret < 0) return ret; @@ -302,11 +283,14 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rdown, - dvc_ramp_rate); + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); if (ret < 0) return ret; + rsnd_flags_set(dvc, KCTRL_INITIALIZED); + return 0; } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 6c4826c189a4..7998380766f6 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -7,6 +7,33 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * CTUn MIXn + * +------+ +------+ + * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| -> + * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| -> + * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| -> + * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| -> + * +------+ +------+ + * + * ex) + * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>; + * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; + * + * MIX Volume + * amixer set "MIX",0 100% // DAI0 Volume + * amixer set "MIX",1 100% // DAI1 Volume + * + * Volume Ramp + * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step" + * amixer set "MIX Ramp Down Rate" "4 dB/1 step" + * amixer set "MIX Ramp" on + * aplay xxx.wav & + * amixer set "MIX",0 80% // DAI0 Volume Down + * amixer set "MIX",1 100% // DAI1 Volume Up + */ + #include "rsnd.h" #define MIX_NAME_SIZE 16 @@ -14,8 +41,27 @@ struct rsnd_mix { struct rsnd_mod mod; + struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */ + struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */ + struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */ + struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */ + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */ + u32 flags; }; +#define ONCE_KCTRL_INITIALIZED (1 << 0) +#define HAS_VOLA (1 << 1) +#define HAS_VOLB (1 << 2) +#define HAS_VOLC (1 << 3) +#define HAS_VOLD (1 << 4) + +#define VOL_MAX 0x3ff + +#define rsnd_mod_to_mix(_mod) \ + container_of((_mod), struct rsnd_mix, mod) + #define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) #define rsnd_mix_nr(priv) ((priv)->mix_nr) #define for_each_rsnd_mix(pos, priv, i) \ @@ -36,26 +82,43 @@ static void rsnd_mix_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, MIX_SWRSR, 0); } +#define rsnd_mix_get_vol(mix, X) \ + rsnd_flags_has(mix, HAS_VOL##X) ? \ + (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0 static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { - rsnd_mod_write(mod, MIX_MDBAR, 0); - rsnd_mod_write(mod, MIX_MDBBR, 0); - rsnd_mod_write(mod, MIX_MDBCR, 0); - rsnd_mod_write(mod, MIX_MDBDR, 0); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + u32 volA = rsnd_mix_get_vol(mix, A); + u32 volB = rsnd_mix_get_vol(mix, B); + u32 volC = rsnd_mix_get_vol(mix, C); + u32 volD = rsnd_mix_get_vol(mix, D); + + dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", + volA, volB, volC, volD); + + rsnd_mod_write(mod, MIX_MDBAR, volA); + rsnd_mod_write(mod, MIX_MDBBR, volB); + rsnd_mod_write(mod, MIX_MDBCR, volC); + rsnd_mod_write(mod, MIX_MDBDR, volD); } static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ - rsnd_mod_write(mod, MIX_MIXMR, 0); - rsnd_mod_write(mod, MIX_MVPDR, 0); + rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); + rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | + rsnd_kctrl_vals(mix->rdw)); /* common volume parameter */ rsnd_mix_volume_parameter(io, mod); @@ -109,11 +172,94 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, return 0; } +static int rsnd_mix_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct rsnd_kctrl_cfg_s *volume; + int ret; + + switch (rsnd_mod_id(src_mod)) { + case 3: + case 6: /* MDBAR */ + volume = &mix->volumeA; + rsnd_flags_set(mix, HAS_VOLA); + break; + case 4: + case 9: /* MDBBR */ + volume = &mix->volumeB; + rsnd_flags_set(mix, HAS_VOLB); + break; + case 0: + case 1: /* MDBCR */ + volume = &mix->volumeC; + rsnd_flags_set(mix, HAS_VOLC); + break; + case 2: + case 5: /* MDBDR */ + volume = &mix->volumeD; + rsnd_flags_set(mix, HAS_VOLD); + break; + default: + dev_err(dev, "unknown SRC is connected\n"); + return -EINVAL; + } + + /* Volume */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Playback Volume", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + volume, VOL_MAX); + if (ret < 0) + return ret; + rsnd_kctrl_vals(*volume) = VOL_MAX; + + if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED)) + return ret; + + /* Ramp */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Ramp Switch", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->ren, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Up Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rup, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Down Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rdw, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + + rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED); + + return ret; +} + static struct rsnd_mod_ops rsnd_mix_ops = { .name = MIX_NAME, .probe = rsnd_mix_probe_, .init = rsnd_mix_init, .quit = rsnd_mix_quit, + .pcm_new = rsnd_mix_pcm_new, }; struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c5de71f2dc8c..57cd2bc773c2 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -355,8 +355,9 @@ struct rsnd_mod { #define __rsnd_mod_call_nolock_start 0 #define __rsnd_mod_call_nolock_stop 1 -#define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) +#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_name(mod) ((mod)->ops->name) +#define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) @@ -371,7 +372,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); -char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod); void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -601,6 +601,10 @@ struct rsnd_priv { #define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) +#define rsnd_flags_has(p, f) ((p)->flags & (f)) +#define rsnd_flags_set(p, f) ((p)->flags |= (f)) +#define rsnd_flags_del(p, f) ((p)->flags &= ~(f)) + /* * rsnd_kctrl */ @@ -627,6 +631,10 @@ struct rsnd_kctrl_cfg_s { struct rsnd_kctrl_cfg cfg; u32 val; }; +#define rsnd_kctrl_size(x) ((x).cfg.size) +#define rsnd_kctrl_max(x) ((x).cfg.max) +#define rsnd_kctrl_valm(x, i) ((x).val[i]) /* = (x).cfg.val[i] */ +#define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); @@ -652,9 +660,13 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ NULL, 1, max) -#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts) \ +#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ - texts, 1, ARRAY_SIZE(texts)) + texts, 1, size) + +extern const char * const volume_ramp_rate[]; +#define VOLUME_RAMP_MAX_DVC (0x17 + 1) +#define VOLUME_RAMP_MAX_MIX (0x0a + 1) /* * R-Car SSI diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fffc07e72627..fece1e5f582f 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -101,9 +101,6 @@ struct rsnd_ssi { #define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_flags_has(p, f) ((p)->flags & f) -#define rsnd_ssi_flags_set(p, f) ((p)->flags |= f) -#define rsnd_ssi_flags_del(p, f) ((p)->flags = ((p)->flags & ~f)) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) #define rsnd_ssi_is_multi_slave(mod, io) \ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) @@ -116,10 +113,10 @@ int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI0)) + if (rsnd_flags_has(ssi, RSND_SSI_HDMI0)) return RSND_SSI_HDMI_PORT0; - if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI1)) + if (rsnd_flags_has(ssi, RSND_SSI_HDMI1)) return RSND_SSI_HDMI_PORT1; return 0; @@ -134,7 +131,7 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) if (!rsnd_ssi_is_dma_mode(mod)) return 0; - if (!(rsnd_ssi_flags_has(ssi, RSND_SSI_NO_BUSIF))) + if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF))) use_busif = 1; if (rsnd_io_to_mod_src(io)) use_busif = 1; @@ -198,10 +195,15 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) { struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + u32 mods; - return rsnd_ssi_multi_slaves_runtime(io) | - 1 << rsnd_mod_id(ssi_mod) | - 1 << rsnd_mod_id(ssi_parent_mod); + mods = rsnd_ssi_multi_slaves_runtime(io) | + 1 << rsnd_mod_id(ssi_mod); + + if (ssi_parent_mod) + mods |= 1 << rsnd_mod_id(ssi_parent_mod); + + return mods; } u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) @@ -601,15 +603,18 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, if (rsnd_ssi_is_parent(mod, io)) return 0; - /* - * disable all IRQ, - * and, wait all data was sent - */ cr = ssi->cr_own | ssi->cr_clk; - rsnd_mod_write(mod, SSICR, cr | EN); - rsnd_ssi_status_check(mod, DIRQ); + /* + * disable all IRQ, + * Playback: Wait all data was sent + * Capture: It might not receave data. Do nothing + */ + if (rsnd_io_is_play(io)) { + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); + } /* * disable SSI, @@ -793,13 +798,13 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, * But it don't need to call request_irq() many times. * Let's control it by RSND_SSI_PROBED flag. */ - if (!rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) { + if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) { ret = request_irq(ssi->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), mod); - rsnd_ssi_flags_set(ssi, RSND_SSI_PROBED); + rsnd_flags_set(ssi, RSND_SSI_PROBED); } return ret; @@ -817,10 +822,10 @@ static int rsnd_ssi_common_remove(struct rsnd_mod *mod, return 0; /* PIO will request IRQ again */ - if (rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) { + if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) { free_irq(ssi->irq, mod); - rsnd_ssi_flags_del(ssi, RSND_SSI_PROBED); + rsnd_flags_del(ssi, RSND_SSI_PROBED); } return 0; @@ -1003,13 +1008,13 @@ static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, ssi = rsnd_mod_to_ssi(mod); if (strstr(remote_ep->full_name, "hdmi0")) { - rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI0); + rsnd_flags_set(ssi, RSND_SSI_HDMI0); dev_dbg(dev, "%s[%d] connected to HDMI0\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); } if (strstr(remote_ep->full_name, "hdmi1")) { - rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI1); + rsnd_flags_set(ssi, RSND_SSI_HDMI1); dev_dbg(dev, "%s[%d] connected to HDMI1\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); } @@ -1042,7 +1047,7 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - return !!(rsnd_ssi_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); + return !!(rsnd_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); } static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, @@ -1112,6 +1117,9 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) i = 0; for_each_child_of_node(node, np) { + if (!of_device_is_available(np)) + goto skip; + ssi = rsnd_ssi_get(priv, i); snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", @@ -1125,10 +1133,10 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) } if (of_get_property(np, "shared-pin", NULL)) - rsnd_ssi_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); + rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); if (of_get_property(np, "no-busif", NULL)) - rsnd_ssi_flags_set(ssi, RSND_SSI_NO_BUSIF); + rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF); ssi->irq = irq_of_parse_and_map(np, 0); if (!ssi->irq) { @@ -1148,7 +1156,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) of_node_put(np); goto rsnd_ssi_probe_done; } - +skip: i++; } diff --git a/sound/soc/sirf/Makefile b/sound/soc/sirf/Makefile index dd917f20f12f..16ed11965ff9 100644 --- a/sound/soc/sirf/Makefile +++ b/sound/soc/sirf/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 snd-soc-sirf-audio-objs := sirf-audio.o snd-soc-sirf-audio-port-objs := sirf-audio-port.o snd-soc-sirf-usp-objs := sirf-usp.o diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/soc-acpi.c index 56d26f36a3cb..f21df28bc28e 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/soc-acpi.c @@ -1,5 +1,5 @@ /* - * sst_match_apci.c - SST (LPE) match for ACPI enumeration. + * soc-apci.c - support for ACPI enumeration. * * Copyright (c) 2013-15, Intel Corporation. * @@ -14,9 +14,9 @@ * more details. */ -#include "sst-acpi.h" +#include <sound/soc-acpi.h> -static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, +static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level, void *context, void **ret) { struct acpi_device *adev; @@ -34,12 +34,12 @@ static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, return AE_OK; } -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) { const char *name = NULL; acpi_status status; - status = acpi_get_devices(hid, sst_acpi_find_name, NULL, + status = acpi_get_devices(hid, snd_soc_acpi_find_name, NULL, (void **)&name); if (ACPI_FAILURE(status) || name[0] == '\0') @@ -47,9 +47,9 @@ const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) return name; } -EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid); -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, +static acpi_status snd_soc_acpi_mach_match(acpi_handle handle, u32 level, void *context, void **ret) { unsigned long long sta; @@ -63,26 +63,27 @@ static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, return AE_OK; } -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) +bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) { acpi_status status; bool found = false; - status = acpi_get_devices(hid, sst_acpi_mach_match, &found, NULL); + status = acpi_get_devices(hid, snd_soc_acpi_mach_match, &found, NULL); if (ACPI_FAILURE(status)) return false; return found; } -EXPORT_SYMBOL_GPL(sst_acpi_check_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_check_hid); -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) { - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; for (mach = machines; mach->id[0]; mach++) { - if (sst_acpi_check_hid(mach->id) == true) { + if (snd_soc_acpi_check_hid(mach->id) == true) { if (mach->machine_quirk == NULL) return mach; @@ -92,14 +93,14 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) } return NULL; } -EXPORT_SYMBOL_GPL(sst_acpi_find_machine); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine); -static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, - void *context, void **ret) +static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level, + void *context, void **ret) { struct acpi_device *adev; acpi_status status = AE_OK; - struct sst_acpi_package_context *pkg_ctx = context; + struct snd_soc_acpi_package_context *pkg_ctx = context; pkg_ctx->data_valid = false; @@ -137,37 +138,38 @@ static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, return AE_OK; } -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) +bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx) { acpi_status status; - status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL); + status = acpi_get_devices(hid, snd_soc_acpi_find_package, ctx, NULL); if (ACPI_FAILURE(status) || !ctx->data_valid) return false; return true; } -EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid); -struct sst_acpi_mach *sst_acpi_codec_list(void *arg) +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) { - struct sst_acpi_mach *mach = arg; - struct sst_codecs *codec_list = (struct sst_codecs *) mach->quirk_data; + struct snd_soc_acpi_mach *mach = arg; + struct snd_soc_acpi_codecs *codec_list = + (struct snd_soc_acpi_codecs *) mach->quirk_data; int i; if (mach->quirk_data == NULL) return mach; for (i = 0; i < codec_list->num_codecs; i++) { - if (sst_acpi_check_hid(codec_list->codecs[i]) != true) + if (snd_soc_acpi_check_hid(codec_list->codecs[i]) != true) return NULL; } return mach; } -EXPORT_SYMBOL_GPL(sst_acpi_codec_list); +EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel Common ACPI Match module"); +MODULE_DESCRIPTION("ALSA SoC ACPI module"); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 2cb8d3b55fbc..d9b1e6417fb9 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -30,8 +30,10 @@ static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -44,7 +46,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -53,6 +55,27 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { ret = rtd->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -68,7 +91,21 @@ static int soc_compr_open(struct snd_compr_stream *cstream) return 0; machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -84,11 +121,13 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; - int ret = 0; + int ret = 0, __ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -107,7 +146,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -116,6 +155,27 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -167,7 +227,21 @@ fe_err: if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -210,6 +284,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; int stream; @@ -235,7 +311,21 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -267,6 +357,8 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; int stream, ret; @@ -304,9 +396,23 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); @@ -319,18 +425,38 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); @@ -353,16 +479,36 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || cmd == SND_COMPR_TRIGGER_DRAIN) { - if (platform->driver->compr_ops && + if (platform && + platform->driver->compr_ops && platform->driver->compr_ops->trigger) return platform->driver->compr_ops->trigger(cstream, cmd); + + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + return ret; } if (cstream->direction == SND_COMPRESS_PLAYBACK) @@ -379,12 +525,30 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ret = dpcm_be_dai_trigger(fe, stream, cmd); @@ -415,8 +579,10 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -432,12 +598,30 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto err; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -471,8 +655,10 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -487,12 +673,30 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -531,8 +735,10 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -542,8 +748,27 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_params) { ret = platform->driver->compr_ops->get_params(cstream, params); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_params) + continue; + + __ret = component->driver->compr_ops->get_params(cstream, params); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -555,13 +780,35 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_caps) { ret = platform->driver->compr_ops->get_caps(cstream, caps); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_caps) + continue; + + __ret = component->driver->compr_ops->get_caps(cstream, caps); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -571,13 +818,35 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) { ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_codec_caps) + continue; + + __ret = component->driver->compr_ops->get_codec_caps(cstream, codec); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -586,8 +855,10 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -597,8 +868,27 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->ack) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->ack) { ret = platform->driver->compr_ops->ack(cstream, bytes); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->ack) + continue; + + __ret = component->driver->compr_ops->ack(cstream, bytes); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -610,7 +900,9 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -618,9 +910,29 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->pointer) { ret = platform->driver->compr_ops->pointer(cstream, tstamp); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->pointer) + continue; + __ret = component->driver->compr_ops->pointer(cstream, tstamp); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -630,13 +942,34 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) { ret = platform->driver->compr_ops->copy(cstream, buf, count); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + __ret = component->driver->compr_ops->copy(cstream, buf, count); + if (__ret < 0) + ret = __ret; + } +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -646,8 +979,10 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); @@ -655,8 +990,27 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) { ret = platform->driver->compr_ops->set_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_metadata) + continue; + + __ret = component->driver->compr_ops->set_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -666,8 +1020,10 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); @@ -675,8 +1031,27 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) { ret = platform->driver->compr_ops->get_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_metadata) + continue; + + __ret = component->driver->compr_ops->get_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -723,6 +1098,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -798,9 +1175,25 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } + /* Add copy callback for not memory mapped DSPs */ - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + compr->ops->copy = soc_compr_copy; + } + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fee4b0ef5566..c0edac80df34 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -614,6 +614,8 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); +static const struct snd_soc_ops null_snd_soc_ops; + static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { @@ -626,6 +628,9 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( INIT_LIST_HEAD(&rtd->component_list); rtd->card = card; rtd->dai_link = dai_link; + if (!rtd->dai_link->ops) + rtd->dai_link->ops = &null_snd_soc_ops; + rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * dai_link->num_codecs, GFP_KERNEL); @@ -639,8 +644,7 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) { - if (rtd && rtd->codec_dais) - kfree(rtd->codec_dais); + kfree(rtd->codec_dais); snd_soc_rtdcom_del_all(rtd); kfree(rtd); } @@ -2632,7 +2636,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - if (dai->driver && dai->driver->ops->set_sysclk) + if (dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); return snd_soc_component_set_sysclk(dai->component, clk_id, 0, @@ -2700,7 +2704,7 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk); int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { - if (dai->driver && dai->driver->ops->set_clkdiv) + if (dai->driver->ops->set_clkdiv) return dai->driver->ops->set_clkdiv(dai, div_id, div); else return -EINVAL; @@ -2720,7 +2724,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - if (dai->driver && dai->driver->ops->set_pll) + if (dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); @@ -2786,7 +2790,7 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_pll); */ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { - if (dai->driver && dai->driver->ops->set_bclk_ratio) + if (dai->driver->ops->set_bclk_ratio) return dai->driver->ops->set_bclk_ratio(dai, ratio); else return -EINVAL; @@ -2796,7 +2800,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI - * @fmt: SND_SOC_DAIFMT_ format value. + * @fmt: SND_SOC_DAIFMT_* format value. * * Configures the DAI hardware format and clocking. */ @@ -2860,7 +2864,7 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - if (dai->driver && dai->driver->ops->xlate_tdm_slot_mask) + if (dai->driver->ops->xlate_tdm_slot_mask) dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); else @@ -2869,7 +2873,7 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, dai->tx_mask = tx_mask; dai->rx_mask = rx_mask; - if (dai->driver && dai->driver->ops->set_tdm_slot) + if (dai->driver->ops->set_tdm_slot) return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); else @@ -2893,7 +2897,7 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) { - if (dai->driver && dai->driver->ops->set_channel_map) + if (dai->driver->ops->set_channel_map) return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, rx_num, rx_slot); else @@ -2910,7 +2914,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); */ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) { - if (dai->driver && dai->driver->ops->set_tristate) + if (dai->driver->ops->set_tristate) return dai->driver->ops->set_tristate(dai, tristate); else return -EINVAL; @@ -3250,6 +3254,30 @@ static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, return component->driver->stream_event(component, event); } +static int snd_soc_component_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + if (component->driver->pcm_new) + return component->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + if (component->driver->pcm_free) + component->driver->pcm_free(pcm); +} + +static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + + return component->driver->set_bias_level(component, level); +} + static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -3270,16 +3298,21 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->set_sysclk = component->driver->set_sysclk; component->set_pll = component->driver->set_pll; component->set_jack = component->driver->set_jack; + component->pcm_new = snd_soc_component_drv_pcm_new; + component->pcm_free = snd_soc_component_drv_pcm_free; dapm = snd_soc_component_get_dapm(component); dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; - dapm->idle_bias_off = true; + dapm->idle_bias_off = !driver->idle_bias_on; + dapm->suspend_bias_off = driver->suspend_bias_off; if (driver->seq_notifier) dapm->seq_notifier = snd_soc_component_seq_notifier; if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; + if (driver->set_bias_level) + dapm->set_bias_level = snd_soc_component_set_bias_level; INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); @@ -3371,19 +3404,49 @@ static void snd_soc_component_del_unlocked(struct snd_soc_component *component) list_del(&component->list); } -int snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, - int num_dai) +#define ENDIANNESS_MAP(name) \ + (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) +static u64 endianness_format_map[] = { + ENDIANNESS_MAP(S16_), + ENDIANNESS_MAP(U16_), + ENDIANNESS_MAP(S24_), + ENDIANNESS_MAP(U24_), + ENDIANNESS_MAP(S32_), + ENDIANNESS_MAP(U32_), + ENDIANNESS_MAP(S24_3), + ENDIANNESS_MAP(U24_3), + ENDIANNESS_MAP(S20_3), + ENDIANNESS_MAP(U20_3), + ENDIANNESS_MAP(S18_3), + ENDIANNESS_MAP(U18_3), + ENDIANNESS_MAP(FLOAT_), + ENDIANNESS_MAP(FLOAT64_), + ENDIANNESS_MAP(IEC958_SUBFRAME_), +}; + +/* + * Fix up the DAI formats for endianness: codecs don't actually see + * the endianness of the data but we're using the CPU format + * definitions which do need to include endianness so we ensure that + * codec DAIs always have both big and little endian variants set. + */ +static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) { - struct snd_soc_component *component; - int ret; + int i; - component = kzalloc(sizeof(*component), GFP_KERNEL); - if (!component) { - dev_err(dev, "ASoC: Failed to allocate memory\n"); - return -ENOMEM; - } + for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) + if (stream->formats & endianness_format_map[i]) + stream->formats |= endianness_format_map[i]; +} + +int snd_soc_add_component(struct device *dev, + struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + int ret; + int i; ret = snd_soc_component_initialize(component, component_driver, dev); if (ret) @@ -3392,7 +3455,15 @@ int snd_soc_register_component(struct device *dev, component->ignore_pmdown_time = true; component->registered_as_component = true; - ret = snd_soc_register_dais(component, dai_drv, num_dai, true); + if (component_driver->endianness) { + for (i = 0; i < num_dai; i++) { + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); + } + } + + ret = snd_soc_register_dais(component, dai_drv, num_dai, + !component_driver->non_legacy_dai_naming); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; @@ -3408,6 +3479,22 @@ err_free: kfree(component); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_add_component); + +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *component; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + return snd_soc_add_component(dev, component, component_driver, + dai_drv, num_dai); +} EXPORT_SYMBOL_GPL(snd_soc_register_component); /** @@ -3448,6 +3535,32 @@ void snd_soc_unregister_component(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name) +{ + struct snd_soc_component *component; + struct snd_soc_component *ret; + + ret = NULL; + mutex_lock(&client_mutex); + list_for_each_entry(component, &component_list, list) { + if (dev != component->dev) + continue; + + if (driver_name && + (driver_name != component->driver->name) && + (strcmp(component->driver->name, driver_name) != 0)) + continue; + + ret = component; + break; + } + mutex_unlock(&client_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component); + static int snd_soc_platform_drv_probe(struct snd_soc_component *component) { struct snd_soc_platform *platform = snd_soc_component_to_platform(component); @@ -3462,6 +3575,26 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component) platform->driver->remove(platform); } +static int snd_soc_platform_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_new) + return platform->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_platform_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_free) + platform->driver->pcm_free(pcm); +} + /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -3485,6 +3618,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->component.probe = snd_soc_platform_drv_probe; if (platform_drv->remove) platform->component.remove = snd_soc_platform_drv_remove; + if (platform_drv->pcm_new) + platform->component.pcm_new = snd_soc_platform_drv_pcm_new; + if (platform_drv->pcm_free) + platform->component.pcm_free = snd_soc_platform_drv_pcm_free; #ifdef CONFIG_DEBUG_FS platform->component.debugfs_prefix = "platform"; @@ -3582,39 +3719,6 @@ void snd_soc_unregister_platform(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); -static u64 codec_format_map[] = { - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE, - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE, - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE, - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE, - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE, - SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE, - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE, - SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE, - SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE, - SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE, - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE - | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, -}; - -/* Fix up the DAI formats for endianness: codecs don't actually see - * the endianness of the data but we're using the CPU format - * definitions which do need to include endianness so we ensure that - * codec DAIs always have both big and little endian variants set. - */ -static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(codec_format_map); i++) - if (stream->formats & codec_format_map[i]) - stream->formats |= codec_format_map[i]; -} - static int snd_soc_codec_drv_probe(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); @@ -3765,8 +3869,8 @@ int snd_soc_register_codec(struct device *dev, codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { - fixup_codec_formats(&dai_drv[i].playback); - fixup_codec_formats(&dai_drv[i].capture); + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); } ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index dcef67a9bd48..a10b21cfc31e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2884,7 +2884,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, { int i, r, ret = 0; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); for (i = 0; i < num; i++) { r = snd_soc_dapm_add_route(dapm, route); if (r < 0) { @@ -2915,7 +2915,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, { int i; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); for (i = 0; i < num; i++) { snd_soc_dapm_del_route(dapm, route); route++; @@ -3681,7 +3681,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: substream.stream = SNDRV_PCM_STREAM_CAPTURE; - if (source->driver->ops && source->driver->ops->startup) { + if (source->driver->ops->startup) { ret = source->driver->ops->startup(&substream, source); if (ret < 0) { dev_err(source->dev, @@ -3695,7 +3695,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, goto out; substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - if (sink->driver->ops && sink->driver->ops->startup) { + if (sink->driver->ops->startup) { ret = sink->driver->ops->startup(&substream, sink); if (ret < 0) { dev_err(sink->dev, @@ -3725,13 +3725,13 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ret = 0; source->active--; - if (source->driver->ops && source->driver->ops->shutdown) { + if (source->driver->ops->shutdown) { substream.stream = SNDRV_PCM_STREAM_CAPTURE; source->driver->ops->shutdown(&substream, source); } sink->active--; - if (sink->driver->ops && sink->driver->ops->shutdown) { + if (sink->driver->ops->shutdown) { substream.stream = SNDRV_PCM_STREAM_PLAYBACK; sink->driver->ops->shutdown(&substream, sink); } @@ -3778,18 +3778,27 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, return 0; } -int snd_soc_dapm_new_pcm(struct snd_soc_card *card, - const struct snd_soc_pcm_stream *params, - unsigned int num_params, - struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) +static void +snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, + unsigned long *private_value, + int num_params, + const char **w_param_text) +{ + int count; + + devm_kfree(card->dev, (void *)*private_value); + for (count = 0 ; count < num_params; count++) + devm_kfree(card->dev, (void *)w_param_text[count]); + devm_kfree(card->dev, w_param_text); +} + +static struct snd_kcontrol_new * +snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, + char *link_name, + const struct snd_soc_pcm_stream *params, + int num_params, const char **w_param_text, + unsigned long *private_value) { - struct snd_soc_dapm_widget template; - struct snd_soc_dapm_widget *w; - char *link_name; - int ret, count; - unsigned long private_value; - const char **w_param_text; struct soc_enum w_param_enum[] = { SOC_ENUM_SINGLE(0, 0, 0, NULL), }; @@ -3798,19 +3807,9 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, snd_soc_dapm_dai_link_get, snd_soc_dapm_dai_link_put), }; + struct snd_kcontrol_new *kcontrol_news; const struct snd_soc_pcm_stream *config = params; - - w_param_text = devm_kcalloc(card->dev, num_params, - sizeof(char *), GFP_KERNEL); - if (!w_param_text) - return -ENOMEM; - - link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", - source->name, sink->name); - if (!link_name) { - ret = -ENOMEM; - goto outfree_w_param; - } + int count; for (count = 0 ; count < num_params; count++) { if (!config->stream_name) { @@ -3821,57 +3820,94 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, devm_kasprintf(card->dev, GFP_KERNEL, "Anonymous Configuration %d", count); - if (!w_param_text[count]) { - ret = -ENOMEM; - goto outfree_link_name; - } } else { w_param_text[count] = devm_kmemdup(card->dev, config->stream_name, strlen(config->stream_name) + 1, GFP_KERNEL); - if (!w_param_text[count]) { - ret = -ENOMEM; - goto outfree_link_name; - } } + if (!w_param_text[count]) + goto outfree_w_param; config++; } + w_param_enum[0].items = num_params; w_param_enum[0].texts = w_param_text; - memset(&template, 0, sizeof(template)); - template.reg = SND_SOC_NOPM; - template.id = snd_soc_dapm_dai_link; - template.name = link_name; - template.event = snd_soc_dai_link_event; - template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD; - template.num_kcontrols = 1; - /* duplicate w_param_enum on heap so that memory persists */ - private_value = + *private_value = (unsigned long) devm_kmemdup(card->dev, (void *)(kcontrol_dai_link[0].private_value), sizeof(struct soc_enum), GFP_KERNEL); - if (!private_value) { + if (!*private_value) { dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", link_name); - ret = -ENOMEM; - goto outfree_link_name; + goto outfree_w_param; } - kcontrol_dai_link[0].private_value = private_value; + kcontrol_dai_link[0].private_value = *private_value; /* duplicate kcontrol_dai_link on heap so that memory persists */ - template.kcontrol_news = - devm_kmemdup(card->dev, &kcontrol_dai_link[0], + kcontrol_news = devm_kmemdup(card->dev, &kcontrol_dai_link[0], sizeof(struct snd_kcontrol_new), GFP_KERNEL); - if (!template.kcontrol_news) { + if (!kcontrol_news) { dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", link_name); - ret = -ENOMEM; - goto outfree_private_value; + goto outfree_w_param; } + return kcontrol_news; + +outfree_w_param: + snd_soc_dapm_free_kcontrol(card, private_value, num_params, w_param_text); + return NULL; +} + +int snd_soc_dapm_new_pcm(struct snd_soc_card *card, + const struct snd_soc_pcm_stream *params, + unsigned int num_params, + struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget template; + struct snd_soc_dapm_widget *w; + const char **w_param_text; + unsigned long private_value; + char *link_name; + int ret; + + link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", + source->name, sink->name); + if (!link_name) + return -ENOMEM; + + memset(&template, 0, sizeof(template)); + template.reg = SND_SOC_NOPM; + template.id = snd_soc_dapm_dai_link; + template.name = link_name; + template.event = snd_soc_dai_link_event; + template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD; + template.kcontrol_news = NULL; + + /* allocate memory for control, only in case of multiple configs */ + if (num_params > 1) { + w_param_text = devm_kcalloc(card->dev, num_params, + sizeof(char *), GFP_KERNEL); + if (!w_param_text) { + ret = -ENOMEM; + goto param_fail; + } + template.num_kcontrols = 1; + template.kcontrol_news = + snd_soc_dapm_alloc_kcontrol(card, + link_name, params, num_params, + w_param_text, &private_value); + if (!template.kcontrol_news) { + ret = -ENOMEM; + goto param_fail; + } + } else { + w_param_text = NULL; + } dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); @@ -3903,15 +3939,9 @@ outfree_w: devm_kfree(card->dev, w); outfree_kcontrol_news: devm_kfree(card->dev, (void *)template.kcontrol_news); -outfree_private_value: - devm_kfree(card->dev, (void *)private_value); -outfree_link_name: + snd_soc_dapm_free_kcontrol(card, &private_value, num_params, w_param_text); +param_fail: devm_kfree(card->dev, link_name); -outfree_w_param: - for (count = 0 ; count < num_params; count++) - devm_kfree(card->dev, (void *)w_param_text[count]); - devm_kfree(card->dev, w_param_text); - return ret; } diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 9b3939049cef..20340ade20a7 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -41,6 +41,20 @@ int snd_soc_component_read(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_read); +unsigned int snd_soc_component_read32(struct snd_soc_component *component, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return -1; + + return val; +} +EXPORT_SYMBOL_GPL(snd_soc_component_read32); + /** * snd_soc_component_write() - Write register value * @component: Component to write to diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 94b88b897c3b..8075856668c2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -133,16 +133,25 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; int i; bool ignore = true; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + ignore &= !component->driver->pmdown_time; + } + + /* this will be removed */ for (i = 0; i < rtd->num_codecs; i++) ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time; - return rtd->cpu_dai->component->ignore_pmdown_time && ignore; + return ignore; } /** @@ -459,7 +468,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0; + int i, ret = 0, __ret; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -474,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); /* startup the audio subsystem */ - if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { + if (cpu_dai->driver->ops->startup) { ret = cpu_dai->driver->ops->startup(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: can't open interface" @@ -483,7 +492,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->open) { + if (platform && platform->driver->ops && platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: can't open platform" @@ -492,9 +501,32 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->open) + continue; + + __ret = component->driver->ops->open(substream); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: can't open component %s: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { + if (codec_dai->driver->ops->startup) { ret = codec_dai->driver->ops->startup(substream, codec_dai); if (ret < 0) { @@ -511,7 +543,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->rx_mask = 0; } - if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { + if (rtd->dai_link->ops->startup) { ret = rtd->dai_link->ops->startup(substream); if (ret < 0) { pr_err("ASoC: %s startup failed: %d\n", @@ -585,7 +617,7 @@ dynamic: return 0; config_err: - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); machine_err: @@ -598,7 +630,22 @@ codec_dai_err: codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (platform->driver->ops && platform->driver->ops->close) +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: @@ -692,12 +739,26 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform->driver->ops && platform->driver->ops->close) + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ @@ -745,13 +806,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { + if (rtd->dai_link->ops->prepare) { ret = rtd->dai_link->ops->prepare(substream); if (ret < 0) { dev_err(rtd->card->dev, "ASoC: machine prepare error:" @@ -760,7 +823,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->prepare) { + if (platform && platform->driver->ops && platform->driver->ops->prepare) { ret = platform->driver->ops->prepare(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: platform prepare error:" @@ -769,9 +832,28 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->prepare) + continue; + + ret = component->driver->ops->prepare(substream); + if (ret < 0) { + dev_err(component->dev, + "ASoC: platform prepare error: %d\n", ret); + goto out; + } + } + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { + if (codec_dai->driver->ops->prepare) { ret = codec_dai->driver->ops->prepare(substream, codec_dai); if (ret < 0) { @@ -783,7 +865,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { + if (cpu_dai->driver->ops->prepare) { ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, @@ -829,7 +911,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, { int ret; - if (dai->driver->ops && dai->driver->ops->hw_params) { + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n", @@ -851,16 +933,13 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0; + int i, ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - - ret = soc_pcm_params_symmetry(substream, params); - if (ret) - goto out; - - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { + if (rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); if (ret < 0) { dev_err(rtd->card->dev, "ASoC: machine hw_params" @@ -915,7 +994,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - if (platform->driver->ops && platform->driver->ops->hw_params) { + if (platform && platform->driver->ops && platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", @@ -924,18 +1003,62 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_params) + continue; + + __ret = component->driver->ops->hw_params(substream, params); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: %s hw params failed: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); cpu_dai->channels = params_channels(params); cpu_dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); + ret = soc_pcm_params_symmetry(substream, params); + if (ret) + goto component_err; out: mutex_unlock(&rtd->pcm_mutex); return ret; +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->hw_free) + platform->driver->ops->hw_free(substream); + platform_err: - if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); interface_err: @@ -944,12 +1067,12 @@ interface_err: codec_err: while (--i >= 0) { struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); codec_dai->rate = 0; } - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + if (rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); mutex_unlock(&rtd->pcm_mutex); @@ -963,6 +1086,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -995,21 +1120,36 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) } /* free any machine hw params */ - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + if (rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) + if (platform && platform->driver->ops && platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); + /* free any component resources */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); mutex_unlock(&rtd->pcm_mutex); @@ -1020,13 +1160,15 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { + if (codec_dai->driver->ops->trigger) { ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); if (ret < 0) @@ -1034,19 +1176,35 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } - if (platform->driver->ops && platform->driver->ops->trigger) { + if (platform && platform->driver->ops && platform->driver->ops->trigger) { ret = platform->driver->ops->trigger(substream, cmd); if (ret < 0) return ret; } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->trigger) + continue; + + ret = component->driver->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (cpu_dai->driver->ops->trigger) { ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; } - if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) { + if (rtd->dai_link->ops->trigger) { ret = rtd->dai_link->ops->trigger(substream, cmd); if (ret < 0) return ret; @@ -1065,8 +1223,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && - codec_dai->driver->ops->bespoke_trigger) { + if (codec_dai->driver->ops->bespoke_trigger) { ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); if (ret < 0) @@ -1074,7 +1231,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, } } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { + if (cpu_dai->driver->ops->bespoke_trigger) { ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; @@ -1090,6 +1247,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; @@ -1098,15 +1257,31 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; - if (platform->driver->ops && platform->driver->ops->pointer) + if (platform && platform->driver->ops && platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); - if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->pointer) + continue; + + /* FIXME: use 1st pointer */ + offset = component->driver->ops->pointer(substream); + break; + } + + if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->delay) + if (codec_dai->driver->ops->delay) codec_delay = max(codec_delay, codec_dai->driver->ops->delay(substream, codec_dai)); @@ -2285,9 +2460,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; - if (platform->driver->ops && platform->driver->ops->ioctl) + if (platform && platform->driver->ops && platform->driver->ops->ioctl) return platform->driver->ops->ioctl(substream, cmd, arg); + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->ioctl) + continue; + + /* FIXME: use 1st ioctl */ + return component->driver->ops->ioctl(substream, cmd, arg); + } + return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -2632,12 +2825,163 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) return ret; } +static void soc_pcm_private_free(struct snd_pcm *pcm) +{ + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + /* need to sync the delayed work before releasing resources */ + + flush_delayed_work(&rtd->delayed_work); + component = rtdcom->component; + + if (component->pcm_free) + component->pcm_free(component, pcm); + } +} + +static int soc_rtdcom_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->ack) + continue; + + /* FIXME. it returns 1st ask now */ + return component->driver->ops->ack(substream); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_user) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_user(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_kernel) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_kernel(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->fill_silence) + continue; + + /* FIXME. it returns 1st silence now */ + return component->driver->ops->fill_silence(substream, channel, + pos, bytes); + } + + return -EINVAL; +} + +static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + struct page *page; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->page) + continue; + + /* FIXME. it returns 1st page now */ + page = component->driver->ops->page(substream, offset); + if (page) + return page; + } + + return NULL; +} + +static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->mmap) + continue; + + /* FIXME. it returns 1st mmap now */ + return component->driver->ops->mmap(substream, vma); + } + + return -EINVAL; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; @@ -2732,7 +3076,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = soc_pcm_ioctl; } - if (platform->driver->ops) { + for_each_rtdcom(rtd, rtdcom) { + const struct snd_pcm_ops *ops = rtdcom->component->driver->ops; + + if (!ops) + continue; + + if (ops->ack) + rtd->ops.ack = soc_rtdcom_ack; + if (ops->copy_user) + rtd->ops.copy_user = soc_rtdcom_copy_user; + if (ops->copy_kernel) + rtd->ops.copy_kernel = soc_rtdcom_copy_kernel; + if (ops->fill_silence) + rtd->ops.fill_silence = soc_rtdcom_fill_silence; + if (ops->page) + rtd->ops.page = soc_rtdcom_page; + if (ops->mmap) + rtd->ops.mmap = soc_rtdcom_mmap; + } + + /* overwrite */ + if (platform && platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; @@ -2747,17 +3112,22 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); - if (platform->driver->pcm_new) { - ret = platform->driver->pcm_new(rtd); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->pcm_new) + continue; + + ret = component->pcm_new(component, rtd); if (ret < 0) { - dev_err(platform->dev, + dev_err(component->dev, "ASoC: pcm constructor failed: %d\n", ret); return ret; } } - pcm->private_free = platform->driver->pcm_free; + pcm->private_free = soc_pcm_private_free; out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index dd471d2c0266..01a50413c66f 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1301,7 +1301,7 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( /* validate kcontrol */ if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) - return NULL; + goto err; se = kzalloc(sizeof(*se), GFP_KERNEL); if (se == NULL) @@ -1378,6 +1378,9 @@ err_se: for (; i >= 0; i--) { /* free values and texts */ se = (struct soc_enum *)kc[i].private_value; + if (!se) + continue; + kfree(se->dobj.control.dvalues); for (j = 0; j < ec->items; j++) kfree(se->dobj.control.dtexts[j]); diff --git a/sound/soc/spear/Makefile b/sound/soc/spear/Makefile index c4ea7161056c..31d9dae280e7 100644 --- a/sound/soc/spear/Makefile +++ b/sound/soc/spear/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # SPEAR Platform Support snd-soc-spear-pcm-objs := spear_pcm.o snd-soc-spear-spdif-in-objs := spdif_in.o diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile index 4ed22e648a9a..5b7f0fab0bd6 100644 --- a/sound/soc/stm/Makefile +++ b/sound/soc/stm/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # SAI snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o obj-$(CONFIG_SND_SOC_STM32_SAI) += snd-soc-stm32-sai-sub.o diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 1258bef4dcb3..d6f71a3406e9 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -16,6 +16,7 @@ * details. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> @@ -27,6 +28,16 @@ #include "stm32_sai.h" +static LIST_HEAD(sync_providers); +static DEFINE_MUTEX(sync_mutex); + +struct sync_provider { + struct list_head link; + struct device_node *node; + int (*sync_conf)(void *data, int synco); + void *data; +}; + static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; @@ -41,23 +52,143 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; +static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +{ + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_sync_conf_provider(void *data, int synco) +{ + struct stm32_sai_data *sai = (struct stm32_sai_data *)data; + u32 prev_synco; + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", + sai->pdev->dev.of_node->name, + synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + + prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); + if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { + dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", + sai->pdev->dev.of_node->name, + prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + clk_disable_unprepare(sai->pclk); + return -EINVAL; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_set_sync_provider(struct device_node *np, int synco) +{ + struct sync_provider *provider; + int ret; + + mutex_lock(&sync_mutex); + list_for_each_entry(provider, &sync_providers, link) { + if (provider->node == np) { + ret = provider->sync_conf(provider->data, synco); + mutex_unlock(&sync_mutex); + return ret; + } + } + mutex_unlock(&sync_mutex); + + /* SAI sync provider not found */ + return -ENODEV; +} + +static int stm32_sai_set_sync(struct stm32_sai_data *sai, + struct device_node *np_provider, + int synco, int synci) +{ + int ret; + + /* Configure sync client */ + stm32_sai_sync_conf_client(sai, synci); + + /* Configure sync provider */ + ret = stm32_sai_set_sync_provider(np_provider, synco); + + return ret; +} + +static int stm32_sai_sync_add_provider(struct platform_device *pdev, + void *data) +{ + struct sync_provider *sp; + + sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + + sp->node = of_node_get(pdev->dev.of_node); + sp->data = data; + sp->sync_conf = &stm32_sai_sync_conf_provider; + + mutex_lock(&sync_mutex); + list_add(&sp->link, &sync_providers); + mutex_unlock(&sync_mutex); + + return 0; +} + +static void stm32_sai_sync_del_provider(struct device_node *np) +{ + struct sync_provider *sp; + + mutex_lock(&sync_mutex); + list_for_each_entry(sp, &sync_providers, link) { + if (sp->node == np) { + list_del(&sp->link); + of_node_put(sp->node); + break; + } + } + mutex_unlock(&sync_mutex); +} + static int stm32_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; - void __iomem *base; const struct of_device_id *of_id; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) @@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; + if (!STM_SAI_IS_F4(sai)) { + sai->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(sai->pclk)) { + dev_err(&pdev->dev, "missing bus clock pclk\n"); + return PTR_ERR(sai->pclk); + } + } + sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); @@ -85,23 +224,34 @@ static int stm32_sai_probe(struct platform_device *pdev) } /* reset */ - rst = reset_control_get_exclusive(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (!IS_ERR(rst)) { reset_control_assert(rst); udelay(2); reset_control_deassert(rst); } + ret = stm32_sai_sync_add_provider(pdev, sai); + if (ret < 0) + return ret; + sai->set_sync = &stm32_sai_set_sync; + sai->pdev = pdev; platform_set_drvdata(pdev, sai); - return of_platform_populate(np, NULL, NULL, &pdev->dev); + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret < 0) + stm32_sai_sync_del_provider(np); + + return ret; } static int stm32_sai_remove(struct platform_device *pdev) { of_platform_depopulate(&pdev->dev); + stm32_sai_sync_del_provider(pdev->dev.of_node); + return 0; } diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 889974dc62d9..bb062e70de63 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -16,9 +16,11 @@ * details. */ +#include <linux/bitfield.h> + /******************** SAI Register Map **************************************/ -/* common register */ +/* Global configuration register */ #define STM_SAI_GCR 0x00 /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ @@ -37,12 +39,13 @@ /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 +#define SAI_GCR_SYNCIN_WDTH 2 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) -#define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) +#define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ + SAI_GCR_SYNCIN_MASK) #define SAI_GCR_SYNCOUT_SHIFT 4 #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) -#define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) /******************* Bit definition for SAI_XCR1 register *******************/ #define SAI_XCR1_RX_TX_SHIFT 0 @@ -231,6 +234,12 @@ #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) +enum stm32_sai_syncout { + STM_SAI_SYNC_OUT_NONE, + STM_SAI_SYNC_OUT_A, + STM_SAI_SYNC_OUT_B, +}; + enum stm32_sai_version { SAI_STM32F4, SAI_STM32H7 @@ -247,15 +256,22 @@ struct stm32_sai_conf { /** * struct stm32_sai_data - private data of SAI instance driver * @pdev: device data pointer + * @base: common register bank virtual base address + * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz * @version: SOC version * @irq: SAI interrupt line + * @set_sync: pointer to synchro mode configuration callback */ struct stm32_sai_data { struct platform_device *pdev; + void __iomem *base; + struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; struct stm32_sai_conf *conf; int irq; + int (*set_sync)(struct stm32_sai_data *sai, + struct device_node *np_provider, int synco, int synci); }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 90d439613899..08583b958430 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -55,6 +55,12 @@ #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") +#define SAI_SYNC_NONE 0x0 +#define SAI_SYNC_INTERNAL 0x1 +#define SAI_SYNC_EXTERNAL 0x2 + +#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -65,6 +71,7 @@ * @cpu_dai: DAI runtime data pointer * @substream: PCM substream data pointer * @pdata: SAI block parent data pointer + * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init @@ -73,6 +80,8 @@ * @master: SAI block mode flag. (true=master, false=slave) set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) + * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) + * @synci: SAI block ext sync source (client setting). (SAI sync provider index) * @fs_length: frame synchronization length. depends on protocol settings * @slots: rx or tx slot number * @slot_width: rx or tx slot width in bits @@ -88,6 +97,7 @@ struct stm32_sai_sub_data { struct snd_soc_dai *cpu_dai; struct snd_pcm_substream *substream; struct stm32_sai_data *pdata; + struct device_node *np_sync_provider; struct clk *sai_ck; dma_addr_t phys_addr; unsigned int mclk_rate; @@ -96,6 +106,8 @@ struct stm32_sai_sub_data { bool master; int fmt; int sync; + int synco; + int synci; int fs_length; int slots; int slot_width; @@ -184,7 +196,6 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; - struct snd_pcm_substream *substream = sai->substream; struct platform_device *pdev = sai->pdev; unsigned int sr, imr, flags; snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; @@ -199,6 +210,11 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); + if (!sai->substream) { + dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); + return IRQ_NONE; + } + if (flags & SAI_XIMR_OVRUDRIE) { dev_err(&pdev->dev, "IRQ %s\n", STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); @@ -227,9 +243,9 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) } if (status != SNDRV_PCM_STATE_RUNNING) { - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stream_lock(sai->substream); + snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(sai->substream); } return IRQ_HANDLED; @@ -304,12 +320,15 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); - int cr1 = 0, frcr = 0; - int cr1_mask = 0, frcr_mask = 0; + int cr1, frcr = 0; + int cr1_mask, frcr_mask = 0; int ret; dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); + cr1_mask = SAI_XCR1_PRTCFG_MASK; + cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { /* SCK active high for all protocols */ case SND_SOC_DAIFMT_I2S: @@ -336,7 +355,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return -EINVAL; } - cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; + cr1_mask |= SAI_XCR1_CKSTR; frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; @@ -380,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } + + /* Set slave mode if sub-block is synchronized with another SAI */ + if (sai->sync) { + dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); + cr1 |= SAI_XCR1_SLAVE; + sai->master = false; + } + cr1_mask |= SAI_XCR1_SLAVE; /* do not generate master by default */ @@ -412,8 +439,6 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, } /* Enable ITs */ - regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, - SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); @@ -442,34 +467,33 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int cr1, cr1_mask, ret; - int fth = STM_SAI_FIFO_TH_HALF; - /* FIFO config */ + /* + * DMA bursts increment is set to 4 words. + * SAI fifo threshold is set to half fifo, to keep enough space + * for DMA incoming bursts. + */ regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, - SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); + SAI_XCR2_FFLUSH | + SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); /* Mode, data format and channel config */ - cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + cr1_mask = SAI_XCR1_DS_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_8); break; case SNDRV_PCM_FORMAT_S16_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_16); break; case SNDRV_PCM_FORMAT_S32_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32); break; default: dev_err(cpu_dai->dev, "Data format not supported"); return -EINVAL; } - cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; - - cr1_mask |= SAI_XCR1_RX_TX; - if (STM_SAI_IS_CAPTURE(sai)) - cr1 |= SAI_XCR1_RX_TX; cr1_mask |= SAI_XCR1_MONO; if ((sai->slots == 2) && (params_channels(params) == 1)) @@ -481,10 +505,6 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, return ret; } - /* DMA config */ - sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); - return 0; } @@ -691,6 +711,9 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_STOP: dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); + regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, + SAI_XIMR_MASK, 0); + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_SAIEN, (unsigned int)~SAI_XCR1_SAIEN); @@ -725,9 +748,15 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + int cr1 = 0, cr1_mask; sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); - sai->dma_params.maxburst = 1; + /* + * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, + * as it allows bytes, half-word and words transfers. (See DMA fifos + * constraints). + */ + sai->dma_params.maxburst = 4; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -736,7 +765,21 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) else snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); - return 0; + cr1_mask = SAI_XCR1_RX_TX; + if (STM_SAI_IS_CAPTURE(sai)) + cr1 |= SAI_XCR1_RX_TX; + + /* Configure synchronization */ + if (sai->sync == SAI_SYNC_EXTERNAL) { + /* Configure synchro client and provider */ + sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, + sai->synco, sai->synci); + } + + cr1_mask |= SAI_XCR1_SYNCEN_MASK; + cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); + + return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { @@ -822,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; + struct of_phandle_args args; + int ret; if (!np) return -ENODEV; @@ -855,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } + /* Get synchronization property */ + args.np = NULL; + ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); + if (ret < 0 && ret != -ENOENT) { + dev_err(&pdev->dev, "Failed to get st,sync property\n"); + return ret; + } + + sai->sync = SAI_SYNC_NONE; + if (args.np) { + if (args.np == np) { + dev_err(&pdev->dev, "%s sync own reference\n", + np->name); + of_node_put(args.np); + return -EINVAL; + } + + sai->np_sync_provider = of_get_parent(args.np); + if (!sai->np_sync_provider) { + dev_err(&pdev->dev, "%s parent node not found\n", + np->name); + of_node_put(args.np); + return -ENODEV; + } + + sai->sync = SAI_SYNC_INTERNAL; + if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { + if (!STM_SAI_HAS_EXT_SYNC(sai)) { + dev_err(&pdev->dev, + "External synchro not supported\n"); + of_node_put(args.np); + return -EINVAL; + } + sai->sync = SAI_SYNC_EXTERNAL; + + sai->synci = args.args[0]; + if (sai->synci < 1 || + (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { + dev_err(&pdev->dev, "Wrong SAI index\n"); + of_node_put(args.np); + return -EINVAL; + } + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-a") >= 0) + sai->synco = STM_SAI_SYNC_OUT_A; + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-b") >= 0) + sai->synco = STM_SAI_SYNC_OUT_B; + + if (!sai->synco) { + dev_err(&pdev->dev, "Unknown SAI sub-block\n"); + of_node_put(args.np); + return -EINVAL; + } + } + + dev_dbg(&pdev->dev, "%s synchronized with %s\n", + pdev->name, args.np->full_name); + } + + of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 84cc5678beba..b9bdefcd3e10 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -392,6 +392,12 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, { int ret; + spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); + if (IS_ERR(spdifrx->ctrl_chan)) { + dev_err(dev, "dma_request_slave_channel failed\n"); + return PTR_ERR(spdifrx->ctrl_chan); + } + spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); if (!spdifrx->dmab) @@ -406,12 +412,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, return ret; } - spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (!spdifrx->ctrl_chan) { - dev_err(dev, "dma_request_slave_channel failed\n"); - return -EINVAL; - } - spdifrx->slave_config.direction = DMA_DEV_TO_MEM; spdifrx->slave_config.src_addr = (dma_addr_t)(spdifrx->phys_addr + STM32_SPDIFRX_CSR); @@ -423,7 +423,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, &spdifrx->slave_config); if (ret < 0) { dev_err(dev, "dmaengine_slave_config returned error %d\n", ret); - dma_release_channel(spdifrx->ctrl_chan); spdifrx->ctrl_chan = NULL; } @@ -750,17 +749,21 @@ static int stm32_spdifrx_hw_params(struct snd_pcm_substream *substream, switch (data_size) { case 16: fmt = SPDIFRX_DRFMT_PACKED; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case 32: fmt = SPDIFRX_DRFMT_LEFT; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: dev_err(&spdifrx->pdev->dev, "Unexpected data format\n"); return -EINVAL; } + /* + * Set buswidth to 4 bytes for all data formats. + * Packed format: transfer 2 x 2 bytes samples + * Left format: transfer 1 x 3 bytes samples + 1 dummy byte + */ + spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params); return regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, @@ -958,7 +961,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) return 0; error: - if (spdifrx->ctrl_chan) + if (!IS_ERR(spdifrx->ctrl_chan)) dma_release_channel(spdifrx->ctrl_chan); if (spdifrx->dmab) snd_dma_free_pages(spdifrx->dmab); diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index 1f1af6271731..4a9ef67386ca 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index baa9007464ed..5da4efe7a550 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -346,11 +346,6 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, 0x3 << 8, 0x1 << 8); - /* Fill most significant bits with valid data MSB */ - regmap_field_update_bits(scodec->reg_adc_fifoc, - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); - return 0; } @@ -490,6 +485,30 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); + /* Set the number of sample bits to either 16 or 24 bits */ + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); + + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + 0); + + scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } else { + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS), + 0); + + /* Fill most significant bits with valid data MSB */ + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); + + scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } + return 0; } diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index abfb710df7cb..3dd183be08a4 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -73,6 +73,7 @@ #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) +#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) struct sun8i_codec { struct device *dev; @@ -170,11 +171,11 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) /* clock masters */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */ - value = 0x0; /* Codec Master */ + case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */ + value = 0x1; break; - case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */ - value = 0x1; /* Codec Slave */ + case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */ + value = 0x0; break; default: return -EINVAL; @@ -197,9 +198,20 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV), value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); + + /* + * It appears that the DAI and the codec don't share the same + * polarity for the LRCK signal when they mean 'normal' and + * 'inverted' in the datasheet. + * + * Since the DAI here is our regular i2s driver that have been + * tested with way more codecs than just this one, it means + * that the codec probably gets it backward, and we have to + * invert the value here. + */ regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), - value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); + !value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); /* DAI format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -226,12 +238,57 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +struct sun8i_codec_clk_div { + u8 div; + u8 val; +}; + +static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = { + { .div = 1, .val = 0 }, + { .div = 2, .val = 1 }, + { .div = 4, .val = 2 }, + { .div = 6, .val = 3 }, + { .div = 8, .val = 4 }, + { .div = 12, .val = 5 }, + { .div = 16, .val = 6 }, + { .div = 24, .val = 7 }, + { .div = 32, .val = 8 }, + { .div = 48, .val = 9 }, + { .div = 64, .val = 10 }, + { .div = 96, .val = 11 }, + { .div = 128, .val = 12 }, + { .div = 192, .val = 13 }, +}; + +static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec, + unsigned int rate, + unsigned int word_size) +{ + unsigned long clk_rate = clk_get_rate(scodec->clk_module); + unsigned int div = clk_rate / rate / word_size / 2; + unsigned int best_val = 0, best_diff = ~0; + int i; + + for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) { + const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i]; + unsigned int diff = abs(bdiv->div - div); + + if (diff < best_diff) { + best_diff = diff; + best_val = bdiv->val; + } + } + + return best_val; +} + static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec); int sample_rate; + u8 bclk_div; /* * The CPU DAI handles only a sample of 16 bits. Configure the @@ -241,6 +298,11 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK, SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16); + bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16); + regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, + SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK, + bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV); + regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK, SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16); diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index f214a3fd0024..2329b72c93e3 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Tegra platform Support snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o diff --git a/sound/soc/txx9/Makefile b/sound/soc/txx9/Makefile index 551f16c0c4f9..37ad833eb329 100644 --- a/sound/soc/txx9/Makefile +++ b/sound/soc/txx9/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Platform snd-soc-txx9aclc-objs := txx9aclc.o snd-soc-txx9aclc-ac97-objs := txx9aclc-ac97.o diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile index cce0c11a4d86..e7d6de51b32b 100644 --- a/sound/soc/ux500/Makefile +++ b/sound/soc/ux500/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # Ux500 Platform Support snd-soc-ux500-plat-msp-i2s-objs := ux500_msp_dai.o ux500_msp_i2s.o diff --git a/sound/soc/zte/zx-spdif.c b/sound/soc/zte/zx-spdif.c index b143f9f682d2..17b6ce35037a 100644 --- a/sound/soc/zte/zx-spdif.c +++ b/sound/soc/zte/zx-spdif.c @@ -139,11 +139,11 @@ static int zx_spdif_hw_params(struct snd_pcm_substream *substream, { struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev); struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai); - struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data; + struct snd_dmaengine_dai_dma_data *dma_data = + snd_soc_dai_get_dma_data(socdai, substream); u32 val, ch_num, rate; int ret; - dma_data = snd_soc_dai_get_dma_data(socdai, substream); dma_data->addr_width = params_width(params) >> 3; val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL); |