/*
* STM32 ALSA SoC Digital Audio Interface (SAI) driver.
*
* Copyright (C) 2016, STMicroelectronics - All Rights Reserved
* Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
*
* License terms: GPL V2.0.
*
* 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/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "stm32_sai.h"
#define SAI_FREE_PROTOCOL 0x0
#define SAI_SPDIF_PROTOCOL 0x1
#define SAI_SLOT_SIZE_AUTO 0x0
#define SAI_SLOT_SIZE_16 0x1
#define SAI_SLOT_SIZE_32 0x2
#define SAI_DATASIZE_8 0x2
#define SAI_DATASIZE_10 0x3
#define SAI_DATASIZE_16 0x4
#define SAI_DATASIZE_20 0x5
#define SAI_DATASIZE_24 0x6
#define SAI_DATASIZE_32 0x7
#define STM_SAI_FIFO_SIZE 8
#define STM_SAI_DAI_NAME_SIZE 15
#define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK)
#define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE)
#define STM_SAI_A_ID 0x0
#define STM_SAI_B_ID 0x1
#define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID)
#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_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif)
#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf->has_spdif)
#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata))
#define SAI_IEC60958_BLOCK_FRAMES 192
#define SAI_IEC60958_STATUS_BYTES 24
/**
* struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
* @pdev: device data pointer
* @regmap: SAI register map pointer
* @regmap_config: SAI sub block register map configuration pointer
* @dma_params: dma configuration data for rx or tx channel
* @cpu_dai_drv: DAI driver data pointer
* @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
* @id: SAI sub block id corresponding to sub-block A or B
* @dir: SAI block direction (playback or capture). set at init
* @master: SAI block mode flag. (true=master, false=slave) set at init
* @spdif: SAI S/PDIF iec60958 mode flag. 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
* @slot_mask: rx or tx active slots mask. set at init or at runtime
* @data_size: PCM data width. corresponds to PCM substream width.
* @spdif_frm_cnt: S/PDIF playback frame counter
* @snd_aes_iec958: iec958 data
* @ctrl_lock: control lock
*/
struct stm32_sai_sub_data {
struct platform_device *pdev;
struct regmap *regmap;
const struct regmap_config *regmap_config;
struct snd_dmaengine_dai_dma_data dma_params;
struct snd_soc_dai_driver *cpu_dai_drv;
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;
unsigned int id;
int dir;
bool master;
bool spdif;
int fmt;
int sync;
int synco;
int synci;
int fs_length;
int slots;
int slot_width;
int slot_mask;
int data_size;
unsigned int spdif_frm_cnt;
struct snd_aes_iec958 iec958;
struct mutex ctrl_lock; /* protect resources accessed by controls */
};
enum stm32_sai_fifo_th {
STM_SAI_FIFO_TH_EMPTY,
STM_SAI_FIFO_TH_QUARTER,
STM_SAI_FIFO_TH_HALF,
STM_SAI_FIFO_TH_3_QUARTER,
STM_SAI_FIFO_TH_FULL,
};
static bool stm32_sai_sub_readable_reg(