13592b7f6SOla Lilja /* 23592b7f6SOla Lilja * Copyright (C) ST-Ericsson SA 2012 33592b7f6SOla Lilja * 43592b7f6SOla Lilja * Author: Ola Lilja <ola.o.lilja@stericsson.com>, 53592b7f6SOla Lilja * Roger Nilsson <roger.xr.nilsson@stericsson.com> 63592b7f6SOla Lilja * for ST-Ericsson. 73592b7f6SOla Lilja * 83592b7f6SOla Lilja * License terms: 93592b7f6SOla Lilja * 103592b7f6SOla Lilja * This program is free software; you can redistribute it and/or modify 113592b7f6SOla Lilja * it under the terms of the GNU General Public License version 2 as published 123592b7f6SOla Lilja * by the Free Software Foundation. 133592b7f6SOla Lilja */ 143592b7f6SOla Lilja 153592b7f6SOla Lilja #include <linux/module.h> 163592b7f6SOla Lilja #include <linux/slab.h> 173592b7f6SOla Lilja #include <linux/bitops.h> 183592b7f6SOla Lilja #include <linux/platform_device.h> 193592b7f6SOla Lilja #include <linux/clk.h> 203592b7f6SOla Lilja #include <linux/regulator/consumer.h> 213592b7f6SOla Lilja #include <linux/mfd/dbx500-prcmu.h> 22ab0fc6ceSArnd Bergmann #include <linux/platform_data/asoc-ux500-msp.h> 233592b7f6SOla Lilja 243592b7f6SOla Lilja #include <sound/soc.h> 253592b7f6SOla Lilja #include <sound/soc-dai.h> 263592b7f6SOla Lilja 273592b7f6SOla Lilja #include "ux500_msp_i2s.h" 283592b7f6SOla Lilja #include "ux500_msp_dai.h" 291428c20fSLee Jones #include "ux500_pcm.h" 303592b7f6SOla Lilja 313592b7f6SOla Lilja static int setup_pcm_multichan(struct snd_soc_dai *dai, 323592b7f6SOla Lilja struct ux500_msp_config *msp_config) 333592b7f6SOla Lilja { 343592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 353592b7f6SOla Lilja struct msp_multichannel_config *multi = 363592b7f6SOla Lilja &msp_config->multichannel_config; 373592b7f6SOla Lilja 383592b7f6SOla Lilja if (drvdata->slots > 1) { 393592b7f6SOla Lilja msp_config->multichannel_configured = 1; 403592b7f6SOla Lilja 413592b7f6SOla Lilja multi->tx_multichannel_enable = true; 423592b7f6SOla Lilja multi->rx_multichannel_enable = true; 433592b7f6SOla Lilja multi->rx_comparison_enable_mode = MSP_COMPARISON_DISABLED; 443592b7f6SOla Lilja 453592b7f6SOla Lilja multi->tx_channel_0_enable = drvdata->tx_mask; 463592b7f6SOla Lilja multi->tx_channel_1_enable = 0; 473592b7f6SOla Lilja multi->tx_channel_2_enable = 0; 483592b7f6SOla Lilja multi->tx_channel_3_enable = 0; 493592b7f6SOla Lilja 503592b7f6SOla Lilja multi->rx_channel_0_enable = drvdata->rx_mask; 513592b7f6SOla Lilja multi->rx_channel_1_enable = 0; 523592b7f6SOla Lilja multi->rx_channel_2_enable = 0; 533592b7f6SOla Lilja multi->rx_channel_3_enable = 0; 543592b7f6SOla Lilja 553592b7f6SOla Lilja dev_dbg(dai->dev, 563592b7f6SOla Lilja "%s: Multichannel enabled. Slots: %d, TX: %u, RX: %u\n", 573592b7f6SOla Lilja __func__, drvdata->slots, multi->tx_channel_0_enable, 583592b7f6SOla Lilja multi->rx_channel_0_enable); 593592b7f6SOla Lilja } 603592b7f6SOla Lilja 613592b7f6SOla Lilja return 0; 623592b7f6SOla Lilja } 633592b7f6SOla Lilja 643592b7f6SOla Lilja static int setup_frameper(struct snd_soc_dai *dai, unsigned int rate, 653592b7f6SOla Lilja struct msp_protdesc *prot_desc) 663592b7f6SOla Lilja { 673592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 683592b7f6SOla Lilja 693592b7f6SOla Lilja switch (drvdata->slots) { 703592b7f6SOla Lilja case 1: 713592b7f6SOla Lilja switch (rate) { 723592b7f6SOla Lilja case 8000: 733592b7f6SOla Lilja prot_desc->frame_period = 743592b7f6SOla Lilja FRAME_PER_SINGLE_SLOT_8_KHZ; 753592b7f6SOla Lilja break; 763592b7f6SOla Lilja 773592b7f6SOla Lilja case 16000: 783592b7f6SOla Lilja prot_desc->frame_period = 793592b7f6SOla Lilja FRAME_PER_SINGLE_SLOT_16_KHZ; 803592b7f6SOla Lilja break; 813592b7f6SOla Lilja 823592b7f6SOla Lilja case 44100: 833592b7f6SOla Lilja prot_desc->frame_period = 843592b7f6SOla Lilja FRAME_PER_SINGLE_SLOT_44_1_KHZ; 853592b7f6SOla Lilja break; 863592b7f6SOla Lilja 873592b7f6SOla Lilja case 48000: 883592b7f6SOla Lilja prot_desc->frame_period = 893592b7f6SOla Lilja FRAME_PER_SINGLE_SLOT_48_KHZ; 903592b7f6SOla Lilja break; 913592b7f6SOla Lilja 923592b7f6SOla Lilja default: 933592b7f6SOla Lilja dev_err(dai->dev, 943592b7f6SOla Lilja "%s: Error: Unsupported sample-rate (freq = %d)!\n", 953592b7f6SOla Lilja __func__, rate); 963592b7f6SOla Lilja return -EINVAL; 973592b7f6SOla Lilja } 983592b7f6SOla Lilja break; 993592b7f6SOla Lilja 1003592b7f6SOla Lilja case 2: 1013592b7f6SOla Lilja prot_desc->frame_period = FRAME_PER_2_SLOTS; 1023592b7f6SOla Lilja break; 1033592b7f6SOla Lilja 1043592b7f6SOla Lilja case 8: 1053592b7f6SOla Lilja prot_desc->frame_period = FRAME_PER_8_SLOTS; 1063592b7f6SOla Lilja break; 1073592b7f6SOla Lilja 1083592b7f6SOla Lilja case 16: 1093592b7f6SOla Lilja prot_desc->frame_period = FRAME_PER_16_SLOTS; 1103592b7f6SOla Lilja break; 1113592b7f6SOla Lilja default: 1123592b7f6SOla Lilja dev_err(dai->dev, 1133592b7f6SOla Lilja "%s: Error: Unsupported slot-count (slots = %d)!\n", 1143592b7f6SOla Lilja __func__, drvdata->slots); 1153592b7f6SOla Lilja return -EINVAL; 1163592b7f6SOla Lilja } 1173592b7f6SOla Lilja 1183592b7f6SOla Lilja prot_desc->clocks_per_frame = 1193592b7f6SOla Lilja prot_desc->frame_period+1; 1203592b7f6SOla Lilja 1213592b7f6SOla Lilja dev_dbg(dai->dev, "%s: Clocks per frame: %u\n", 1223592b7f6SOla Lilja __func__, 1233592b7f6SOla Lilja prot_desc->clocks_per_frame); 1243592b7f6SOla Lilja 1253592b7f6SOla Lilja return 0; 1263592b7f6SOla Lilja } 1273592b7f6SOla Lilja 1283592b7f6SOla Lilja static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate, 1293592b7f6SOla Lilja struct msp_protdesc *prot_desc) 1303592b7f6SOla Lilja { 1313592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 1323592b7f6SOla Lilja 1333592b7f6SOla Lilja u32 frame_length = MSP_FRAME_LEN_1; 1343592b7f6SOla Lilja prot_desc->frame_width = 0; 1353592b7f6SOla Lilja 1363592b7f6SOla Lilja switch (drvdata->slots) { 1373592b7f6SOla Lilja case 1: 1383592b7f6SOla Lilja frame_length = MSP_FRAME_LEN_1; 1393592b7f6SOla Lilja break; 1403592b7f6SOla Lilja 1413592b7f6SOla Lilja case 2: 1423592b7f6SOla Lilja frame_length = MSP_FRAME_LEN_2; 1433592b7f6SOla Lilja break; 1443592b7f6SOla Lilja 1453592b7f6SOla Lilja case 8: 1463592b7f6SOla Lilja frame_length = MSP_FRAME_LEN_8; 1473592b7f6SOla Lilja break; 1483592b7f6SOla Lilja 1493592b7f6SOla Lilja case 16: 1503592b7f6SOla Lilja frame_length = MSP_FRAME_LEN_16; 1513592b7f6SOla Lilja break; 1523592b7f6SOla Lilja default: 1533592b7f6SOla Lilja dev_err(dai->dev, 1543592b7f6SOla Lilja "%s: Error: Unsupported slot-count (slots = %d)!\n", 1553592b7f6SOla Lilja __func__, drvdata->slots); 1563592b7f6SOla Lilja return -EINVAL; 1573592b7f6SOla Lilja } 1583592b7f6SOla Lilja 1593592b7f6SOla Lilja prot_desc->tx_frame_len_1 = frame_length; 1603592b7f6SOla Lilja prot_desc->rx_frame_len_1 = frame_length; 1613592b7f6SOla Lilja prot_desc->tx_frame_len_2 = frame_length; 1623592b7f6SOla Lilja prot_desc->rx_frame_len_2 = frame_length; 1633592b7f6SOla Lilja 1643592b7f6SOla Lilja prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; 1653592b7f6SOla Lilja prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; 1663592b7f6SOla Lilja prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; 1673592b7f6SOla Lilja prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; 1683592b7f6SOla Lilja 1693592b7f6SOla Lilja return setup_frameper(dai, rate, prot_desc); 1703592b7f6SOla Lilja } 1713592b7f6SOla Lilja 1723592b7f6SOla Lilja static int setup_clocking(struct snd_soc_dai *dai, 1733592b7f6SOla Lilja unsigned int fmt, 1743592b7f6SOla Lilja struct ux500_msp_config *msp_config) 1753592b7f6SOla Lilja { 1763592b7f6SOla Lilja switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 1773592b7f6SOla Lilja case SND_SOC_DAIFMT_NB_NF: 1783592b7f6SOla Lilja break; 1793592b7f6SOla Lilja 1803592b7f6SOla Lilja case SND_SOC_DAIFMT_NB_IF: 1813592b7f6SOla Lilja msp_config->tx_fsync_pol ^= 1 << TFSPOL_SHIFT; 1823592b7f6SOla Lilja msp_config->rx_fsync_pol ^= 1 << RFSPOL_SHIFT; 1833592b7f6SOla Lilja 1843592b7f6SOla Lilja break; 1853592b7f6SOla Lilja 1863592b7f6SOla Lilja default: 1873592b7f6SOla Lilja dev_err(dai->dev, 1883592b7f6SOla Lilja "%s: Error: Unsopported inversion (fmt = 0x%x)!\n", 1893592b7f6SOla Lilja __func__, fmt); 1903592b7f6SOla Lilja 1913592b7f6SOla Lilja return -EINVAL; 1923592b7f6SOla Lilja } 1933592b7f6SOla Lilja 1943592b7f6SOla Lilja switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1953592b7f6SOla Lilja case SND_SOC_DAIFMT_CBM_CFM: 1963592b7f6SOla Lilja dev_dbg(dai->dev, "%s: Codec is master.\n", __func__); 1973592b7f6SOla Lilja 1983592b7f6SOla Lilja msp_config->iodelay = 0x20; 1993592b7f6SOla Lilja msp_config->rx_fsync_sel = 0; 2003592b7f6SOla Lilja msp_config->tx_fsync_sel = 1 << TFSSEL_SHIFT; 2013592b7f6SOla Lilja msp_config->tx_clk_sel = 0; 2023592b7f6SOla Lilja msp_config->rx_clk_sel = 0; 2033592b7f6SOla Lilja msp_config->srg_clk_sel = 0x2 << SCKSEL_SHIFT; 2043592b7f6SOla Lilja 2053592b7f6SOla Lilja break; 2063592b7f6SOla Lilja 2073592b7f6SOla Lilja case SND_SOC_DAIFMT_CBS_CFS: 2083592b7f6SOla Lilja dev_dbg(dai->dev, "%s: Codec is slave.\n", __func__); 2093592b7f6SOla Lilja 2103592b7f6SOla Lilja msp_config->tx_clk_sel = TX_CLK_SEL_SRG; 2113592b7f6SOla Lilja msp_config->tx_fsync_sel = TX_SYNC_SRG_PROG; 2123592b7f6SOla Lilja msp_config->rx_clk_sel = RX_CLK_SEL_SRG; 2133592b7f6SOla Lilja msp_config->rx_fsync_sel = RX_SYNC_SRG; 2143592b7f6SOla Lilja msp_config->srg_clk_sel = 1 << SCKSEL_SHIFT; 2153592b7f6SOla Lilja 2163592b7f6SOla Lilja break; 2173592b7f6SOla Lilja 2183592b7f6SOla Lilja default: 2193592b7f6SOla Lilja dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n", 2203592b7f6SOla Lilja __func__, fmt); 2213592b7f6SOla Lilja 2223592b7f6SOla Lilja return -EINVAL; 2233592b7f6SOla Lilja } 2243592b7f6SOla Lilja 2253592b7f6SOla Lilja return 0; 2263592b7f6SOla Lilja } 2273592b7f6SOla Lilja 2283592b7f6SOla Lilja static int setup_pcm_protdesc(struct snd_soc_dai *dai, 2293592b7f6SOla Lilja unsigned int fmt, 2303592b7f6SOla Lilja struct msp_protdesc *prot_desc) 2313592b7f6SOla Lilja { 2323592b7f6SOla Lilja prot_desc->rx_phase_mode = MSP_SINGLE_PHASE; 2333592b7f6SOla Lilja prot_desc->tx_phase_mode = MSP_SINGLE_PHASE; 2343592b7f6SOla Lilja prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; 2353592b7f6SOla Lilja prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE; 2363592b7f6SOla Lilja prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; 2373592b7f6SOla Lilja prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; 2383592b7f6SOla Lilja prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_HI); 2393592b7f6SOla Lilja prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_HI << RFSPOL_SHIFT; 2403592b7f6SOla Lilja 2413592b7f6SOla Lilja if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A) { 2423592b7f6SOla Lilja dev_dbg(dai->dev, "%s: DSP_A.\n", __func__); 2433592b7f6SOla Lilja prot_desc->rx_clk_pol = MSP_RISING_EDGE; 2443592b7f6SOla Lilja prot_desc->tx_clk_pol = MSP_FALLING_EDGE; 2453592b7f6SOla Lilja 2463592b7f6SOla Lilja prot_desc->rx_data_delay = MSP_DELAY_1; 2473592b7f6SOla Lilja prot_desc->tx_data_delay = MSP_DELAY_1; 2483592b7f6SOla Lilja } else { 2493592b7f6SOla Lilja dev_dbg(dai->dev, "%s: DSP_B.\n", __func__); 2503592b7f6SOla Lilja prot_desc->rx_clk_pol = MSP_FALLING_EDGE; 2513592b7f6SOla Lilja prot_desc->tx_clk_pol = MSP_RISING_EDGE; 2523592b7f6SOla Lilja 2533592b7f6SOla Lilja prot_desc->rx_data_delay = MSP_DELAY_0; 2543592b7f6SOla Lilja prot_desc->tx_data_delay = MSP_DELAY_0; 2553592b7f6SOla Lilja } 2563592b7f6SOla Lilja 2573592b7f6SOla Lilja prot_desc->rx_half_word_swap = MSP_SWAP_NONE; 2583592b7f6SOla Lilja prot_desc->tx_half_word_swap = MSP_SWAP_NONE; 2593592b7f6SOla Lilja prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; 2603592b7f6SOla Lilja prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; 2613592b7f6SOla Lilja prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; 2623592b7f6SOla Lilja 2633592b7f6SOla Lilja return 0; 2643592b7f6SOla Lilja } 2653592b7f6SOla Lilja 2663592b7f6SOla Lilja static int setup_i2s_protdesc(struct msp_protdesc *prot_desc) 2673592b7f6SOla Lilja { 2683592b7f6SOla Lilja prot_desc->rx_phase_mode = MSP_DUAL_PHASE; 2693592b7f6SOla Lilja prot_desc->tx_phase_mode = MSP_DUAL_PHASE; 2703592b7f6SOla Lilja prot_desc->rx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; 2713592b7f6SOla Lilja prot_desc->tx_phase2_start_mode = MSP_PHASE2_START_MODE_FSYNC; 2723592b7f6SOla Lilja prot_desc->rx_byte_order = MSP_BTF_MS_BIT_FIRST; 2733592b7f6SOla Lilja prot_desc->tx_byte_order = MSP_BTF_MS_BIT_FIRST; 2743592b7f6SOla Lilja prot_desc->tx_fsync_pol = MSP_FSYNC_POL(MSP_FSYNC_POL_ACT_LO); 2753592b7f6SOla Lilja prot_desc->rx_fsync_pol = MSP_FSYNC_POL_ACT_LO << RFSPOL_SHIFT; 2763592b7f6SOla Lilja 2773592b7f6SOla Lilja prot_desc->rx_frame_len_1 = MSP_FRAME_LEN_1; 2783592b7f6SOla Lilja prot_desc->rx_frame_len_2 = MSP_FRAME_LEN_1; 2793592b7f6SOla Lilja prot_desc->tx_frame_len_1 = MSP_FRAME_LEN_1; 2803592b7f6SOla Lilja prot_desc->tx_frame_len_2 = MSP_FRAME_LEN_1; 2813592b7f6SOla Lilja prot_desc->rx_elem_len_1 = MSP_ELEM_LEN_16; 2823592b7f6SOla Lilja prot_desc->rx_elem_len_2 = MSP_ELEM_LEN_16; 2833592b7f6SOla Lilja prot_desc->tx_elem_len_1 = MSP_ELEM_LEN_16; 2843592b7f6SOla Lilja prot_desc->tx_elem_len_2 = MSP_ELEM_LEN_16; 2853592b7f6SOla Lilja 2863592b7f6SOla Lilja prot_desc->rx_clk_pol = MSP_RISING_EDGE; 2873592b7f6SOla Lilja prot_desc->tx_clk_pol = MSP_FALLING_EDGE; 2883592b7f6SOla Lilja 2893592b7f6SOla Lilja prot_desc->rx_data_delay = MSP_DELAY_0; 2903592b7f6SOla Lilja prot_desc->tx_data_delay = MSP_DELAY_0; 2913592b7f6SOla Lilja 2923592b7f6SOla Lilja prot_desc->tx_half_word_swap = MSP_SWAP_NONE; 2933592b7f6SOla Lilja prot_desc->rx_half_word_swap = MSP_SWAP_NONE; 2943592b7f6SOla Lilja prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR; 2953592b7f6SOla Lilja prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR; 2963592b7f6SOla Lilja prot_desc->frame_sync_ignore = MSP_FSYNC_IGNORE; 2973592b7f6SOla Lilja 2983592b7f6SOla Lilja return 0; 2993592b7f6SOla Lilja } 3003592b7f6SOla Lilja 3013592b7f6SOla Lilja static int setup_msp_config(struct snd_pcm_substream *substream, 3023592b7f6SOla Lilja struct snd_soc_dai *dai, 3033592b7f6SOla Lilja struct ux500_msp_config *msp_config) 3043592b7f6SOla Lilja { 3053592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 3063592b7f6SOla Lilja struct msp_protdesc *prot_desc = &msp_config->protdesc; 3073592b7f6SOla Lilja struct snd_pcm_runtime *runtime = substream->runtime; 3083592b7f6SOla Lilja unsigned int fmt = drvdata->fmt; 3093592b7f6SOla Lilja int ret; 3103592b7f6SOla Lilja 3113592b7f6SOla Lilja memset(msp_config, 0, sizeof(*msp_config)); 3123592b7f6SOla Lilja 3133592b7f6SOla Lilja msp_config->f_inputclk = drvdata->master_clk; 3143592b7f6SOla Lilja 3153592b7f6SOla Lilja msp_config->tx_fifo_config = TX_FIFO_ENABLE; 3163592b7f6SOla Lilja msp_config->rx_fifo_config = RX_FIFO_ENABLE; 3173592b7f6SOla Lilja msp_config->def_elem_len = 1; 3183592b7f6SOla Lilja msp_config->direction = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 3193592b7f6SOla Lilja MSP_DIR_TX : MSP_DIR_RX; 3203592b7f6SOla Lilja msp_config->data_size = MSP_DATA_BITS_32; 3213592b7f6SOla Lilja msp_config->frame_freq = runtime->rate; 3223592b7f6SOla Lilja 3233592b7f6SOla Lilja dev_dbg(dai->dev, "%s: f_inputclk = %u, frame_freq = %u.\n", 3243592b7f6SOla Lilja __func__, msp_config->f_inputclk, msp_config->frame_freq); 3253592b7f6SOla Lilja /* To avoid division by zero */ 3263592b7f6SOla Lilja prot_desc->clocks_per_frame = 1; 3273592b7f6SOla Lilja 3283592b7f6SOla Lilja dev_dbg(dai->dev, "%s: rate: %u, channels: %d.\n", __func__, 3293592b7f6SOla Lilja runtime->rate, runtime->channels); 3303592b7f6SOla Lilja switch (fmt & 3313592b7f6SOla Lilja (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { 3323592b7f6SOla Lilja case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: 3333592b7f6SOla Lilja dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); 3343592b7f6SOla Lilja 3353592b7f6SOla Lilja msp_config->default_protdesc = 1; 3363592b7f6SOla Lilja msp_config->protocol = MSP_I2S_PROTOCOL; 3373592b7f6SOla Lilja break; 3383592b7f6SOla Lilja 3393592b7f6SOla Lilja case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: 3403592b7f6SOla Lilja dev_dbg(dai->dev, "%s: SND_SOC_DAIFMT_I2S.\n", __func__); 3413592b7f6SOla Lilja 3423592b7f6SOla Lilja msp_config->data_size = MSP_DATA_BITS_16; 3433592b7f6SOla Lilja msp_config->protocol = MSP_I2S_PROTOCOL; 3443592b7f6SOla Lilja 3453592b7f6SOla Lilja ret = setup_i2s_protdesc(prot_desc); 3463592b7f6SOla Lilja if (ret < 0) 3473592b7f6SOla Lilja return ret; 3483592b7f6SOla Lilja 3493592b7f6SOla Lilja break; 3503592b7f6SOla Lilja 3513592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: 3523592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: 3533592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: 3543592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: 3553592b7f6SOla Lilja dev_dbg(dai->dev, "%s: PCM format.\n", __func__); 3563592b7f6SOla Lilja 3573592b7f6SOla Lilja msp_config->data_size = MSP_DATA_BITS_16; 3583592b7f6SOla Lilja msp_config->protocol = MSP_PCM_PROTOCOL; 3593592b7f6SOla Lilja 3603592b7f6SOla Lilja ret = setup_pcm_protdesc(dai, fmt, prot_desc); 3613592b7f6SOla Lilja if (ret < 0) 3623592b7f6SOla Lilja return ret; 3633592b7f6SOla Lilja 3643592b7f6SOla Lilja ret = setup_pcm_multichan(dai, msp_config); 3653592b7f6SOla Lilja if (ret < 0) 3663592b7f6SOla Lilja return ret; 3673592b7f6SOla Lilja 3683592b7f6SOla Lilja ret = setup_pcm_framing(dai, runtime->rate, prot_desc); 3693592b7f6SOla Lilja if (ret < 0) 3703592b7f6SOla Lilja return ret; 3713592b7f6SOla Lilja 3723592b7f6SOla Lilja break; 3733592b7f6SOla Lilja 3743592b7f6SOla Lilja default: 3753592b7f6SOla Lilja dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n", 3763592b7f6SOla Lilja __func__, fmt); 3773592b7f6SOla Lilja return -EINVAL; 3783592b7f6SOla Lilja } 3793592b7f6SOla Lilja 3803592b7f6SOla Lilja return setup_clocking(dai, fmt, msp_config); 3813592b7f6SOla Lilja } 3823592b7f6SOla Lilja 3833592b7f6SOla Lilja static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, 3843592b7f6SOla Lilja struct snd_soc_dai *dai) 3853592b7f6SOla Lilja { 3863592b7f6SOla Lilja int ret = 0; 3873592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 3883592b7f6SOla Lilja 3893592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, 3903592b7f6SOla Lilja snd_pcm_stream_str(substream)); 3913592b7f6SOla Lilja 3923592b7f6SOla Lilja /* Enable regulator */ 3933592b7f6SOla Lilja ret = regulator_enable(drvdata->reg_vape); 3943592b7f6SOla Lilja if (ret != 0) { 3953592b7f6SOla Lilja dev_err(drvdata->msp->dev, 3963592b7f6SOla Lilja "%s: Failed to enable regulator!\n", __func__); 3973592b7f6SOla Lilja return ret; 3983592b7f6SOla Lilja } 3993592b7f6SOla Lilja 400f61ab093SUlf Hansson /* Prepare and enable clocks */ 401f61ab093SUlf Hansson dev_dbg(dai->dev, "%s: Enabling MSP-clocks.\n", __func__); 402f61ab093SUlf Hansson ret = clk_prepare_enable(drvdata->pclk); 403f61ab093SUlf Hansson if (ret) { 404f61ab093SUlf Hansson dev_err(drvdata->msp->dev, 405f61ab093SUlf Hansson "%s: Failed to prepare/enable pclk!\n", __func__); 406f61ab093SUlf Hansson goto err_pclk; 407f61ab093SUlf Hansson } 4083592b7f6SOla Lilja 409f61ab093SUlf Hansson ret = clk_prepare_enable(drvdata->clk); 410f61ab093SUlf Hansson if (ret) { 411f61ab093SUlf Hansson dev_err(drvdata->msp->dev, 412f61ab093SUlf Hansson "%s: Failed to prepare/enable clk!\n", __func__); 413f61ab093SUlf Hansson goto err_clk; 414f61ab093SUlf Hansson } 415f61ab093SUlf Hansson 416f61ab093SUlf Hansson return ret; 417f61ab093SUlf Hansson err_clk: 418f61ab093SUlf Hansson clk_disable_unprepare(drvdata->pclk); 419f61ab093SUlf Hansson err_pclk: 420f61ab093SUlf Hansson regulator_disable(drvdata->reg_vape); 421fe36a0b2SUlf Hansson return ret; 4223592b7f6SOla Lilja } 4233592b7f6SOla Lilja 4243592b7f6SOla Lilja static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, 4253592b7f6SOla Lilja struct snd_soc_dai *dai) 4263592b7f6SOla Lilja { 4273592b7f6SOla Lilja int ret; 4283592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 4293592b7f6SOla Lilja bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); 4303592b7f6SOla Lilja 4313592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, 4323592b7f6SOla Lilja snd_pcm_stream_str(substream)); 4333592b7f6SOla Lilja 4343592b7f6SOla Lilja if (drvdata->vape_opp_constraint == 1) { 4353592b7f6SOla Lilja prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4363592b7f6SOla Lilja "ux500_msp_i2s", 50); 4373592b7f6SOla Lilja drvdata->vape_opp_constraint = 0; 4383592b7f6SOla Lilja } 4393592b7f6SOla Lilja 4403592b7f6SOla Lilja if (ux500_msp_i2s_close(drvdata->msp, 4413592b7f6SOla Lilja is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { 4423592b7f6SOla Lilja dev_err(dai->dev, 4433592b7f6SOla Lilja "%s: Error: MSP %d (%s): Unable to close i2s.\n", 4443592b7f6SOla Lilja __func__, dai->id, snd_pcm_stream_str(substream)); 4453592b7f6SOla Lilja } 4463592b7f6SOla Lilja 447f61ab093SUlf Hansson /* Disable and unprepare clocks */ 448fe36a0b2SUlf Hansson clk_disable_unprepare(drvdata->clk); 449f61ab093SUlf Hansson clk_disable_unprepare(drvdata->pclk); 4503592b7f6SOla Lilja 4513592b7f6SOla Lilja /* Disable regulator */ 4523592b7f6SOla Lilja ret = regulator_disable(drvdata->reg_vape); 4533592b7f6SOla Lilja if (ret < 0) 4543592b7f6SOla Lilja dev_err(dai->dev, 4553592b7f6SOla Lilja "%s: ERROR: Failed to disable regulator (%d)!\n", 4563592b7f6SOla Lilja __func__, ret); 4573592b7f6SOla Lilja } 4583592b7f6SOla Lilja 4593592b7f6SOla Lilja static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, 4603592b7f6SOla Lilja struct snd_soc_dai *dai) 4613592b7f6SOla Lilja { 4623592b7f6SOla Lilja int ret = 0; 4633592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 4643592b7f6SOla Lilja struct snd_pcm_runtime *runtime = substream->runtime; 4653592b7f6SOla Lilja struct ux500_msp_config msp_config; 4663592b7f6SOla Lilja 4673592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, 4683592b7f6SOla Lilja dai->id, snd_pcm_stream_str(substream), runtime->rate); 4693592b7f6SOla Lilja 4703592b7f6SOla Lilja setup_msp_config(substream, dai, &msp_config); 4713592b7f6SOla Lilja 4723592b7f6SOla Lilja ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); 4733592b7f6SOla Lilja if (ret < 0) { 4743592b7f6SOla Lilja dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n", 4753592b7f6SOla Lilja __func__, ret); 4763592b7f6SOla Lilja return ret; 4773592b7f6SOla Lilja } 4783592b7f6SOla Lilja 4793592b7f6SOla Lilja /* Set OPP-level */ 4803592b7f6SOla Lilja if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) && 4813592b7f6SOla Lilja (drvdata->msp->f_bitclk > 19200000)) { 4823592b7f6SOla Lilja /* If the bit-clock is higher than 19.2MHz, Vape should be 4833592b7f6SOla Lilja * run in 100% OPP. Only when bit-clock is used (MSP master) */ 4843592b7f6SOla Lilja prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4853592b7f6SOla Lilja "ux500-msp-i2s", 100); 4863592b7f6SOla Lilja drvdata->vape_opp_constraint = 1; 4873592b7f6SOla Lilja } else { 4883592b7f6SOla Lilja prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, 4893592b7f6SOla Lilja "ux500-msp-i2s", 50); 4903592b7f6SOla Lilja drvdata->vape_opp_constraint = 0; 4913592b7f6SOla Lilja } 4923592b7f6SOla Lilja 4933592b7f6SOla Lilja return ret; 4943592b7f6SOla Lilja } 4953592b7f6SOla Lilja 4963592b7f6SOla Lilja static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, 4973592b7f6SOla Lilja struct snd_pcm_hw_params *params, 4983592b7f6SOla Lilja struct snd_soc_dai *dai) 4993592b7f6SOla Lilja { 5003592b7f6SOla Lilja unsigned int mask, slots_active; 5013592b7f6SOla Lilja struct snd_pcm_runtime *runtime = substream->runtime; 5023592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5033592b7f6SOla Lilja 5043592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", 5053592b7f6SOla Lilja __func__, dai->id, snd_pcm_stream_str(substream)); 5063592b7f6SOla Lilja 5073592b7f6SOla Lilja switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 5083592b7f6SOla Lilja case SND_SOC_DAIFMT_I2S: 5093592b7f6SOla Lilja snd_pcm_hw_constraint_minmax(runtime, 5103592b7f6SOla Lilja SNDRV_PCM_HW_PARAM_CHANNELS, 5113592b7f6SOla Lilja 1, 2); 5123592b7f6SOla Lilja break; 5133592b7f6SOla Lilja 5143592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_B: 5153592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_A: 5163592b7f6SOla Lilja mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 5173592b7f6SOla Lilja drvdata->tx_mask : 5183592b7f6SOla Lilja drvdata->rx_mask; 5193592b7f6SOla Lilja 5203592b7f6SOla Lilja slots_active = hweight32(mask); 5213592b7f6SOla Lilja dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); 5223592b7f6SOla Lilja 5233592b7f6SOla Lilja snd_pcm_hw_constraint_minmax(runtime, 5243592b7f6SOla Lilja SNDRV_PCM_HW_PARAM_CHANNELS, 5253592b7f6SOla Lilja slots_active, slots_active); 5263592b7f6SOla Lilja break; 5273592b7f6SOla Lilja 5283592b7f6SOla Lilja default: 5293592b7f6SOla Lilja dev_err(dai->dev, 5303592b7f6SOla Lilja "%s: Error: Unsupported protocol (fmt = 0x%x)!\n", 5313592b7f6SOla Lilja __func__, drvdata->fmt); 5323592b7f6SOla Lilja return -EINVAL; 5333592b7f6SOla Lilja } 5343592b7f6SOla Lilja 5353592b7f6SOla Lilja return 0; 5363592b7f6SOla Lilja } 5373592b7f6SOla Lilja 5383592b7f6SOla Lilja static int ux500_msp_dai_set_dai_fmt(struct snd_soc_dai *dai, 5393592b7f6SOla Lilja unsigned int fmt) 5403592b7f6SOla Lilja { 5413592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5423592b7f6SOla Lilja 5433592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d: Enter.\n", __func__, dai->id); 5443592b7f6SOla Lilja 5453592b7f6SOla Lilja switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | 5463592b7f6SOla Lilja SND_SOC_DAIFMT_MASTER_MASK)) { 5473592b7f6SOla Lilja case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: 5483592b7f6SOla Lilja case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: 5493592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS: 5503592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM: 5513592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: 5523592b7f6SOla Lilja case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: 5533592b7f6SOla Lilja break; 5543592b7f6SOla Lilja 5553592b7f6SOla Lilja default: 5563592b7f6SOla Lilja dev_err(dai->dev, 5573592b7f6SOla Lilja "%s: Error: Unsupported protocol/master (fmt = 0x%x)!\n", 5583592b7f6SOla Lilja __func__, drvdata->fmt); 5593592b7f6SOla Lilja return -EINVAL; 5603592b7f6SOla Lilja } 5613592b7f6SOla Lilja 5623592b7f6SOla Lilja switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 5633592b7f6SOla Lilja case SND_SOC_DAIFMT_NB_NF: 5643592b7f6SOla Lilja case SND_SOC_DAIFMT_NB_IF: 5653592b7f6SOla Lilja case SND_SOC_DAIFMT_IB_IF: 5663592b7f6SOla Lilja break; 5673592b7f6SOla Lilja 5683592b7f6SOla Lilja default: 5693592b7f6SOla Lilja dev_err(dai->dev, 5703592b7f6SOla Lilja "%s: Error: Unsupported inversion (fmt = 0x%x)!\n", 5713592b7f6SOla Lilja __func__, drvdata->fmt); 5723592b7f6SOla Lilja return -EINVAL; 5733592b7f6SOla Lilja } 5743592b7f6SOla Lilja 5753592b7f6SOla Lilja drvdata->fmt = fmt; 5763592b7f6SOla Lilja return 0; 5773592b7f6SOla Lilja } 5783592b7f6SOla Lilja 5793592b7f6SOla Lilja static int ux500_msp_dai_set_tdm_slot(struct snd_soc_dai *dai, 5803592b7f6SOla Lilja unsigned int tx_mask, 5813592b7f6SOla Lilja unsigned int rx_mask, 5823592b7f6SOla Lilja int slots, int slot_width) 5833592b7f6SOla Lilja { 5843592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 5853592b7f6SOla Lilja unsigned int cap; 5863592b7f6SOla Lilja 5873592b7f6SOla Lilja switch (slots) { 5883592b7f6SOla Lilja case 1: 5893592b7f6SOla Lilja cap = 0x01; 5903592b7f6SOla Lilja break; 5913592b7f6SOla Lilja case 2: 5923592b7f6SOla Lilja cap = 0x03; 5933592b7f6SOla Lilja break; 5943592b7f6SOla Lilja case 8: 5953592b7f6SOla Lilja cap = 0xFF; 5963592b7f6SOla Lilja break; 5973592b7f6SOla Lilja case 16: 5983592b7f6SOla Lilja cap = 0xFFFF; 5993592b7f6SOla Lilja break; 6003592b7f6SOla Lilja default: 6013592b7f6SOla Lilja dev_err(dai->dev, "%s: Error: Unsupported slot-count (%d)!\n", 6023592b7f6SOla Lilja __func__, slots); 6033592b7f6SOla Lilja return -EINVAL; 6043592b7f6SOla Lilja } 6053592b7f6SOla Lilja drvdata->slots = slots; 6063592b7f6SOla Lilja 6073592b7f6SOla Lilja if (!(slot_width == 16)) { 6083592b7f6SOla Lilja dev_err(dai->dev, "%s: Error: Unsupported slot-width (%d)!\n", 6093592b7f6SOla Lilja __func__, slot_width); 6103592b7f6SOla Lilja return -EINVAL; 6113592b7f6SOla Lilja } 6123592b7f6SOla Lilja drvdata->slot_width = slot_width; 6133592b7f6SOla Lilja 6143592b7f6SOla Lilja drvdata->tx_mask = tx_mask & cap; 6153592b7f6SOla Lilja drvdata->rx_mask = rx_mask & cap; 6163592b7f6SOla Lilja 6173592b7f6SOla Lilja return 0; 6183592b7f6SOla Lilja } 6193592b7f6SOla Lilja 6203592b7f6SOla Lilja static int ux500_msp_dai_set_dai_sysclk(struct snd_soc_dai *dai, 6213592b7f6SOla Lilja int clk_id, unsigned int freq, int dir) 6223592b7f6SOla Lilja { 6233592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6243592b7f6SOla Lilja 6253592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d: Enter. clk-id: %d, freq: %u.\n", 6263592b7f6SOla Lilja __func__, dai->id, clk_id, freq); 6273592b7f6SOla Lilja 6283592b7f6SOla Lilja switch (clk_id) { 6293592b7f6SOla Lilja case UX500_MSP_MASTER_CLOCK: 6303592b7f6SOla Lilja drvdata->master_clk = freq; 6313592b7f6SOla Lilja break; 6323592b7f6SOla Lilja 6333592b7f6SOla Lilja default: 6343592b7f6SOla Lilja dev_err(dai->dev, "%s: MSP %d: Invalid clk-id (%d)!\n", 6353592b7f6SOla Lilja __func__, dai->id, clk_id); 6363592b7f6SOla Lilja return -EINVAL; 6373592b7f6SOla Lilja } 6383592b7f6SOla Lilja 6393592b7f6SOla Lilja return 0; 6403592b7f6SOla Lilja } 6413592b7f6SOla Lilja 6423592b7f6SOla Lilja static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, 6433592b7f6SOla Lilja int cmd, struct snd_soc_dai *dai) 6443592b7f6SOla Lilja { 6453592b7f6SOla Lilja int ret = 0; 6463592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6473592b7f6SOla Lilja 6483592b7f6SOla Lilja dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", 6493592b7f6SOla Lilja __func__, dai->id, snd_pcm_stream_str(substream), 6503592b7f6SOla Lilja (int)drvdata->msp->id, cmd); 6513592b7f6SOla Lilja 6523592b7f6SOla Lilja ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream); 6533592b7f6SOla Lilja 6543592b7f6SOla Lilja return ret; 6553592b7f6SOla Lilja } 6563592b7f6SOla Lilja 6573592b7f6SOla Lilja static int ux500_msp_dai_probe(struct snd_soc_dai *dai) 6583592b7f6SOla Lilja { 6593592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); 6603592b7f6SOla Lilja 661f3fe53ddSFabio Baltieri dai->playback_dma_data = &drvdata->msp->playback_dma_data; 662f3fe53ddSFabio Baltieri dai->capture_dma_data = &drvdata->msp->capture_dma_data; 6633592b7f6SOla Lilja 664f3fe53ddSFabio Baltieri drvdata->msp->playback_dma_data.data_size = drvdata->slot_width; 665f3fe53ddSFabio Baltieri drvdata->msp->capture_dma_data.data_size = drvdata->slot_width; 6663592b7f6SOla Lilja 6673592b7f6SOla Lilja return 0; 6683592b7f6SOla Lilja } 6693592b7f6SOla Lilja 6703592b7f6SOla Lilja static struct snd_soc_dai_ops ux500_msp_dai_ops[] = { 6713592b7f6SOla Lilja { 6723592b7f6SOla Lilja .set_sysclk = ux500_msp_dai_set_dai_sysclk, 6733592b7f6SOla Lilja .set_fmt = ux500_msp_dai_set_dai_fmt, 6743592b7f6SOla Lilja .set_tdm_slot = ux500_msp_dai_set_tdm_slot, 6753592b7f6SOla Lilja .startup = ux500_msp_dai_startup, 6763592b7f6SOla Lilja .shutdown = ux500_msp_dai_shutdown, 6773592b7f6SOla Lilja .prepare = ux500_msp_dai_prepare, 6783592b7f6SOla Lilja .trigger = ux500_msp_dai_trigger, 6793592b7f6SOla Lilja .hw_params = ux500_msp_dai_hw_params, 6803592b7f6SOla Lilja } 6813592b7f6SOla Lilja }; 6823592b7f6SOla Lilja 6833592b7f6SOla Lilja static struct snd_soc_dai_driver ux500_msp_dai_drv[UX500_NBR_OF_DAI] = { 6843592b7f6SOla Lilja { 6853592b7f6SOla Lilja .name = "ux500-msp-i2s.0", 6863592b7f6SOla Lilja .probe = ux500_msp_dai_probe, 6873592b7f6SOla Lilja .id = 0, 6883592b7f6SOla Lilja .suspend = NULL, 6893592b7f6SOla Lilja .resume = NULL, 6903592b7f6SOla Lilja .playback = { 6913592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 6923592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 6933592b7f6SOla Lilja .rates = UX500_I2S_RATES, 6943592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 6953592b7f6SOla Lilja }, 6963592b7f6SOla Lilja .capture = { 6973592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 6983592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 6993592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7003592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7013592b7f6SOla Lilja }, 7023592b7f6SOla Lilja .ops = ux500_msp_dai_ops, 7033592b7f6SOla Lilja }, 7043592b7f6SOla Lilja { 7053592b7f6SOla Lilja .name = "ux500-msp-i2s.1", 7063592b7f6SOla Lilja .probe = ux500_msp_dai_probe, 7073592b7f6SOla Lilja .id = 1, 7083592b7f6SOla Lilja .suspend = NULL, 7093592b7f6SOla Lilja .resume = NULL, 7103592b7f6SOla Lilja .playback = { 7113592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7123592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7133592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7143592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7153592b7f6SOla Lilja }, 7163592b7f6SOla Lilja .capture = { 7173592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7183592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7193592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7203592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7213592b7f6SOla Lilja }, 7223592b7f6SOla Lilja .ops = ux500_msp_dai_ops, 7233592b7f6SOla Lilja }, 7243592b7f6SOla Lilja { 7253592b7f6SOla Lilja .name = "ux500-msp-i2s.2", 7263592b7f6SOla Lilja .id = 2, 7273592b7f6SOla Lilja .probe = ux500_msp_dai_probe, 7283592b7f6SOla Lilja .suspend = NULL, 7293592b7f6SOla Lilja .resume = NULL, 7303592b7f6SOla Lilja .playback = { 7313592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7323592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7333592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7343592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7353592b7f6SOla Lilja }, 7363592b7f6SOla Lilja .capture = { 7373592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7383592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7393592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7403592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7413592b7f6SOla Lilja }, 7423592b7f6SOla Lilja .ops = ux500_msp_dai_ops, 7433592b7f6SOla Lilja }, 7443592b7f6SOla Lilja { 7453592b7f6SOla Lilja .name = "ux500-msp-i2s.3", 7463592b7f6SOla Lilja .probe = ux500_msp_dai_probe, 7473592b7f6SOla Lilja .id = 3, 7483592b7f6SOla Lilja .suspend = NULL, 7493592b7f6SOla Lilja .resume = NULL, 7503592b7f6SOla Lilja .playback = { 7513592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7523592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7533592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7543592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7553592b7f6SOla Lilja }, 7563592b7f6SOla Lilja .capture = { 7573592b7f6SOla Lilja .channels_min = UX500_MSP_MIN_CHANNELS, 7583592b7f6SOla Lilja .channels_max = UX500_MSP_MAX_CHANNELS, 7593592b7f6SOla Lilja .rates = UX500_I2S_RATES, 7603592b7f6SOla Lilja .formats = UX500_I2S_FORMATS, 7613592b7f6SOla Lilja }, 7623592b7f6SOla Lilja .ops = ux500_msp_dai_ops, 7633592b7f6SOla Lilja }, 7643592b7f6SOla Lilja }; 7653592b7f6SOla Lilja 76642277bddSKuninori Morimoto static const struct snd_soc_component_driver ux500_msp_component = { 76742277bddSKuninori Morimoto .name = "ux500-msp", 76842277bddSKuninori Morimoto }; 76942277bddSKuninori Morimoto 77042277bddSKuninori Morimoto 771da794876SBill Pemberton static int ux500_msp_drv_probe(struct platform_device *pdev) 7723592b7f6SOla Lilja { 7733592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata; 774*a61f9e31SLee Jones struct msp_i2s_platform_data *pdata = pdev->dev.platform_data; 775*a61f9e31SLee Jones struct device_node *np = pdev->dev.of_node; 7763592b7f6SOla Lilja int ret = 0; 7773592b7f6SOla Lilja 778*a61f9e31SLee Jones if (!pdata && !np) { 779*a61f9e31SLee Jones dev_err(&pdev->dev, "No platform data or Device Tree found\n"); 780*a61f9e31SLee Jones return -ENODEV; 781*a61f9e31SLee Jones } 7823592b7f6SOla Lilja 7833592b7f6SOla Lilja drvdata = devm_kzalloc(&pdev->dev, 7843592b7f6SOla Lilja sizeof(struct ux500_msp_i2s_drvdata), 7853592b7f6SOla Lilja GFP_KERNEL); 7860dcd4742SLee Jones if (!drvdata) 7870dcd4742SLee Jones return -ENOMEM; 7880dcd4742SLee Jones 7893592b7f6SOla Lilja drvdata->fmt = 0; 7903592b7f6SOla Lilja drvdata->slots = 1; 7913592b7f6SOla Lilja drvdata->tx_mask = 0x01; 7923592b7f6SOla Lilja drvdata->rx_mask = 0x01; 7933592b7f6SOla Lilja drvdata->slot_width = 16; 7943592b7f6SOla Lilja drvdata->master_clk = MSP_INPUT_FREQ_APB; 7953592b7f6SOla Lilja 7963592b7f6SOla Lilja drvdata->reg_vape = devm_regulator_get(&pdev->dev, "v-ape"); 7973592b7f6SOla Lilja if (IS_ERR(drvdata->reg_vape)) { 7983592b7f6SOla Lilja ret = (int)PTR_ERR(drvdata->reg_vape); 7993592b7f6SOla Lilja dev_err(&pdev->dev, 8003592b7f6SOla Lilja "%s: ERROR: Failed to get Vape supply (%d)!\n", 8013592b7f6SOla Lilja __func__, ret); 8023592b7f6SOla Lilja return ret; 8033592b7f6SOla Lilja } 8043592b7f6SOla Lilja prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, (char *)pdev->name, 50); 8053592b7f6SOla Lilja 806f61ab093SUlf Hansson drvdata->pclk = clk_get(&pdev->dev, "apb_pclk"); 807f61ab093SUlf Hansson if (IS_ERR(drvdata->pclk)) { 808f61ab093SUlf Hansson ret = (int)PTR_ERR(drvdata->pclk); 809f61ab093SUlf Hansson dev_err(&pdev->dev, "%s: ERROR: clk_get of pclk failed (%d)!\n", 810f61ab093SUlf Hansson __func__, ret); 811f61ab093SUlf Hansson goto err_pclk; 812f61ab093SUlf Hansson } 813f61ab093SUlf Hansson 8143592b7f6SOla Lilja drvdata->clk = clk_get(&pdev->dev, NULL); 8153592b7f6SOla Lilja if (IS_ERR(drvdata->clk)) { 8163592b7f6SOla Lilja ret = (int)PTR_ERR(drvdata->clk); 8173592b7f6SOla Lilja dev_err(&pdev->dev, "%s: ERROR: clk_get failed (%d)!\n", 8183592b7f6SOla Lilja __func__, ret); 8193592b7f6SOla Lilja goto err_clk; 8203592b7f6SOla Lilja } 8213592b7f6SOla Lilja 8223592b7f6SOla Lilja ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp, 8233592b7f6SOla Lilja pdev->dev.platform_data); 8243592b7f6SOla Lilja if (!drvdata->msp) { 8253592b7f6SOla Lilja dev_err(&pdev->dev, 8263592b7f6SOla Lilja "%s: ERROR: Failed to init MSP-struct (%d)!", 8273592b7f6SOla Lilja __func__, ret); 8283592b7f6SOla Lilja goto err_init_msp; 8293592b7f6SOla Lilja } 8303592b7f6SOla Lilja dev_set_drvdata(&pdev->dev, drvdata); 8313592b7f6SOla Lilja 83242277bddSKuninori Morimoto ret = snd_soc_register_component(&pdev->dev, &ux500_msp_component, 83342277bddSKuninori Morimoto &ux500_msp_dai_drv[drvdata->msp->id], 1); 8343592b7f6SOla Lilja if (ret < 0) { 8353592b7f6SOla Lilja dev_err(&pdev->dev, "Error: %s: Failed to register MSP%d!\n", 8363592b7f6SOla Lilja __func__, drvdata->msp->id); 8373592b7f6SOla Lilja goto err_init_msp; 8383592b7f6SOla Lilja } 8393592b7f6SOla Lilja 8401428c20fSLee Jones ret = ux500_pcm_register_platform(pdev); 8411428c20fSLee Jones if (ret < 0) { 8421428c20fSLee Jones dev_err(&pdev->dev, 8431428c20fSLee Jones "Error: %s: Failed to register PCM platform device!\n", 8441428c20fSLee Jones __func__); 8451428c20fSLee Jones goto err_reg_plat; 8461428c20fSLee Jones } 8471428c20fSLee Jones 8483592b7f6SOla Lilja return 0; 8493592b7f6SOla Lilja 8501428c20fSLee Jones err_reg_plat: 85142277bddSKuninori Morimoto snd_soc_unregister_component(&pdev->dev); 8523592b7f6SOla Lilja err_init_msp: 8533592b7f6SOla Lilja clk_put(drvdata->clk); 8543592b7f6SOla Lilja err_clk: 855f61ab093SUlf Hansson clk_put(drvdata->pclk); 856f61ab093SUlf Hansson err_pclk: 8573592b7f6SOla Lilja devm_regulator_put(drvdata->reg_vape); 8583592b7f6SOla Lilja 8593592b7f6SOla Lilja return ret; 8603592b7f6SOla Lilja } 8613592b7f6SOla Lilja 862da794876SBill Pemberton static int ux500_msp_drv_remove(struct platform_device *pdev) 8633592b7f6SOla Lilja { 8643592b7f6SOla Lilja struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(&pdev->dev); 8653592b7f6SOla Lilja 8661428c20fSLee Jones ux500_pcm_unregister_platform(pdev); 8671428c20fSLee Jones 86842277bddSKuninori Morimoto snd_soc_unregister_component(&pdev->dev); 8693592b7f6SOla Lilja 8703592b7f6SOla Lilja devm_regulator_put(drvdata->reg_vape); 8713592b7f6SOla Lilja prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s"); 8723592b7f6SOla Lilja 8733592b7f6SOla Lilja clk_put(drvdata->clk); 874f61ab093SUlf Hansson clk_put(drvdata->pclk); 8753592b7f6SOla Lilja 8763592b7f6SOla Lilja ux500_msp_i2s_cleanup_msp(pdev, drvdata->msp); 8773592b7f6SOla Lilja 8783592b7f6SOla Lilja return 0; 8793592b7f6SOla Lilja } 8803592b7f6SOla Lilja 88149731c23SLee Jones static const struct of_device_id ux500_msp_i2s_match[] = { 88249731c23SLee Jones { .compatible = "stericsson,ux500-msp-i2s", }, 88349731c23SLee Jones {}, 88449731c23SLee Jones }; 88549731c23SLee Jones 8863592b7f6SOla Lilja static struct platform_driver msp_i2s_driver = { 8873592b7f6SOla Lilja .driver = { 8883592b7f6SOla Lilja .name = "ux500-msp-i2s", 8893592b7f6SOla Lilja .owner = THIS_MODULE, 89049731c23SLee Jones .of_match_table = ux500_msp_i2s_match, 8913592b7f6SOla Lilja }, 8923592b7f6SOla Lilja .probe = ux500_msp_drv_probe, 8933592b7f6SOla Lilja .remove = ux500_msp_drv_remove, 8943592b7f6SOla Lilja }; 8953592b7f6SOla Lilja module_platform_driver(msp_i2s_driver); 8963592b7f6SOla Lilja 8973592b7f6SOla Lilja MODULE_LICENSE("GPL v2"); 898