18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Ola Lilja <ola.o.lilja@stericsson.com>, 68c2ecf20Sopenharmony_ci * Roger Nilsson <roger.xr.nilsson@stericsson.com> 78c2ecf20Sopenharmony_ci * for ST-Ericsson. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * License terms: 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/bitops.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/mfd/dbx500-prcmu.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-ux500-msp.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <sound/soc.h> 238c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 248c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "ux500_msp_i2s.h" 278c2ecf20Sopenharmony_ci#include "ux500_msp_dai.h" 288c2ecf20Sopenharmony_ci#include "ux500_pcm.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int setup_pcm_multichan(struct snd_soc_dai *dai, 318c2ecf20Sopenharmony_ci struct ux500_msp_config *msp_config) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 348c2ecf20Sopenharmony_ci struct msp_multichannel_config *multi = 358c2ecf20Sopenharmony_ci &msp_config->multichannel_config; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (drvdata->slots > 1) { 388c2ecf20Sopenharmony_ci msp_config->multichannel_configured = 1; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci multi->tx_multichannel_enable = true; 418c2ecf20Sopenharmony_ci multi->rx_multichannel_enable = true; 428c2ecf20Sopenharmony_ci multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci multi->tx_channel_0_enable = drvdata->tx_mask; 458c2ecf20Sopenharmony_ci multi->tx_channel_1_enable = 0; 468c2ecf20Sopenharmony_ci multi->tx_channel_2_enable = 0; 478c2ecf20Sopenharmony_ci multi->tx_channel_3_enable = 0; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci multi->rx_channel_0_enable = drvdata->rx_mask; 508c2ecf20Sopenharmony_ci multi->rx_channel_1_enable = 0; 518c2ecf20Sopenharmony_ci multi->rx_channel_2_enable = 0; 528c2ecf20Sopenharmony_ci multi->rx_channel_3_enable = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci dev_dbg(dai->dev, 558c2ecf20Sopenharmony_ci "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n", 568c2ecf20Sopenharmony_ci __func__, drvdata->slots, multi->tx_channel_0_enable, 578c2ecf20Sopenharmony_ci multi->rx_channel_0_enable); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, 648c2ecf20Sopenharmony_ci struct msp_protdesc *prot_desc) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci switch (drvdata->slots) { 698c2ecf20Sopenharmony_ci case 1: 708c2ecf20Sopenharmony_ci switch (rate) { 718c2ecf20Sopenharmony_ci case 8000: 728c2ecf20Sopenharmony_ci prot_desc->frame_period = 738c2ecf20Sopenharmony_ci FRAME_PER_SINGLE_SLOT_8_KHZ; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci case 16000: 778c2ecf20Sopenharmony_ci prot_desc->frame_period = 788c2ecf20Sopenharmony_ci FRAME_PER_SINGLE_SLOT_16_KHZ; 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci case 44100: 828c2ecf20Sopenharmony_ci prot_desc->frame_period = 838c2ecf20Sopenharmony_ci FRAME_PER_SINGLE_SLOT_44_1_KHZ; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci case 48000: 878c2ecf20Sopenharmony_ci prot_desc->frame_period = 888c2ecf20Sopenharmony_ci FRAME_PER_SINGLE_SLOT_48_KHZ; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci default: 928c2ecf20Sopenharmony_ci dev_err(dai->dev, 938c2ecf20Sopenharmony_ci "%s: Error: Unsupported sample-rate (freq = %d)!\n", 948c2ecf20Sopenharmony_ci __func__, rate); 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci case 2: 1008c2ecf20Sopenharmony_ci prot_desc->frame_period = FRAME_PER_2_SLOTS; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci case 8: 1048c2ecf20Sopenharmony_ci prot_desc->frame_period = FRAME_PER_8_SLOTS; 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci case 16: 1088c2ecf20Sopenharmony_ci prot_desc->frame_period = FRAME_PER_16_SLOTS; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci default: 1118c2ecf20Sopenharmony_ci dev_err(dai->dev, 1128c2ecf20Sopenharmony_ci "%s: Error: Unsupported slot-count (slots = %d)!\n", 1138c2ecf20Sopenharmony_ci __func__, drvdata->slots); 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci prot_desc->clocks_per_frame = 1188c2ecf20Sopenharmony_ci prot_desc->frame_period+1; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: Clocks per frame: %u\n", 1218c2ecf20Sopenharmony_ci __func__, 1228c2ecf20Sopenharmony_ci prot_desc->clocks_per_frame); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, 1288c2ecf20Sopenharmony_ci struct msp_protdesc *prot_desc) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci u32 frame_length = MSP_FRAME_LEN_1; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci prot_desc->frame_width = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci switch (drvdata->slots) { 1378c2ecf20Sopenharmony_ci case 1: 1388c2ecf20Sopenharmony_ci frame_length = MSP_FRAME_LEN_1; 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci case 2: 1428c2ecf20Sopenharmony_ci frame_length = MSP_FRAME_LEN_2; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci case 8: 1468c2ecf20Sopenharmony_ci frame_length = MSP_FRAME_LEN_8; 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci case 16: 1508c2ecf20Sopenharmony_ci frame_length = MSP_FRAME_LEN_16; 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci default: 1538c2ecf20Sopenharmony_ci dev_err(dai->dev, 1548c2ecf20Sopenharmony_ci "%s: Error: Unsupported slot-count (slots = %d)!\n", 1558c2ecf20Sopenharmony_ci __func__, drvdata->slots); 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci prot_desc->tx_frame_len_1 = frame_length; 1608c2ecf20Sopenharmony_ci prot_desc->rx_frame_len_1 = frame_length; 1618c2ecf20Sopenharmony_ci prot_desc->tx_frame_len_2 = frame_length; 1628c2ecf20Sopenharmony_ci prot_desc->rx_frame_len_2 = frame_length; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; 1658c2ecf20Sopenharmony_ci prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; 1668c2ecf20Sopenharmony_ci prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; 1678c2ecf20Sopenharmony_ci prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return setup_frameper(dai, rate, prot_desc); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int setup_clocking(struct snd_soc_dai *dai, 1738c2ecf20Sopenharmony_ci unsigned int fmt, 1748c2ecf20Sopenharmony_ci struct ux500_msp_config *msp_config) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 1778c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 1818c2ecf20Sopenharmony_ci msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; 1828c2ecf20Sopenharmony_ci msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci default: 1878c2ecf20Sopenharmony_ci dev_err(dai->dev, 1888c2ecf20Sopenharmony_ci "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", 1898c2ecf20Sopenharmony_ci __func__, fmt); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1958c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 1968c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: Codec is master.\n", __func__); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci msp_config->iodelay = 0x20; 1998c2ecf20Sopenharmony_ci msp_config->rx_fsync_sel = 0; 2008c2ecf20Sopenharmony_ci msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; 2018c2ecf20Sopenharmony_ci msp_config->tx_clk_sel = 0; 2028c2ecf20Sopenharmony_ci msp_config->rx_clk_sel = 0; 2038c2ecf20Sopenharmony_ci msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 2088c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci msp_config->tx_clk_sel = TX_CLK_SEL_SRG; 2118c2ecf20Sopenharmony_ci msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; 2128c2ecf20Sopenharmony_ci msp_config->rx_clk_sel = RX_CLK_SEL_SRG; 2138c2ecf20Sopenharmony_ci msp_config->rx_fsync_sel = RX_SYNC_SRG; 2148c2ecf20Sopenharmony_ci msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci default: 2198c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: Error: Unsupported master (fmt = 0x%x)!\n", 2208c2ecf20Sopenharmony_ci __func__, fmt); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return -EINVAL; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int setup_pcm_protdesc(struct snd_soc_dai *dai, 2298c2ecf20Sopenharmony_ci unsigned int fmt, 2308c2ecf20Sopenharmony_ci struct msp_protdesc *prot_desc) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci prot_desc->rx_phase_mode = MSP_SINGLE_PHASE; 2338c2ecf20Sopenharmony_ci prot_desc->tx_phase_mode = MSP_SINGLE_PHASE; 2348c2ecf20Sopenharmony_ci prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; 2358c2ecf20Sopenharmony_ci prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; 2368c2ecf20Sopenharmony_ci prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; 2378c2ecf20Sopenharmony_ci prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; 2388c2ecf20Sopenharmony_ci prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI); 2398c2ecf20Sopenharmony_ci prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) { 2428c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: DSP_A.\n", __func__); 2438c2ecf20Sopenharmony_ci prot_desc->rx_clk_pol = MSP_RISING_EDGE; 2448c2ecf20Sopenharmony_ci prot_desc->tx_clk_pol = MSP_FALLING_EDGE; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci prot_desc->rx_data_delay = MSP_DELAY_1; 2478c2ecf20Sopenharmony_ci prot_desc->tx_data_delay = MSP_DELAY_1; 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: DSP_B.\n", __func__); 2508c2ecf20Sopenharmony_ci prot_desc->rx_clk_pol = MSP_FALLING_EDGE; 2518c2ecf20Sopenharmony_ci prot_desc->tx_clk_pol = MSP_RISING_EDGE; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci prot_desc->rx_data_delay = MSP_DELAY_0; 2548c2ecf20Sopenharmony_ci prot_desc->tx_data_delay = MSP_DELAY_0; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci prot_desc->rx_half_word_swap = MSP_SWAP_NONE; 2588c2ecf20Sopenharmony_ci prot_desc->tx_half_word_swap = MSP_SWAP_NONE; 2598c2ecf20Sopenharmony_ci prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; 2608c2ecf20Sopenharmony_ci prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; 2618c2ecf20Sopenharmony_ci prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int setup_i2s_protdesc(struct msp_protdesc *prot_desc) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci prot_desc->rx_phase_mode = MSP_DUAL_PHASE; 2698c2ecf20Sopenharmony_ci prot_desc->tx_phase_mode = MSP_DUAL_PHASE; 2708c2ecf20Sopenharmony_ci prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; 2718c2ecf20Sopenharmony_ci prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; 2728c2ecf20Sopenharmony_ci prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; 2738c2ecf20Sopenharmony_ci prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; 2748c2ecf20Sopenharmony_ci prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO); 2758c2ecf20Sopenharmony_ci prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1; 2788c2ecf20Sopenharmony_ci prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1; 2798c2ecf20Sopenharmony_ci prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1; 2808c2ecf20Sopenharmony_ci prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1; 2818c2ecf20Sopenharmony_ci prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; 2828c2ecf20Sopenharmony_ci prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; 2838c2ecf20Sopenharmony_ci prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; 2848c2ecf20Sopenharmony_ci prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci prot_desc->rx_clk_pol = MSP_RISING_EDGE; 2878c2ecf20Sopenharmony_ci prot_desc->tx_clk_pol = MSP_FALLING_EDGE; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci prot_desc->rx_data_delay = MSP_DELAY_0; 2908c2ecf20Sopenharmony_ci prot_desc->tx_data_delay = MSP_DELAY_0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci prot_desc->tx_half_word_swap = MSP_SWAP_NONE; 2938c2ecf20Sopenharmony_ci prot_desc->rx_half_word_swap = MSP_SWAP_NONE; 2948c2ecf20Sopenharmony_ci prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; 2958c2ecf20Sopenharmony_ci prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; 2968c2ecf20Sopenharmony_ci prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic int setup_msp_config(struct snd_pcm_substream *substream, 3028c2ecf20Sopenharmony_ci struct snd_soc_dai *dai, 3038c2ecf20Sopenharmony_ci struct ux500_msp_config *msp_config) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 3068c2ecf20Sopenharmony_ci struct msp_protdesc *prot_desc = &msp_config->protdesc; 3078c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3088c2ecf20Sopenharmony_ci unsigned int fmt = drvdata->fmt; 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci memset(msp_config, 0, sizeof(*msp_config)); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci msp_config->f_inputclk = drvdata->master_clk; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci msp_config->tx_fifo_config = TX_FIFO_ENABLE; 3168c2ecf20Sopenharmony_ci msp_config->rx_fifo_config = RX_FIFO_ENABLE; 3178c2ecf20Sopenharmony_ci msp_config->def_elem_len = 1; 3188c2ecf20Sopenharmony_ci msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 3198c2ecf20Sopenharmony_ci MSP_DIR_TX : MSP_DIR_RX; 3208c2ecf20Sopenharmony_ci msp_config->data_size = MSP_DATA_BITS_32; 3218c2ecf20Sopenharmony_ci msp_config->frame_freq = runtime->rate; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n", 3248c2ecf20Sopenharmony_ci __func__, msp_config->f_inputclk, msp_config->frame_freq); 3258c2ecf20Sopenharmony_ci /* To avoid division by zero */ 3268c2ecf20Sopenharmony_ci prot_desc->clocks_per_frame = 1; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__, 3298c2ecf20Sopenharmony_ci runtime->rate, runtime->channels); 3308c2ecf20Sopenharmony_ci switch (fmt & 3318c2ecf20Sopenharmony_ci (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { 3328c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: 3338c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci msp_config->default_protdesc = 1; 3368c2ecf20Sopenharmony_ci msp_config->protocol = MSP_I2S_PROTOCOL; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: 3408c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci msp_config->data_size = MSP_DATA_BITS_16; 3438c2ecf20Sopenharmony_ci msp_config->protocol = MSP_I2S_PROTOCOL; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = setup_i2s_protdesc(prot_desc); 3468c2ecf20Sopenharmony_ci if (ret < 0) 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: 3528c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: 3538c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: 3548c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: 3558c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: PCM format.\n", __func__); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci msp_config->data_size = MSP_DATA_BITS_16; 3588c2ecf20Sopenharmony_ci msp_config->protocol = MSP_PCM_PROTOCOL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = setup_pcm_protdesc(dai, fmt, prot_desc); 3618c2ecf20Sopenharmony_ci if (ret < 0) 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = setup_pcm_multichan(dai, msp_config); 3658c2ecf20Sopenharmony_ci if (ret < 0) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = setup_pcm_framing(dai, runtime->rate, prot_desc); 3698c2ecf20Sopenharmony_ci if (ret < 0) 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci default: 3758c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: Error: Unsupported format (%d)!\n", 3768c2ecf20Sopenharmony_ci __func__, fmt); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return setup_clocking(dai, fmt, msp_config); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int ux500_msp_dai_startup(struct snd_pcm_substream *substream, 3848c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci int ret = 0; 3878c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, 3908c2ecf20Sopenharmony_ci snd_pcm_stream_str(substream)); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Enable regulator */ 3938c2ecf20Sopenharmony_ci ret = regulator_enable(drvdata->reg_vape); 3948c2ecf20Sopenharmony_ci if (ret != 0) { 3958c2ecf20Sopenharmony_ci dev_err(drvdata->msp->dev, 3968c2ecf20Sopenharmony_ci "%s: Failed to enable regulator!\n", __func__); 3978c2ecf20Sopenharmony_ci return ret; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Prepare and enable clocks */ 4018c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: Enabling MSP-clocks.\n", __func__); 4028c2ecf20Sopenharmony_ci ret = clk_prepare_enable(drvdata->pclk); 4038c2ecf20Sopenharmony_ci if (ret) { 4048c2ecf20Sopenharmony_ci dev_err(drvdata->msp->dev, 4058c2ecf20Sopenharmony_ci "%s: Failed to prepare/enable pclk!\n", __func__); 4068c2ecf20Sopenharmony_ci goto err_pclk; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ret = clk_prepare_enable(drvdata->clk); 4108c2ecf20Sopenharmony_ci if (ret) { 4118c2ecf20Sopenharmony_ci dev_err(drvdata->msp->dev, 4128c2ecf20Sopenharmony_ci "%s: Failed to prepare/enable clk!\n", __func__); 4138c2ecf20Sopenharmony_ci goto err_clk; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_cierr_clk: 4188c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->pclk); 4198c2ecf20Sopenharmony_cierr_pclk: 4208c2ecf20Sopenharmony_ci regulator_disable(drvdata->reg_vape); 4218c2ecf20Sopenharmony_ci return ret; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, 4258c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 4298c2ecf20Sopenharmony_ci bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, 4328c2ecf20Sopenharmony_ci snd_pcm_stream_str(substream)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (drvdata->vape_opp_constraint == 1) { 4358c2ecf20Sopenharmony_ci prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4368c2ecf20Sopenharmony_ci "ux500_msp_i2s", 50); 4378c2ecf20Sopenharmony_ci drvdata->vape_opp_constraint = 0; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (ux500_msp_i2s_close(drvdata->msp, 4418c2ecf20Sopenharmony_ci is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { 4428c2ecf20Sopenharmony_ci dev_err(dai->dev, 4438c2ecf20Sopenharmony_ci "%s: Error: MSP %d (%s): Unable to close i2s.\n", 4448c2ecf20Sopenharmony_ci __func__, dai->id, snd_pcm_stream_str(substream)); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* Disable and unprepare clocks */ 4488c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->clk); 4498c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->pclk); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Disable regulator */ 4528c2ecf20Sopenharmony_ci ret = regulator_disable(drvdata->reg_vape); 4538c2ecf20Sopenharmony_ci if (ret < 0) 4548c2ecf20Sopenharmony_ci dev_err(dai->dev, 4558c2ecf20Sopenharmony_ci "%s: ERROR: Failed to disable regulator (%d)!\n", 4568c2ecf20Sopenharmony_ci __func__, ret); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, 4608c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci int ret = 0; 4638c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 4648c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4658c2ecf20Sopenharmony_ci struct ux500_msp_config msp_config; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, 4688c2ecf20Sopenharmony_ci dai->id, snd_pcm_stream_str(substream), runtime->rate); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci setup_msp_config(substream, dai, &msp_config); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); 4738c2ecf20Sopenharmony_ci if (ret < 0) { 4748c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n", 4758c2ecf20Sopenharmony_ci __func__, ret); 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Set OPP-level */ 4808c2ecf20Sopenharmony_ci if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) && 4818c2ecf20Sopenharmony_ci (drvdata->msp->f_bitclk > 19200000)) { 4828c2ecf20Sopenharmony_ci /* If the bit-clock is higher than 19.2MHz, Vape should be 4838c2ecf20Sopenharmony_ci * run in 100% OPP. Only when bit-clock is used (MSP master) 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4868c2ecf20Sopenharmony_ci "ux500-msp-i2s", 100); 4878c2ecf20Sopenharmony_ci drvdata->vape_opp_constraint = 1; 4888c2ecf20Sopenharmony_ci } else { 4898c2ecf20Sopenharmony_ci prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4908c2ecf20Sopenharmony_ci "ux500-msp-i2s", 50); 4918c2ecf20Sopenharmony_ci drvdata->vape_opp_constraint = 0; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return ret; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, 4988c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 4998c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci unsigned int mask, slots_active; 5028c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5038c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", 5068c2ecf20Sopenharmony_ci __func__, dai->id, snd_pcm_stream_str(substream)); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 5098c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 5108c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, 5118c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 5128c2ecf20Sopenharmony_ci 1, 2); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 5168c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 5178c2ecf20Sopenharmony_ci mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 5188c2ecf20Sopenharmony_ci drvdata->tx_mask : 5198c2ecf20Sopenharmony_ci drvdata->rx_mask; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci slots_active = hweight32(mask); 5228c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_single(runtime, 5258c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 5268c2ecf20Sopenharmony_ci slots_active); 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci default: 5308c2ecf20Sopenharmony_ci dev_err(dai->dev, 5318c2ecf20Sopenharmony_ci "%s: Error: Unsupported protocol (fmt = 0x%x)!\n", 5328c2ecf20Sopenharmony_ci __func__, drvdata->fmt); 5338c2ecf20Sopenharmony_ci return -EINVAL; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, 5408c2ecf20Sopenharmony_ci unsigned int fmt) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | 5478c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_MASTER_MASK)) { 5488c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: 5498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: 5508c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: 5518c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: 5528c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: 5538c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci default: 5578c2ecf20Sopenharmony_ci dev_err(dai->dev, 5588c2ecf20Sopenharmony_ci "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n", 5598c2ecf20Sopenharmony_ci __func__, drvdata->fmt); 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 5648c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 5658c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 5668c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci default: 5708c2ecf20Sopenharmony_ci dev_err(dai->dev, 5718c2ecf20Sopenharmony_ci "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", 5728c2ecf20Sopenharmony_ci __func__, drvdata->fmt); 5738c2ecf20Sopenharmony_ci return -EINVAL; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci drvdata->fmt = fmt; 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, 5818c2ecf20Sopenharmony_ci unsigned int tx_mask, 5828c2ecf20Sopenharmony_ci unsigned int rx_mask, 5838c2ecf20Sopenharmony_ci int slots, int slot_width) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5868c2ecf20Sopenharmony_ci unsigned int cap; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci switch (slots) { 5898c2ecf20Sopenharmony_ci case 1: 5908c2ecf20Sopenharmony_ci cap = 0x01; 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci case 2: 5938c2ecf20Sopenharmony_ci cap = 0x03; 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case 8: 5968c2ecf20Sopenharmony_ci cap = 0xFF; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case 16: 5998c2ecf20Sopenharmony_ci cap = 0xFFFF; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n", 6038c2ecf20Sopenharmony_ci __func__, slots); 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci drvdata->slots = slots; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (!(slot_width == 16)) { 6098c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n", 6108c2ecf20Sopenharmony_ci __func__, slot_width); 6118c2ecf20Sopenharmony_ci return -EINVAL; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci drvdata->slot_width = slot_width; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci drvdata->tx_mask = tx_mask & cap; 6168c2ecf20Sopenharmony_ci drvdata->rx_mask = rx_mask & cap; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai, 6228c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n", 6278c2ecf20Sopenharmony_ci __func__, dai->id, clk_id, freq); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci switch (clk_id) { 6308c2ecf20Sopenharmony_ci case UX500_MSP_MASTER_CLOCK: 6318c2ecf20Sopenharmony_ci drvdata->master_clk = freq; 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci default: 6358c2ecf20Sopenharmony_ci dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n", 6368c2ecf20Sopenharmony_ci __func__, dai->id, clk_id); 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, 6448c2ecf20Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci int ret = 0; 6478c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", 6508c2ecf20Sopenharmony_ci __func__, dai->id, snd_pcm_stream_str(substream), 6518c2ecf20Sopenharmony_ci (int)drvdata->msp->id, cmd); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return ret; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6618c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *playback_dma_data; 6628c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *capture_dma_data; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci playback_dma_data = devm_kzalloc(dai->dev, 6658c2ecf20Sopenharmony_ci sizeof(*playback_dma_data), 6668c2ecf20Sopenharmony_ci GFP_KERNEL); 6678c2ecf20Sopenharmony_ci if (!playback_dma_data) 6688c2ecf20Sopenharmony_ci return -ENOMEM; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci capture_dma_data = devm_kzalloc(dai->dev, 6718c2ecf20Sopenharmony_ci sizeof(*capture_dma_data), 6728c2ecf20Sopenharmony_ci GFP_KERNEL); 6738c2ecf20Sopenharmony_ci if (!capture_dma_data) 6748c2ecf20Sopenharmony_ci return -ENOMEM; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci playback_dma_data->addr = drvdata->msp->playback_dma_data.tx_rx_addr; 6778c2ecf20Sopenharmony_ci capture_dma_data->addr = drvdata->msp->capture_dma_data.tx_rx_addr; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci playback_dma_data->maxburst = 4; 6808c2ecf20Sopenharmony_ci capture_dma_data->maxburst = 4; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, playback_dma_data, capture_dma_data); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return 0; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic int ux500_msp_dai_probe(struct snd_soc_dai *dai) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6908c2ecf20Sopenharmony_ci struct msp_i2s_platform_data *pdata = dai->dev->platform_data; 6918c2ecf20Sopenharmony_ci int ret; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (!pdata) { 6948c2ecf20Sopenharmony_ci ret = ux500_msp_dai_of_probe(dai); 6958c2ecf20Sopenharmony_ci return ret; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci drvdata->msp->playback_dma_data.data_size = drvdata->slot_width; 6998c2ecf20Sopenharmony_ci drvdata->msp->capture_dma_data.data_size = drvdata->slot_width; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, 7028c2ecf20Sopenharmony_ci &drvdata->msp->playback_dma_data, 7038c2ecf20Sopenharmony_ci &drvdata->msp->capture_dma_data); 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { 7088c2ecf20Sopenharmony_ci { 7098c2ecf20Sopenharmony_ci .set_sysclk = ux500_msp_dai_set_dai_sysclk, 7108c2ecf20Sopenharmony_ci .set_fmt = ux500_msp_dai_set_dai_fmt, 7118c2ecf20Sopenharmony_ci .set_tdm_slot = ux500_msp_dai_set_tdm_slot, 7128c2ecf20Sopenharmony_ci .startup = ux500_msp_dai_startup, 7138c2ecf20Sopenharmony_ci .shutdown = ux500_msp_dai_shutdown, 7148c2ecf20Sopenharmony_ci .prepare = ux500_msp_dai_prepare, 7158c2ecf20Sopenharmony_ci .trigger = ux500_msp_dai_trigger, 7168c2ecf20Sopenharmony_ci .hw_params = ux500_msp_dai_hw_params, 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver ux500_msp_dai_drv = { 7218c2ecf20Sopenharmony_ci .probe = ux500_msp_dai_probe, 7228c2ecf20Sopenharmony_ci .playback.channels_min = UX500_MSP_MIN_CHANNELS, 7238c2ecf20Sopenharmony_ci .playback.channels_max = UX500_MSP_MAX_CHANNELS, 7248c2ecf20Sopenharmony_ci .playback.rates = UX500_I2S_RATES, 7258c2ecf20Sopenharmony_ci .playback.formats = UX500_I2S_FORMATS, 7268c2ecf20Sopenharmony_ci .capture.channels_min = UX500_MSP_MIN_CHANNELS, 7278c2ecf20Sopenharmony_ci .capture.channels_max = UX500_MSP_MAX_CHANNELS, 7288c2ecf20Sopenharmony_ci .capture.rates = UX500_I2S_RATES, 7298c2ecf20Sopenharmony_ci .capture.formats = UX500_I2S_FORMATS, 7308c2ecf20Sopenharmony_ci .ops = ux500_msp_dai_ops, 7318c2ecf20Sopenharmony_ci}; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver ux500_msp_component = { 7348c2ecf20Sopenharmony_ci .name = "ux500-msp", 7358c2ecf20Sopenharmony_ci}; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic int ux500_msp_drv_probe(struct platform_device *pdev) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata; 7418c2ecf20Sopenharmony_ci struct msp_i2s_platform_data *pdata = pdev->dev.platform_data; 7428c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 7438c2ecf20Sopenharmony_ci int ret = 0; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (!pdata && !np) { 7468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data or Device Tree found\n"); 7478c2ecf20Sopenharmony_ci return -ENODEV; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(&pdev->dev, 7518c2ecf20Sopenharmony_ci sizeof(struct ux500_msp_i2s_drvdata), 7528c2ecf20Sopenharmony_ci GFP_KERNEL); 7538c2ecf20Sopenharmony_ci if (!drvdata) 7548c2ecf20Sopenharmony_ci return -ENOMEM; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci drvdata->fmt = 0; 7578c2ecf20Sopenharmony_ci drvdata->slots = 1; 7588c2ecf20Sopenharmony_ci drvdata->tx_mask = 0x01; 7598c2ecf20Sopenharmony_ci drvdata->rx_mask = 0x01; 7608c2ecf20Sopenharmony_ci drvdata->slot_width = 16; 7618c2ecf20Sopenharmony_ci drvdata->master_clk = MSP_INPUT_FREQ_APB; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape"); 7648c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->reg_vape)) { 7658c2ecf20Sopenharmony_ci ret = (int)PTR_ERR(drvdata->reg_vape); 7668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7678c2ecf20Sopenharmony_ci "%s: ERROR: Failed to get Vape supply (%d)!\n", 7688c2ecf20Sopenharmony_ci __func__, ret); 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci drvdata->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); 7748c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->pclk)) { 7758c2ecf20Sopenharmony_ci ret = (int)PTR_ERR(drvdata->pclk); 7768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7778c2ecf20Sopenharmony_ci "%s: ERROR: devm_clk_get of pclk failed (%d)!\n", 7788c2ecf20Sopenharmony_ci __func__, ret); 7798c2ecf20Sopenharmony_ci return ret; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci drvdata->clk = devm_clk_get(&pdev->dev, NULL); 7838c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->clk)) { 7848c2ecf20Sopenharmony_ci ret = (int)PTR_ERR(drvdata->clk); 7858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7868c2ecf20Sopenharmony_ci "%s: ERROR: devm_clk_get failed (%d)!\n", 7878c2ecf20Sopenharmony_ci __func__, ret); 7888c2ecf20Sopenharmony_ci return ret; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, 7928c2ecf20Sopenharmony_ci pdev->dev.platform_data); 7938c2ecf20Sopenharmony_ci if (!drvdata->msp) { 7948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 7958c2ecf20Sopenharmony_ci "%s: ERROR: Failed to init MSP-struct (%d)!", 7968c2ecf20Sopenharmony_ci __func__, ret); 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, drvdata); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component, 8028c2ecf20Sopenharmony_ci &ux500_msp_dai_drv, 1); 8038c2ecf20Sopenharmony_ci if (ret < 0) { 8048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", 8058c2ecf20Sopenharmony_ci __func__, drvdata->msp->id); 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci ret = ux500_pcm_register_platform(pdev); 8108c2ecf20Sopenharmony_ci if (ret < 0) { 8118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8128c2ecf20Sopenharmony_ci "Error: %s: Failed to register PCM platform device!\n", 8138c2ecf20Sopenharmony_ci __func__); 8148c2ecf20Sopenharmony_ci goto err_reg_plat; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return 0; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cierr_reg_plat: 8208c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 8218c2ecf20Sopenharmony_ci return ret; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int ux500_msp_drv_remove(struct platform_device *pdev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci ux500_pcm_unregister_platform(pdev); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci return 0; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic const struct of_device_id ux500_msp_i2s_match[] = { 8408c2ecf20Sopenharmony_ci { .compatible = "stericsson,ux500-msp-i2s", }, 8418c2ecf20Sopenharmony_ci {}, 8428c2ecf20Sopenharmony_ci}; 8438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ux500_msp_i2s_match); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic struct platform_driver msp_i2s_driver = { 8468c2ecf20Sopenharmony_ci .driver = { 8478c2ecf20Sopenharmony_ci .name = "ux500-msp-i2s", 8488c2ecf20Sopenharmony_ci .of_match_table = ux500_msp_i2s_match, 8498c2ecf20Sopenharmony_ci }, 8508c2ecf20Sopenharmony_ci .probe = ux500_msp_drv_probe, 8518c2ecf20Sopenharmony_ci .remove = ux500_msp_drv_remove, 8528c2ecf20Sopenharmony_ci}; 8538c2ecf20Sopenharmony_cimodule_platform_driver(msp_i2s_driver); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 856