174 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright (c) 2020 BayLibre, SAS.4// Author: Jerome Brunet <jbrunet@baylibre.com>5 6#include <linux/bitfield.h>7#include <linux/clk.h>8#include <sound/pcm_params.h>9#include <sound/soc.h>10#include <sound/soc-dai.h>11 12#include "aiu.h"13#include "aiu-fifo.h"14 15#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)16#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)17#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)18#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)19#define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16)20#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)21#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)22#define AIU_RST_SOFT_I2S_FAST BIT(0)23#define AIU_I2S_MISC_HOLD_EN BIT(2)24#define AIU_I2S_MISC_FORCE_LEFT_RIGHT BIT(4)25 26#define AIU_FIFO_I2S_BLOCK 25627 28static const struct snd_pcm_hardware fifo_i2s_pcm = {29 .info = (SNDRV_PCM_INFO_INTERLEAVED |30 SNDRV_PCM_INFO_MMAP |31 SNDRV_PCM_INFO_MMAP_VALID |32 SNDRV_PCM_INFO_PAUSE),33 .formats = AIU_FORMATS,34 .rate_min = 5512,35 .rate_max = 192000,36 .channels_min = 2,37 .channels_max = 8,38 .period_bytes_min = AIU_FIFO_I2S_BLOCK,39 .period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,40 .periods_min = 2,41 .periods_max = UINT_MAX,42 43 /* No real justification for this */44 .buffer_bytes_max = 1 * 1024 * 1024,45};46 47static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,48 struct snd_soc_dai *dai)49{50 struct snd_soc_component *component = dai->component;51 52 switch (cmd) {53 case SNDRV_PCM_TRIGGER_START:54 case SNDRV_PCM_TRIGGER_RESUME:55 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:56 snd_soc_component_write(component, AIU_RST_SOFT,57 AIU_RST_SOFT_I2S_FAST);58 snd_soc_component_read(component, AIU_I2S_SYNC);59 break;60 }61 62 return aiu_fifo_trigger(substream, cmd, dai);63}64 65static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,66 struct snd_soc_dai *dai)67{68 struct snd_soc_component *component = dai->component;69 int ret;70 71 ret = aiu_fifo_prepare(substream, dai);72 if (ret)73 return ret;74 75 snd_soc_component_update_bits(component,76 AIU_MEM_I2S_BUF_CNTL,77 AIU_MEM_I2S_BUF_CNTL_INIT,78 AIU_MEM_I2S_BUF_CNTL_INIT);79 snd_soc_component_update_bits(component,80 AIU_MEM_I2S_BUF_CNTL,81 AIU_MEM_I2S_BUF_CNTL_INIT, 0);82 83 return 0;84}85 86static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,87 struct snd_pcm_hw_params *params,88 struct snd_soc_dai *dai)89{90 struct snd_soc_component *component = dai->component;91 struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);92 unsigned int val;93 int ret;94 95 snd_soc_component_update_bits(component, AIU_I2S_MISC,96 AIU_I2S_MISC_HOLD_EN,97 AIU_I2S_MISC_HOLD_EN);98 99 ret = aiu_fifo_hw_params(substream, params, dai);100 if (ret)101 return ret;102 103 switch (params_physical_width(params)) {104 case 16:105 val = AIU_MEM_I2S_CONTROL_MODE_16BIT;106 break;107 case 32:108 val = 0;109 break;110 default:111 dev_err(dai->dev, "Unsupported physical width %u\n",112 params_physical_width(params));113 return -EINVAL;114 }115 116 snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,117 AIU_MEM_I2S_CONTROL_MODE_16BIT,118 val);119 120 /* Setup the irq periodicity */121 val = params_period_bytes(params) / fifo->fifo_block;122 val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);123 snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,124 AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);125 126 /*127 * Most (all?) supported SoCs have this bit set by default. The vendor128 * driver however sets it manually (depending on the version either129 * while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow130 * the same approach for consistency with the vendor driver.131 */132 snd_soc_component_update_bits(component, AIU_I2S_MISC,133 AIU_I2S_MISC_FORCE_LEFT_RIGHT,134 AIU_I2S_MISC_FORCE_LEFT_RIGHT);135 136 snd_soc_component_update_bits(component, AIU_I2S_MISC,137 AIU_I2S_MISC_HOLD_EN, 0);138 139 return 0;140}141 142const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {143 .pcm_new = aiu_fifo_pcm_new,144 .probe = aiu_fifo_i2s_dai_probe,145 .remove = aiu_fifo_dai_remove,146 .trigger = aiu_fifo_i2s_trigger,147 .prepare = aiu_fifo_i2s_prepare,148 .hw_params = aiu_fifo_i2s_hw_params,149 .startup = aiu_fifo_startup,150 .shutdown = aiu_fifo_shutdown,151};152 153int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)154{155 struct snd_soc_component *component = dai->component;156 struct aiu *aiu = snd_soc_component_get_drvdata(component);157 struct aiu_fifo *fifo;158 int ret;159 160 ret = aiu_fifo_dai_probe(dai);161 if (ret)162 return ret;163 164 fifo = snd_soc_dai_dma_data_get_playback(dai);165 166 fifo->pcm = &fifo_i2s_pcm;167 fifo->mem_offset = AIU_MEM_I2S_START;168 fifo->fifo_block = AIU_FIFO_I2S_BLOCK;169 fifo->pclk = aiu->i2s.clks[PCLK].clk;170 fifo->irq = aiu->i2s.irq;171 172 return 0;173}174