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 * Sandeep Kaushik <sandeep.kaushik@st.com> 88c2ecf20Sopenharmony_ci * for ST-Ericsson. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * License terms: 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-ux500-msp.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <sound/soc.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "ux500_msp_i2s.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* Protocol desciptors */ 268c2ecf20Sopenharmony_cistatic const struct msp_protdesc prot_descs[] = { 278c2ecf20Sopenharmony_ci { /* I2S */ 288c2ecf20Sopenharmony_ci MSP_SINGLE_PHASE, 298c2ecf20Sopenharmony_ci MSP_SINGLE_PHASE, 308c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_IMEDIATE, 318c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_IMEDIATE, 328c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 338c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 348c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 358c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 368c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 378c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 388c2ecf20Sopenharmony_ci MSP_ELEM_LEN_32, 398c2ecf20Sopenharmony_ci MSP_ELEM_LEN_32, 408c2ecf20Sopenharmony_ci MSP_ELEM_LEN_32, 418c2ecf20Sopenharmony_ci MSP_ELEM_LEN_32, 428c2ecf20Sopenharmony_ci MSP_DELAY_1, 438c2ecf20Sopenharmony_ci MSP_DELAY_1, 448c2ecf20Sopenharmony_ci MSP_RISING_EDGE, 458c2ecf20Sopenharmony_ci MSP_FALLING_EDGE, 468c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_LO, 478c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_LO, 488c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 498c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 508c2ecf20Sopenharmony_ci MSP_COMPRESS_MODE_LINEAR, 518c2ecf20Sopenharmony_ci MSP_EXPAND_MODE_LINEAR, 528c2ecf20Sopenharmony_ci MSP_FSYNC_IGNORE, 538c2ecf20Sopenharmony_ci 31, 548c2ecf20Sopenharmony_ci 15, 558c2ecf20Sopenharmony_ci 32, 568c2ecf20Sopenharmony_ci }, { /* PCM */ 578c2ecf20Sopenharmony_ci MSP_DUAL_PHASE, 588c2ecf20Sopenharmony_ci MSP_DUAL_PHASE, 598c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_FSYNC, 608c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_FSYNC, 618c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 628c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 638c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 648c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 658c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 668c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 678c2ecf20Sopenharmony_ci MSP_ELEM_LEN_16, 688c2ecf20Sopenharmony_ci MSP_ELEM_LEN_16, 698c2ecf20Sopenharmony_ci MSP_ELEM_LEN_16, 708c2ecf20Sopenharmony_ci MSP_ELEM_LEN_16, 718c2ecf20Sopenharmony_ci MSP_DELAY_0, 728c2ecf20Sopenharmony_ci MSP_DELAY_0, 738c2ecf20Sopenharmony_ci MSP_RISING_EDGE, 748c2ecf20Sopenharmony_ci MSP_FALLING_EDGE, 758c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_HI, 768c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_HI, 778c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 788c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 798c2ecf20Sopenharmony_ci MSP_COMPRESS_MODE_LINEAR, 808c2ecf20Sopenharmony_ci MSP_EXPAND_MODE_LINEAR, 818c2ecf20Sopenharmony_ci MSP_FSYNC_IGNORE, 828c2ecf20Sopenharmony_ci 255, 838c2ecf20Sopenharmony_ci 0, 848c2ecf20Sopenharmony_ci 256, 858c2ecf20Sopenharmony_ci }, { /* Companded PCM */ 868c2ecf20Sopenharmony_ci MSP_SINGLE_PHASE, 878c2ecf20Sopenharmony_ci MSP_SINGLE_PHASE, 888c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_FSYNC, 898c2ecf20Sopenharmony_ci MSP_PHASE2_START_MODE_FSYNC, 908c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 918c2ecf20Sopenharmony_ci MSP_BTF_MS_BIT_FIRST, 928c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 938c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 948c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 958c2ecf20Sopenharmony_ci MSP_FRAME_LEN_1, 968c2ecf20Sopenharmony_ci MSP_ELEM_LEN_8, 978c2ecf20Sopenharmony_ci MSP_ELEM_LEN_8, 988c2ecf20Sopenharmony_ci MSP_ELEM_LEN_8, 998c2ecf20Sopenharmony_ci MSP_ELEM_LEN_8, 1008c2ecf20Sopenharmony_ci MSP_DELAY_0, 1018c2ecf20Sopenharmony_ci MSP_DELAY_0, 1028c2ecf20Sopenharmony_ci MSP_RISING_EDGE, 1038c2ecf20Sopenharmony_ci MSP_RISING_EDGE, 1048c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_HI, 1058c2ecf20Sopenharmony_ci MSP_FSYNC_POL_ACT_HI, 1068c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 1078c2ecf20Sopenharmony_ci MSP_SWAP_NONE, 1088c2ecf20Sopenharmony_ci MSP_COMPRESS_MODE_LINEAR, 1098c2ecf20Sopenharmony_ci MSP_EXPAND_MODE_LINEAR, 1108c2ecf20Sopenharmony_ci MSP_FSYNC_IGNORE, 1118c2ecf20Sopenharmony_ci 255, 1128c2ecf20Sopenharmony_ci 0, 1138c2ecf20Sopenharmony_ci 256, 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void set_prot_desc_tx(struct ux500_msp *msp, 1188c2ecf20Sopenharmony_ci struct msp_protdesc *protdesc, 1198c2ecf20Sopenharmony_ci enum msp_data_size data_size) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci u32 temp_reg = 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ENABLE_BIT(protdesc->tx_phase_mode); 1248c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_START_MODE_BIT(protdesc->tx_phase2_start_mode); 1258c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->tx_frame_len_1); 1268c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->tx_frame_len_2); 1278c2ecf20Sopenharmony_ci if (msp->def_elem_len) { 1288c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->tx_elem_len_1); 1298c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->tx_elem_len_2); 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); 1328c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci temp_reg |= MSP_DATA_DELAY_BITS(protdesc->tx_data_delay); 1358c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->tx_byte_order); 1368c2ecf20Sopenharmony_ci temp_reg |= MSP_FSYNC_POL(protdesc->tx_fsync_pol); 1378c2ecf20Sopenharmony_ci temp_reg |= MSP_DATA_WORD_SWAP(protdesc->tx_half_word_swap); 1388c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->compression_mode); 1398c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci writel(temp_reg, msp->registers + MSP_TCF); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void set_prot_desc_rx(struct ux500_msp *msp, 1458c2ecf20Sopenharmony_ci struct msp_protdesc *protdesc, 1468c2ecf20Sopenharmony_ci enum msp_data_size data_size) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u32 temp_reg = 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ENABLE_BIT(protdesc->rx_phase_mode); 1518c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_START_MODE_BIT(protdesc->rx_phase2_start_mode); 1528c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_FRAME_LEN_BITS(protdesc->rx_frame_len_1); 1538c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_FRAME_LEN_BITS(protdesc->rx_frame_len_2); 1548c2ecf20Sopenharmony_ci if (msp->def_elem_len) { 1558c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_ELEM_LEN_BITS(protdesc->rx_elem_len_1); 1568c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ELEM_LEN_BITS(protdesc->rx_elem_len_2); 1578c2ecf20Sopenharmony_ci } else { 1588c2ecf20Sopenharmony_ci temp_reg |= MSP_P1_ELEM_LEN_BITS(data_size); 1598c2ecf20Sopenharmony_ci temp_reg |= MSP_P2_ELEM_LEN_BITS(data_size); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci temp_reg |= MSP_DATA_DELAY_BITS(protdesc->rx_data_delay); 1638c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_ENDIANNES_BIT(protdesc->rx_byte_order); 1648c2ecf20Sopenharmony_ci temp_reg |= MSP_FSYNC_POL(protdesc->rx_fsync_pol); 1658c2ecf20Sopenharmony_ci temp_reg |= MSP_DATA_WORD_SWAP(protdesc->rx_half_word_swap); 1668c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_COMPANDING_MODE(protdesc->expansion_mode); 1678c2ecf20Sopenharmony_ci temp_reg |= MSP_SET_FSYNC_IGNORE(protdesc->frame_sync_ignore); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci writel(temp_reg, msp->registers + MSP_RCF); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int configure_protocol(struct ux500_msp *msp, 1738c2ecf20Sopenharmony_ci struct ux500_msp_config *config) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct msp_protdesc *protdesc; 1768c2ecf20Sopenharmony_ci enum msp_data_size data_size; 1778c2ecf20Sopenharmony_ci u32 temp_reg = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci data_size = config->data_size; 1808c2ecf20Sopenharmony_ci msp->def_elem_len = config->def_elem_len; 1818c2ecf20Sopenharmony_ci if (config->default_protdesc == 1) { 1828c2ecf20Sopenharmony_ci if (config->protocol >= MSP_INVALID_PROTOCOL) { 1838c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: Invalid protocol!\n", 1848c2ecf20Sopenharmony_ci __func__); 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci protdesc = 1888c2ecf20Sopenharmony_ci (struct msp_protdesc *)&prot_descs[config->protocol]; 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci protdesc = (struct msp_protdesc *)&config->protdesc; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (data_size < MSP_DATA_BITS_DEFAULT || data_size > MSP_DATA_BITS_32) { 1948c2ecf20Sopenharmony_ci dev_err(msp->dev, 1958c2ecf20Sopenharmony_ci "%s: ERROR: Invalid data-size requested (data_size = %d)!\n", 1968c2ecf20Sopenharmony_ci __func__, data_size); 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (config->direction & MSP_DIR_TX) 2018c2ecf20Sopenharmony_ci set_prot_desc_tx(msp, protdesc, data_size); 2028c2ecf20Sopenharmony_ci if (config->direction & MSP_DIR_RX) 2038c2ecf20Sopenharmony_ci set_prot_desc_rx(msp, protdesc, data_size); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* The code below should not be separated. */ 2068c2ecf20Sopenharmony_ci temp_reg = readl(msp->registers + MSP_GCR) & ~TX_CLK_POL_RISING; 2078c2ecf20Sopenharmony_ci temp_reg |= MSP_TX_CLKPOL_BIT(~protdesc->tx_clk_pol); 2088c2ecf20Sopenharmony_ci writel(temp_reg, msp->registers + MSP_GCR); 2098c2ecf20Sopenharmony_ci temp_reg = readl(msp->registers + MSP_GCR) & ~RX_CLK_POL_RISING; 2108c2ecf20Sopenharmony_ci temp_reg |= MSP_RX_CLKPOL_BIT(protdesc->rx_clk_pol); 2118c2ecf20Sopenharmony_ci writel(temp_reg, msp->registers + MSP_GCR); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int setup_bitclk(struct ux500_msp *msp, struct ux500_msp_config *config) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u32 reg_val_GCR; 2198c2ecf20Sopenharmony_ci u32 frame_per = 0; 2208c2ecf20Sopenharmony_ci u32 sck_div = 0; 2218c2ecf20Sopenharmony_ci u32 frame_width = 0; 2228c2ecf20Sopenharmony_ci u32 temp_reg = 0; 2238c2ecf20Sopenharmony_ci struct msp_protdesc *protdesc = NULL; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 2268c2ecf20Sopenharmony_ci writel(reg_val_GCR & ~SRG_ENABLE, msp->registers + MSP_GCR); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (config->default_protdesc) 2298c2ecf20Sopenharmony_ci protdesc = 2308c2ecf20Sopenharmony_ci (struct msp_protdesc *)&prot_descs[config->protocol]; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci protdesc = (struct msp_protdesc *)&config->protdesc; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci switch (config->protocol) { 2358c2ecf20Sopenharmony_ci case MSP_PCM_PROTOCOL: 2368c2ecf20Sopenharmony_ci case MSP_PCM_COMPAND_PROTOCOL: 2378c2ecf20Sopenharmony_ci frame_width = protdesc->frame_width; 2388c2ecf20Sopenharmony_ci sck_div = config->f_inputclk / (config->frame_freq * 2398c2ecf20Sopenharmony_ci (protdesc->clocks_per_frame)); 2408c2ecf20Sopenharmony_ci frame_per = protdesc->frame_period; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case MSP_I2S_PROTOCOL: 2438c2ecf20Sopenharmony_ci frame_width = protdesc->frame_width; 2448c2ecf20Sopenharmony_ci sck_div = config->f_inputclk / (config->frame_freq * 2458c2ecf20Sopenharmony_ci (protdesc->clocks_per_frame)); 2468c2ecf20Sopenharmony_ci frame_per = protdesc->frame_period; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci default: 2498c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: Unknown protocol (%d)!\n", 2508c2ecf20Sopenharmony_ci __func__, 2518c2ecf20Sopenharmony_ci config->protocol); 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci temp_reg = (sck_div - 1) & SCK_DIV_MASK; 2568c2ecf20Sopenharmony_ci temp_reg |= FRAME_WIDTH_BITS(frame_width); 2578c2ecf20Sopenharmony_ci temp_reg |= FRAME_PERIOD_BITS(frame_per); 2588c2ecf20Sopenharmony_ci writel(temp_reg, msp->registers + MSP_SRG); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci msp->f_bitclk = (config->f_inputclk)/(sck_div + 1); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Enable bit-clock */ 2638c2ecf20Sopenharmony_ci udelay(100); 2648c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 2658c2ecf20Sopenharmony_ci writel(reg_val_GCR | SRG_ENABLE, msp->registers + MSP_GCR); 2668c2ecf20Sopenharmony_ci udelay(100); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int configure_multichannel(struct ux500_msp *msp, 2728c2ecf20Sopenharmony_ci struct ux500_msp_config *config) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct msp_protdesc *protdesc; 2758c2ecf20Sopenharmony_ci struct msp_multichannel_config *mcfg; 2768c2ecf20Sopenharmony_ci u32 reg_val_MCR; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (config->default_protdesc == 1) { 2798c2ecf20Sopenharmony_ci if (config->protocol >= MSP_INVALID_PROTOCOL) { 2808c2ecf20Sopenharmony_ci dev_err(msp->dev, 2818c2ecf20Sopenharmony_ci "%s: ERROR: Invalid protocol (%d)!\n", 2828c2ecf20Sopenharmony_ci __func__, config->protocol); 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci protdesc = (struct msp_protdesc *) 2868c2ecf20Sopenharmony_ci &prot_descs[config->protocol]; 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci protdesc = (struct msp_protdesc *)&config->protdesc; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mcfg = &config->multichannel_config; 2928c2ecf20Sopenharmony_ci if (mcfg->tx_multichannel_enable) { 2938c2ecf20Sopenharmony_ci if (protdesc->tx_phase_mode == MSP_SINGLE_PHASE) { 2948c2ecf20Sopenharmony_ci reg_val_MCR = readl(msp->registers + MSP_MCR); 2958c2ecf20Sopenharmony_ci writel(reg_val_MCR | (mcfg->tx_multichannel_enable ? 2968c2ecf20Sopenharmony_ci 1 << TMCEN_BIT : 0), 2978c2ecf20Sopenharmony_ci msp->registers + MSP_MCR); 2988c2ecf20Sopenharmony_ci writel(mcfg->tx_channel_0_enable, 2998c2ecf20Sopenharmony_ci msp->registers + MSP_TCE0); 3008c2ecf20Sopenharmony_ci writel(mcfg->tx_channel_1_enable, 3018c2ecf20Sopenharmony_ci msp->registers + MSP_TCE1); 3028c2ecf20Sopenharmony_ci writel(mcfg->tx_channel_2_enable, 3038c2ecf20Sopenharmony_ci msp->registers + MSP_TCE2); 3048c2ecf20Sopenharmony_ci writel(mcfg->tx_channel_3_enable, 3058c2ecf20Sopenharmony_ci msp->registers + MSP_TCE3); 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci dev_err(msp->dev, 3088c2ecf20Sopenharmony_ci "%s: ERROR: Only single-phase supported (TX-mode: %d)!\n", 3098c2ecf20Sopenharmony_ci __func__, protdesc->tx_phase_mode); 3108c2ecf20Sopenharmony_ci return -EINVAL; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci if (mcfg->rx_multichannel_enable) { 3148c2ecf20Sopenharmony_ci if (protdesc->rx_phase_mode == MSP_SINGLE_PHASE) { 3158c2ecf20Sopenharmony_ci reg_val_MCR = readl(msp->registers + MSP_MCR); 3168c2ecf20Sopenharmony_ci writel(reg_val_MCR | (mcfg->rx_multichannel_enable ? 3178c2ecf20Sopenharmony_ci 1 << RMCEN_BIT : 0), 3188c2ecf20Sopenharmony_ci msp->registers + MSP_MCR); 3198c2ecf20Sopenharmony_ci writel(mcfg->rx_channel_0_enable, 3208c2ecf20Sopenharmony_ci msp->registers + MSP_RCE0); 3218c2ecf20Sopenharmony_ci writel(mcfg->rx_channel_1_enable, 3228c2ecf20Sopenharmony_ci msp->registers + MSP_RCE1); 3238c2ecf20Sopenharmony_ci writel(mcfg->rx_channel_2_enable, 3248c2ecf20Sopenharmony_ci msp->registers + MSP_RCE2); 3258c2ecf20Sopenharmony_ci writel(mcfg->rx_channel_3_enable, 3268c2ecf20Sopenharmony_ci msp->registers + MSP_RCE3); 3278c2ecf20Sopenharmony_ci } else { 3288c2ecf20Sopenharmony_ci dev_err(msp->dev, 3298c2ecf20Sopenharmony_ci "%s: ERROR: Only single-phase supported (RX-mode: %d)!\n", 3308c2ecf20Sopenharmony_ci __func__, protdesc->rx_phase_mode); 3318c2ecf20Sopenharmony_ci return -EINVAL; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci if (mcfg->rx_comparison_enable_mode) { 3348c2ecf20Sopenharmony_ci reg_val_MCR = readl(msp->registers + MSP_MCR); 3358c2ecf20Sopenharmony_ci writel(reg_val_MCR | 3368c2ecf20Sopenharmony_ci (mcfg->rx_comparison_enable_mode << RCMPM_BIT), 3378c2ecf20Sopenharmony_ci msp->registers + MSP_MCR); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci writel(mcfg->comparison_mask, 3408c2ecf20Sopenharmony_ci msp->registers + MSP_RCM); 3418c2ecf20Sopenharmony_ci writel(mcfg->comparison_value, 3428c2ecf20Sopenharmony_ci msp->registers + MSP_RCV); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int status = 0; 3538c2ecf20Sopenharmony_ci u32 reg_val_DMACR, reg_val_GCR; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Configure msp with protocol dependent settings */ 3568c2ecf20Sopenharmony_ci configure_protocol(msp, config); 3578c2ecf20Sopenharmony_ci setup_bitclk(msp, config); 3588c2ecf20Sopenharmony_ci if (config->multichannel_configured == 1) { 3598c2ecf20Sopenharmony_ci status = configure_multichannel(msp, config); 3608c2ecf20Sopenharmony_ci if (status) 3618c2ecf20Sopenharmony_ci dev_warn(msp->dev, 3628c2ecf20Sopenharmony_ci "%s: WARN: configure_multichannel failed (%d)!\n", 3638c2ecf20Sopenharmony_ci __func__, status); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Make sure the correct DMA-directions are configured */ 3678c2ecf20Sopenharmony_ci if ((config->direction & MSP_DIR_RX) && 3688c2ecf20Sopenharmony_ci !msp->capture_dma_data.dma_cfg) { 3698c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!", 3708c2ecf20Sopenharmony_ci __func__); 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci if ((config->direction == MSP_DIR_TX) && 3748c2ecf20Sopenharmony_ci !msp->playback_dma_data.dma_cfg) { 3758c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!", 3768c2ecf20Sopenharmony_ci __func__); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci reg_val_DMACR = readl(msp->registers + MSP_DMACR); 3818c2ecf20Sopenharmony_ci if (config->direction & MSP_DIR_RX) 3828c2ecf20Sopenharmony_ci reg_val_DMACR |= RX_DMA_ENABLE; 3838c2ecf20Sopenharmony_ci if (config->direction & MSP_DIR_TX) 3848c2ecf20Sopenharmony_ci reg_val_DMACR |= TX_DMA_ENABLE; 3858c2ecf20Sopenharmony_ci writel(reg_val_DMACR, msp->registers + MSP_DMACR); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci writel(config->iodelay, msp->registers + MSP_IODLY); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Enable frame generation logic */ 3908c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 3918c2ecf20Sopenharmony_ci writel(reg_val_GCR | FRAME_GEN_ENABLE, msp->registers + MSP_GCR); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return status; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void flush_fifo_rx(struct ux500_msp *msp) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci u32 reg_val_GCR, reg_val_FLR; 3998c2ecf20Sopenharmony_ci u32 limit = 32; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 4028c2ecf20Sopenharmony_ci writel(reg_val_GCR | RX_ENABLE, msp->registers + MSP_GCR); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci reg_val_FLR = readl(msp->registers + MSP_FLR); 4058c2ecf20Sopenharmony_ci while (!(reg_val_FLR & RX_FIFO_EMPTY) && limit--) { 4068c2ecf20Sopenharmony_ci readl(msp->registers + MSP_DR); 4078c2ecf20Sopenharmony_ci reg_val_FLR = readl(msp->registers + MSP_FLR); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci writel(reg_val_GCR, msp->registers + MSP_GCR); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void flush_fifo_tx(struct ux500_msp *msp) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci u32 reg_val_GCR, reg_val_FLR; 4168c2ecf20Sopenharmony_ci u32 limit = 32; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 4198c2ecf20Sopenharmony_ci writel(reg_val_GCR | TX_ENABLE, msp->registers + MSP_GCR); 4208c2ecf20Sopenharmony_ci writel(MSP_ITCR_ITEN | MSP_ITCR_TESTFIFO, msp->registers + MSP_ITCR); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci reg_val_FLR = readl(msp->registers + MSP_FLR); 4238c2ecf20Sopenharmony_ci while (!(reg_val_FLR & TX_FIFO_EMPTY) && limit--) { 4248c2ecf20Sopenharmony_ci readl(msp->registers + MSP_TSTDR); 4258c2ecf20Sopenharmony_ci reg_val_FLR = readl(msp->registers + MSP_FLR); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci writel(0x0, msp->registers + MSP_ITCR); 4288c2ecf20Sopenharmony_ci writel(reg_val_GCR, msp->registers + MSP_GCR); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciint ux500_msp_i2s_open(struct ux500_msp *msp, 4328c2ecf20Sopenharmony_ci struct ux500_msp_config *config) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci u32 old_reg, new_reg, mask; 4358c2ecf20Sopenharmony_ci int res; 4368c2ecf20Sopenharmony_ci unsigned int tx_sel, rx_sel, tx_busy, rx_busy; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (in_interrupt()) { 4398c2ecf20Sopenharmony_ci dev_err(msp->dev, 4408c2ecf20Sopenharmony_ci "%s: ERROR: Open called in interrupt context!\n", 4418c2ecf20Sopenharmony_ci __func__); 4428c2ecf20Sopenharmony_ci return -1; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci tx_sel = (config->direction & MSP_DIR_TX) > 0; 4468c2ecf20Sopenharmony_ci rx_sel = (config->direction & MSP_DIR_RX) > 0; 4478c2ecf20Sopenharmony_ci if (!tx_sel && !rx_sel) { 4488c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: Error: No direction selected!\n", 4498c2ecf20Sopenharmony_ci __func__); 4508c2ecf20Sopenharmony_ci return -EINVAL; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci tx_busy = (msp->dir_busy & MSP_DIR_TX) > 0; 4548c2ecf20Sopenharmony_ci rx_busy = (msp->dir_busy & MSP_DIR_RX) > 0; 4558c2ecf20Sopenharmony_ci if (tx_busy && tx_sel) { 4568c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: Error: TX is in use!\n", __func__); 4578c2ecf20Sopenharmony_ci return -EBUSY; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci if (rx_busy && rx_sel) { 4608c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: Error: RX is in use!\n", __func__); 4618c2ecf20Sopenharmony_ci return -EBUSY; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci msp->dir_busy |= (tx_sel ? MSP_DIR_TX : 0) | (rx_sel ? MSP_DIR_RX : 0); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* First do the global config register */ 4678c2ecf20Sopenharmony_ci mask = RX_CLK_SEL_MASK | TX_CLK_SEL_MASK | RX_FSYNC_MASK | 4688c2ecf20Sopenharmony_ci TX_FSYNC_MASK | RX_SYNC_SEL_MASK | TX_SYNC_SEL_MASK | 4698c2ecf20Sopenharmony_ci RX_FIFO_ENABLE_MASK | TX_FIFO_ENABLE_MASK | SRG_CLK_SEL_MASK | 4708c2ecf20Sopenharmony_ci LOOPBACK_MASK | TX_EXTRA_DELAY_MASK; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci new_reg = (config->tx_clk_sel | config->rx_clk_sel | 4738c2ecf20Sopenharmony_ci config->rx_fsync_pol | config->tx_fsync_pol | 4748c2ecf20Sopenharmony_ci config->rx_fsync_sel | config->tx_fsync_sel | 4758c2ecf20Sopenharmony_ci config->rx_fifo_config | config->tx_fifo_config | 4768c2ecf20Sopenharmony_ci config->srg_clk_sel | config->loopback_enable | 4778c2ecf20Sopenharmony_ci config->tx_data_enable); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci old_reg = readl(msp->registers + MSP_GCR); 4808c2ecf20Sopenharmony_ci old_reg &= ~mask; 4818c2ecf20Sopenharmony_ci new_reg |= old_reg; 4828c2ecf20Sopenharmony_ci writel(new_reg, msp->registers + MSP_GCR); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci res = enable_msp(msp, config); 4858c2ecf20Sopenharmony_ci if (res < 0) { 4868c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: enable_msp failed (%d)!\n", 4878c2ecf20Sopenharmony_ci __func__, res); 4888c2ecf20Sopenharmony_ci return -EBUSY; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci if (config->loopback_enable & 0x80) 4918c2ecf20Sopenharmony_ci msp->loopback_enable = 1; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Flush FIFOs */ 4948c2ecf20Sopenharmony_ci flush_fifo_tx(msp); 4958c2ecf20Sopenharmony_ci flush_fifo_rx(msp); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci msp->msp_state = MSP_STATE_CONFIGURED; 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void disable_msp_rx(struct ux500_msp *msp) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 5068c2ecf20Sopenharmony_ci writel(reg_val_GCR & ~RX_ENABLE, msp->registers + MSP_GCR); 5078c2ecf20Sopenharmony_ci reg_val_DMACR = readl(msp->registers + MSP_DMACR); 5088c2ecf20Sopenharmony_ci writel(reg_val_DMACR & ~RX_DMA_ENABLE, msp->registers + MSP_DMACR); 5098c2ecf20Sopenharmony_ci reg_val_IMSC = readl(msp->registers + MSP_IMSC); 5108c2ecf20Sopenharmony_ci writel(reg_val_IMSC & 5118c2ecf20Sopenharmony_ci ~(RX_SERVICE_INT | RX_OVERRUN_ERROR_INT), 5128c2ecf20Sopenharmony_ci msp->registers + MSP_IMSC); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci msp->dir_busy &= ~MSP_DIR_RX; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic void disable_msp_tx(struct ux500_msp *msp) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci u32 reg_val_GCR, reg_val_DMACR, reg_val_IMSC; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 5228c2ecf20Sopenharmony_ci writel(reg_val_GCR & ~TX_ENABLE, msp->registers + MSP_GCR); 5238c2ecf20Sopenharmony_ci reg_val_DMACR = readl(msp->registers + MSP_DMACR); 5248c2ecf20Sopenharmony_ci writel(reg_val_DMACR & ~TX_DMA_ENABLE, msp->registers + MSP_DMACR); 5258c2ecf20Sopenharmony_ci reg_val_IMSC = readl(msp->registers + MSP_IMSC); 5268c2ecf20Sopenharmony_ci writel(reg_val_IMSC & 5278c2ecf20Sopenharmony_ci ~(TX_SERVICE_INT | TX_UNDERRUN_ERR_INT), 5288c2ecf20Sopenharmony_ci msp->registers + MSP_IMSC); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci msp->dir_busy &= ~MSP_DIR_TX; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int disable_msp(struct ux500_msp *msp, unsigned int dir) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci u32 reg_val_GCR; 5368c2ecf20Sopenharmony_ci unsigned int disable_tx, disable_rx; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 5398c2ecf20Sopenharmony_ci disable_tx = dir & MSP_DIR_TX; 5408c2ecf20Sopenharmony_ci disable_rx = dir & MSP_DIR_TX; 5418c2ecf20Sopenharmony_ci if (disable_tx && disable_rx) { 5428c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 5438c2ecf20Sopenharmony_ci writel(reg_val_GCR | LOOPBACK_MASK, 5448c2ecf20Sopenharmony_ci msp->registers + MSP_GCR); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Flush TX-FIFO */ 5478c2ecf20Sopenharmony_ci flush_fifo_tx(msp); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Disable TX-channel */ 5508c2ecf20Sopenharmony_ci writel((readl(msp->registers + MSP_GCR) & 5518c2ecf20Sopenharmony_ci (~TX_ENABLE)), msp->registers + MSP_GCR); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Flush RX-FIFO */ 5548c2ecf20Sopenharmony_ci flush_fifo_rx(msp); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Disable Loopback and Receive channel */ 5578c2ecf20Sopenharmony_ci writel((readl(msp->registers + MSP_GCR) & 5588c2ecf20Sopenharmony_ci (~(RX_ENABLE | LOOPBACK_MASK))), 5598c2ecf20Sopenharmony_ci msp->registers + MSP_GCR); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci disable_msp_tx(msp); 5628c2ecf20Sopenharmony_ci disable_msp_rx(msp); 5638c2ecf20Sopenharmony_ci } else if (disable_tx) 5648c2ecf20Sopenharmony_ci disable_msp_tx(msp); 5658c2ecf20Sopenharmony_ci else if (disable_rx) 5668c2ecf20Sopenharmony_ci disable_msp_rx(msp); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ciint ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci u32 reg_val_GCR, enable_bit; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (msp->msp_state == MSP_STATE_IDLE) { 5768c2ecf20Sopenharmony_ci dev_err(msp->dev, "%s: ERROR: MSP is not configured!\n", 5778c2ecf20Sopenharmony_ci __func__); 5788c2ecf20Sopenharmony_ci return -EINVAL; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci switch (cmd) { 5828c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 5838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 5848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 5858c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) 5868c2ecf20Sopenharmony_ci enable_bit = TX_ENABLE; 5878c2ecf20Sopenharmony_ci else 5888c2ecf20Sopenharmony_ci enable_bit = RX_ENABLE; 5898c2ecf20Sopenharmony_ci reg_val_GCR = readl(msp->registers + MSP_GCR); 5908c2ecf20Sopenharmony_ci writel(reg_val_GCR | enable_bit, msp->registers + MSP_GCR); 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5948c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 5958c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 5968c2ecf20Sopenharmony_ci if (direction == SNDRV_PCM_STREAM_PLAYBACK) 5978c2ecf20Sopenharmony_ci disable_msp_tx(msp); 5988c2ecf20Sopenharmony_ci else 5998c2ecf20Sopenharmony_ci disable_msp_rx(msp); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci return -EINVAL; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciint ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci int status = 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci status = disable_msp(msp, dir); 6158c2ecf20Sopenharmony_ci if (msp->dir_busy == 0) { 6168c2ecf20Sopenharmony_ci /* disable sample rate and frame generators */ 6178c2ecf20Sopenharmony_ci msp->msp_state = MSP_STATE_IDLE; 6188c2ecf20Sopenharmony_ci writel((readl(msp->registers + MSP_GCR) & 6198c2ecf20Sopenharmony_ci (~(FRAME_GEN_ENABLE | SRG_ENABLE))), 6208c2ecf20Sopenharmony_ci msp->registers + MSP_GCR); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_GCR); 6238c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_TCF); 6248c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCF); 6258c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_DMACR); 6268c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_SRG); 6278c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_MCR); 6288c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCM); 6298c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCV); 6308c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_TCE0); 6318c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_TCE1); 6328c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_TCE2); 6338c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_TCE3); 6348c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCE0); 6358c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCE1); 6368c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCE2); 6378c2ecf20Sopenharmony_ci writel(0, msp->registers + MSP_RCE3); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return status; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int ux500_msp_i2s_of_init_msp(struct platform_device *pdev, 6458c2ecf20Sopenharmony_ci struct ux500_msp *msp, 6468c2ecf20Sopenharmony_ci struct msp_i2s_platform_data **platform_data) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct msp_i2s_platform_data *pdata; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci *platform_data = devm_kzalloc(&pdev->dev, 6518c2ecf20Sopenharmony_ci sizeof(struct msp_i2s_platform_data), 6528c2ecf20Sopenharmony_ci GFP_KERNEL); 6538c2ecf20Sopenharmony_ci pdata = *platform_data; 6548c2ecf20Sopenharmony_ci if (!pdata) 6558c2ecf20Sopenharmony_ci return -ENOMEM; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, 6588c2ecf20Sopenharmony_ci sizeof(struct stedma40_chan_cfg), 6598c2ecf20Sopenharmony_ci GFP_KERNEL); 6608c2ecf20Sopenharmony_ci if (!msp->playback_dma_data.dma_cfg) 6618c2ecf20Sopenharmony_ci return -ENOMEM; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev, 6648c2ecf20Sopenharmony_ci sizeof(struct stedma40_chan_cfg), 6658c2ecf20Sopenharmony_ci GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (!msp->capture_dma_data.dma_cfg) 6678c2ecf20Sopenharmony_ci return -ENOMEM; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ciint ux500_msp_i2s_init_msp(struct platform_device *pdev, 6738c2ecf20Sopenharmony_ci struct ux500_msp **msp_p, 6748c2ecf20Sopenharmony_ci struct msp_i2s_platform_data *platform_data) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct resource *res = NULL; 6778c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 6788c2ecf20Sopenharmony_ci struct ux500_msp *msp; 6798c2ecf20Sopenharmony_ci int ret; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci *msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL); 6828c2ecf20Sopenharmony_ci msp = *msp_p; 6838c2ecf20Sopenharmony_ci if (!msp) 6848c2ecf20Sopenharmony_ci return -ENOMEM; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (!platform_data) { 6878c2ecf20Sopenharmony_ci if (np) { 6888c2ecf20Sopenharmony_ci ret = ux500_msp_i2s_of_init_msp(pdev, msp, 6898c2ecf20Sopenharmony_ci &platform_data); 6908c2ecf20Sopenharmony_ci if (ret) 6918c2ecf20Sopenharmony_ci return ret; 6928c2ecf20Sopenharmony_ci } else 6938c2ecf20Sopenharmony_ci return -EINVAL; 6948c2ecf20Sopenharmony_ci } else { 6958c2ecf20Sopenharmony_ci msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx; 6968c2ecf20Sopenharmony_ci msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx; 6978c2ecf20Sopenharmony_ci msp->id = platform_data->id; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci msp->dev = &pdev->dev; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7038c2ecf20Sopenharmony_ci if (res == NULL) { 7048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n", 7058c2ecf20Sopenharmony_ci __func__); 7068c2ecf20Sopenharmony_ci return -ENOMEM; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR; 7108c2ecf20Sopenharmony_ci msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci msp->registers = devm_ioremap(&pdev->dev, res->start, 7138c2ecf20Sopenharmony_ci resource_size(res)); 7148c2ecf20Sopenharmony_ci if (msp->registers == NULL) { 7158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__); 7168c2ecf20Sopenharmony_ci return -ENOMEM; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci msp->msp_state = MSP_STATE_IDLE; 7208c2ecf20Sopenharmony_ci msp->loopback_enable = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_civoid ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, 7268c2ecf20Sopenharmony_ci struct ux500_msp *msp) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 732